zoukankan      html  css  js  c++  java
  • K8S系列第四篇(Dockerfile)

    DokcerFile 镜像定制

    更多精彩内容请关注微信公众号:新猿技术生态圈

    定制docker镜像的方式有两种:

    1. 手动修改容器内容,导出新的镜像。

    2. 基于dockerfile自行编写指令,基于指令流程创建镜像。

    Dockerfile简介

    镜像是多层存储,每一层都是在前一层的基础上进行修改;
    容器也是多层存储,以镜像为基础层,在其基础上加一层作为容器运行时的存储层。
    在这里插入图片描述
    刚才说了,创建镜像的两个方法:

    1. 手动修改容器内容,然后dokcer commit提交容器为新的镜像

    2. 通过在dockerfile中定义一系列的命令和参数构建成的脚本,然后这些命令应用于基础镜像,依次添加层,最终生成一个新的镜像。极大的简化了部署工作。

    Dockerfile主要组成部分

    基础镜像信息 FROM centos:7.9 
    制作镜像操作指令 RUN yum install -y nginx 
    容器启动时执行指令 CMD ["/bin/bash"]
    

    宿主机直接部署软件流程与Dockerfile部署软件流程对比

    需求 : 安装一个mysql,并启动。

    虚拟机部署形式:

      1. 开启vmware
      
      2. 运行某一个虚拟即,centos7
      
      3. centos7安装mysql yum install mysql-server
      
      4. 通过脚本或者命令,启动mysql即可
    
    部署缓慢,且修改了宿主机的环境,删除较为麻烦,占用宿主机的一个3306端口
    

    容器的部署形式:

      1.  开始vmware
      
      2. 运行虚拟机centos7(宿主机)
      
    3. 安装docker容器软件
      
    4. 获取mysql镜像即可,docker pull mysql:tag(你无法自由控制,该mysql的基础镜像时什么发行版本,你获取的镜像,是别人定制好的,你下载使用的默认时Debian发行版,你希望得到一个基于centos7.9的发行版本,运行mysql)
      
    5. 直接运行该镜像,通过端口映射,运行mysql
      
    6. 访问宿主机对的一个映射端口,访问到容器内的mysql
    

    想自定义镜像,就得自己写脚本,也就是dockerfile了

    Dokcerfile指令

    FROM  指定基础镜像
    
    MAINTAINER  指定维护者信息,可以没有
    
    RUN  你想让它干啥(在命令前面加上RUN即可)
    
    ADD  添加宿主机的文件到容器内,还多了一个自动解压的功能
    # RUN tar -Zxf /opt/xx.tgz 	# 报错!该tgz文件不存在! !
    
    COPY  作用和ADD是一样的,都是拷贝宿主机的文件到容器内, COPY就是仅仅拷贝
    
    WORKDIR  相当于cd命令,设置当前工作目录
    
    VOLUME  设置目录映射,挂载主机目录
    
    EXPOSE  指定对外的端口,在容器内暴露一个端口,端口 EXPORT 80
    
    CMD  指定容器启动后的要干的事情
    
    ENTRYPOINT	作用和CMD一样,都是在指定容器启动程序以及参数。
    # 当指定了ENTRYPOINT之后,CMD指令的语义就有了变化,而是把CMD的内容当作参数传递给ENTRYPOINT指令。
    
    ARG	 设置环境变量
    # ARG只是用于构建镜像需要设置的变量,容器运行时就消失了
    
    ENV   和ARG一样,都是设置环境变量
    # 区别在于ENV无论是在镜像构建时,还是容器运行,该变量都可以使用
    
    USER  用于改变环境,用于切换用户
    

    Dokcerfile实践

    需求:通过dockerfile,构建nginx镜像,且运行容器后,生成的页面是"辣辣小姐姐"。

    1. 创建Dockerfile,注意文件名,必须是这个
    [root@docker01 ~]# mkdir /learn_docker
    [root@docker01 ~]# cd /learn_docker/
    [root@docker01 learn_docker]# vim Dockerfile
    FROM nginx
    RUN echo "<meta charset=utf-8>辣辣小姐姐" > /usr/share/nginx/html/index.html
    
    2. 构建Dockerfile
    [root@docker01 learn_docker]# docker build .
    
    3. 修改镜像名字
    [root@docker01 learn_docker]# docker images
    REPOSITORY   TAG       IMAGE ID       CREATED          SIZE
    <none>       <none>    950549357c1f   18 seconds ago   133MB
    nginx        latest    08b152afcfae   6 days ago       133MB
    [root@docker01 learn_docker]# docker tag 950549357c1f my_nginx
    
    4. 运行该镜像
    docker run -d -p 80:80 my_nginx
    
    5. 查看宿主机的80端口
    http://192.168.15.80/
    # 辣辣小姐姐
    

    更多精彩内容请关注微信公众号:新猿技术生态圈

    Dokcerfile相关指令用法

    COPY

    copy指令从宿主机复制文件或者目录到新的一层镜像内
    如:
    copy nana.py /opt
    
    支持多个文件,以及通配符形式的复制,语法要满足Golang的filepath.Match
    copy na* /tmp/cc?.txt /opt
    
    COPY指令能够保留源文件的元数据,访问时间等等,这点很重要
    

    ADD

    特性和COPY基本一致,不过多了些功能
    1. 源文件是一个URL,此时dockcer引擎会下载该链接,放入目标路径,且权限自动设为600。若这不是期望结果,还得增加一层RUN指令进行调整
    # ADD nana.tgz /home
    # RUN xxx修改命令
    2. 源文件是一个URL,且是一个压缩包,不会自动解压,也得单独用RUN指令解压
    3. 源文件是一个压缩文件,且是gzip,bzip,xz,tar情况,ADD指令会自动解压压缩该文件到没有文件
    

    CMD

    用法,注意是双引号
    # CMD在容器内运行某个命令,启动程序
    # 该镜像在运行容器实例的时候,执行的具体参数是什么
    CMD["参数1","参数2"]
    在指定了entrypoint指令后,用CMD指定具体的参数
    
    dokcer不是虚拟机,容器就是一个进程,既然是进程,那么程序在启动的时候需要指定些运行参数,这就是CMD指令作用
    
    例如centos镜像默认的CMD是/bin/bash,直接docker run -it centos会直接进入bash解释器。
    也可以启动容器时候,指定参数: docker run -it centos cat /etc/os-release
    
    CMD ["/bin/bash"]
    
    # 该容器运行时,执行的命令
    # 等同于命令行的直接操作:docker run -it centos cat /etc/os-release
    CMD ["cat","/etc/os-release"]
    

    容器内运行程序

    这里要注意的是,docker不是虚拟机的概念,虚拟机的程序运行,基本上都是在后台运行,利用systemctl运行,但是容器内没有后台进程的概念,必须在前台运行
    容器就是为了主进程而存在的,主进程如果退出了,容器也就失去意义,自动退出。

    例如一个经典的问题:
    # 这样的写法是错误的,容器会立即退出
    CMD systemctl start nginx
    
    因为systemctl start nginx是以守护进程(默认在后台运行)的形式启动nginx,且CMD命令会转化为
    
    CMD ["sh","-c","systemctl start nginx" ]
    这样的命令主进程是sh解释器,执行完毕后立即结束了,因此容器也就退出了。
    
    # 相当于nginx -g daemon off
    因此正确的做法应该是 CMD ["nginx","-g","daemon off;"]
    
    把宿主机安装,启动nginx的理念放入到dockerfile中
    1. RUN yum install nginx
    2. RUN 配置文件修改 sed
    # RUN systemctl start nginx   容器内的程序必须在前台运行,容器时启动不了的
    3. 正确的写法应该时CMD ["nginx","-g","daemon off;"]
    

    ENTRYPOINT

    dokcer面试题:
    ENTRYPOINT和CMD的区别以及用法! ! !

    ENTRYPOINT作用和CMD一样,都是在指定容器启动程序以及参数。
    
    当指定了ENTRYPOINT之后,CMD指令的语义就有了变化,而是把CMD的内容当作参数传递给ENTRYPOINT指令。
    

    ENTRYPOINT和CMD的实际用法

    实际用法:
    1. 准备一个Dokcerfile
    [root@docker01 ~]# cd /learn_docker/
    [root@docker01 learn_docker]# > Dockerfile 
    [root@docker01 learn_docker]# vim Dockerfile 
    FROM centos:7.8.2003
    RUN rpm --rebuilddb && yum install epel-release -y
    RUN rpm --rebuilddb && yum install curl -y
    CMD ["curl","-s","ip.sb"]
    
    # 用法如下
    dokcer run my_centos curl -s ip.sb		# curl -s ip.sb获取本机的公网ip地址
    
    2. 构建镜像
    [root@docker01 learn_docker]# docker build .
    Sending build context to Docker daemon  2.048kB
    Step 1/4 : FROM centos:7.8.2003
     ---> afb6fca791e0
    Step 2/4 : RUN rpm --rebuilddb && yum install epel-release -y
     ---> Using cache
     ---> 81b4e83fb0a5
    Step 3/4 : RUN rpm --rebuilddb && yum install curl -y
     ---> Using cache
     ---> bd0074c78b6c
    Step 4/4 : CMD ["curl","-s","ip.sb"]
     ---> Running in 295418f71093
    Removing intermediate container 295418f71093
     ---> c920b743282a
    Successfully built c920b743282a
    
    3. 查看结果(出现Successfully代表镜像构建完成)
    Step 4/4 : CMD ["curl","-s","ip.sb"]
     ---> Running in 295418f71093
    Removing intermediate container 295418f71093
     ---> c920b743282a
    Successfully built c920b743282a
    
    4. 检查镜像
    [root@docker01 learn_docker]# docker tag c920b743282a centos_curl 
    [root@docker01 learn_docker]# docker images | grep curl
    centos_curl   latest     c920b743282a   3 minutes ago    471MB
    
    5. 运行镜像,生成容器记录,没有前台运行,因此立即挂了
    [root@docker01 learn_docker]# docker run centos_curl
    139.227.102.189
    
    6. 上述运行正确,但是我想再传入一个参数,该怎么办
    # 发现是无法直接传入参数的,该形式是覆盖镜像中的cmd
    # 就好比把docker镜像,当作一个环境,去执行后面的命令
    [root@docker01 learn_docker]# docker run centos_curl pwd
    /
    [root@docker01 learn_docker]# 
    [root@docker01 learn_docker]# docker run centos_curl -I
    docker: Error response from daemon: OCI runtime create failed: container_linux.go:380: starting container process caused: exec: "-I": executable file not found in $PATH: unknown.
    
    7. 想要正确的给容器传入一个参数该怎么办
    希望容器内能够正确完整的运作该命令的执行结果
    [root@docker01 learn_docker]# curl -s ip.sb -I			# 获取http报头信息
    HTTP/1.1 200 OK
    Date: Wed, 28 Jul 2021 15:16:18 GMT
    ...
    
    8. 解决办法
    方式一:给容器传入新的完整的命令,让后面的命令覆盖镜像中的cmd
    # 这是投机取巧的办法,不合适
    [root@docker01 learn_docker]# docker run centos_curl curl -s ip.sb -I
    HTTP/1.1 200 OK
    Date: Wed, 28 Jul 2021 15:18:05 GMT
    Content-Type: text/plain
    
    9. 正确的解决办法
    [root@docker01 learn_docker]# vim Dockerfile 
    FROM centos:7.8.2003
    RUN rpm --rebuilddb && yum install epel-release -y
    RUN rpm --rebuilddb && yum install curl -y
    ENTRYPOINT ["curl","-s","ip.sb"]
    
    10. 重新构建镜像
    # 重新构建镜像速度特别快,并且我们发现镜像的前三个Step的IMAGE ID是一致的,说明前三个的IMAGE ID是直接从缓存中拿的。
    # 只有Step 4/4的IMAGE ID发生了变化(Dockerfile文件的第四步是更改过的,是重新构建的镜像层),因此更加验证了我们之前所提到的镜像是分层构建的。
    [root@docker01 learn_docker]# docker build .
    Sending build context to Docker daemon  2.048kB
    Step 1/4 : FROM centos:7.8.2003
     ---> afb6fca791e0
    Step 2/4 : RUN rpm --rebuilddb && yum install epel-release -y
     ---> Using cache
     ---> 81b4e83fb0a5
    Step 3/4 : RUN rpm --rebuilddb && yum install curl -y
     ---> Using cache
     ---> bd0074c78b6c
    Step 4/4 : ENTRYPOINT ["curl","-s","ip.sb"]
     ---> Running in df106e04d533
    Removing intermediate container df106e04d533
     ---> e9479067148c
    Successfully built e9479067148c
    
    11. 重新运行该镜像,看结果,以及传入新的参数
    [root@docker01 learn_docker]# docker tag e9479067148c centos_curl_new
    
    # 此时发现,传入的CMD指令,当作了ENTRYPOINT的参数
    # 其实容器内,执行的完命令是: curl -s ip.sb -I
    [root@docker01 learn_docker]# docker run centos_curl_new -I
    HTTP/1.1 200 OK
    Date: Wed, 28 Jul 2021 15:24:58 GMT
    ...
    

    ARG和ENV指令

    设置环境变量

    dockerfile脚本,shell脚本
    
    ENV NAME="nana"
    ENV AGE=18
    ENV MYSQL_VERSION=5.6
    
    后续所有的操作,通过$NAMME就可以直接获取变量值使用了,维护dockerfile更加方便
    
    ARG和ENV一样,都是设置环境变量
    ENV无论是在镜像构建时,还是容器运行,该变量都可以使用
    ARG只是用于构建镜像需要设置的变量,容器运行时就消失了
    

    VOLUME

    容器在运行时,应该保证在存储层不写入任何数据,运行在容器内产生的数据,我们推荐是挂载,写入到宿主机上,进行维护。

    # mount /mnt 
    
    VOLUME /data  
    # 将容器内的/data文件夹,在容器运行时,该目录自动挂载为匿名卷,任何向该目录中写入数据的操作,都不会被容器记录,保证的容器存储无状态理念。
    
    # Dockerfile
    [root@docker01 ~]# cd /learn_docker/
    [root@docker01 learn_docker]# > Dockerfile 
    [root@docker01 learn_docker]# vim Dockerfile 
    FROM centos
    MAINTAINER nana
    VOLUME ["/data1","/data2"]
    
    # 该容器运行的时候,这两个目录自动和宿主机的目录做好映射关系
    docker build .
    
    # 运行该镜像
    docker run 86b4dceba89a
    # 查看生成的容器信息
    [root@docker01 nana]# docker ps -a | head -2
    CONTAINER ID   IMAGE             COMMAND                  CREATED         STATUS                     PORTS     NAMES
    84014622b3a4   86b4dceba89a      "/bin/bash"              2 minutes ago   Exited (0) 2 minutes ago             sharp_noether
    
    # dokcer inspect命令查看
    [root@docker01 learn_docker]# docker inspect 86b4dceba89a
                "Volumes": {
                    "/data1": {},
                    "/data2": {}
                },
    
    1. 容器数据挂载的方式,通过dockerfile,指定VOLUME目录
    2. 通过docker run -v参数,直接设置需要映射挂载的目录
    

    EXPOSE

    指定容器运行时对外提供的端口服务。

    帮助使用该镜像的人,快速理解该容器的一个端口业务
    docker port 容器
    dokcer run -p 宿主机端口:容器端口
    docker run -P # 作用是随机 宿主机端口:容器内端口
    

    WORKDIR

    用于在dockerfile中,目录的切换,更改工作目录

    WORKDIR /opt
    

    USER

    用于改变环境,用于切换用户

    USER root
    USER nana
    

    使用Dockerfile构建一个网站镜像

    传统方式创建一个网站站点

    1. nginx,修改首页内容,html网站就跑起来了。web server,提供web服务,提供代理转发,提供网关,限流等等。。。
    2. web framework。web框架,一般由开发,通过某个开发语言,基于某个web框架,自己去开发一个web站点,python,django框架。

    使用Dockerfile创建一个网站站点

    1. 用python语言,基于flask web框架,开发一个自己的网站,写一个后端的网站代码
    2. 开发dockerfile,部署该代码,生成镜像
    3. 其他人基于该镜像,docker run就可以在电脑跑起来你这个网站

    使用docker的优势

    • 比如安装一个etcd、naco,都是一些比较复杂的软件。

    • 需要依赖于go语言环境,比如需要依赖于java环境,在自己的机器安装好对应的开发环境,以及对应的版本,以及各种依赖。。。

      tomcat  	依赖于jdk环境
      		
      当你有了docker,
      docker pull tomcat  		# 这些主流的镜像都可以直接找到,并且该镜像中,就已经打包好了java环境
      docker run tomcat xxx ... 		# 直接可以访问tomcat了
      
    1. 在宿主机下,准备一个目录,准备好dockerfile,代码文件
    # 写一个flask的python代码
    # 创建代码文件
    [root@docker01 ~]# cd /learn_docker/
    [root@docker01 ~]# vim nana_flask.py 
    #coding:utf8
    from flask import Flask
    app=Flask(__name__)
    # @app.route(装饰器),网站的route,指的是url地址后面的文件路径
    @app.route("/nana")
    def nana():
        return "From Docker,nana是只臭猪猪!!!"
    if __name__=="__main__":
        app.run(host="0.0.0.0",port=8080)
    
    2. 编写Dockerfile
    [root@docker01 learn_docker]# vim Dockerfile
    FROM centos:7.8.2003
    RUN curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo;
    RUN curl -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo;
    RUN yum makecache fast;
    RUN yum install python3-devel python3-pip -y
    RUN pip3 install -i https://pypi.douban.com/simple flask
    COPY nana_flask.py /opt
    WORKDIR /opt
    EXPOSE 8080
    CMD ["python3","nana_flask.py"]
    
    3. 构建镜像
    # --no-cache不是使用之前旧的缓存,重新构建镜像
    [root@docker01 learn_docker]# docker build --no-cache -t "nana_flask" .
    ...
    Successfully built 9e731f439e41
    Successfully tagged nana_flask:latest
    
    4. 查看构建好的镜像
    [root@docker01 learn_docker]# docker images | head -2
    REPOSITORY        TAG        IMAGE ID       CREATED          SIZE
    nana_flask        latest     9e731f439e41   3 minutes ago    649MB
    
    5. 运行镜像,生成容器
    [root@docker01 learn_docker]# docker run -d --name nana_flask_web01 -p 90:8080 nana_flask
    d9f2f83d16bdd0364473d6e4043c433cbd8e3286e87ecbf93fb3fd5e08ac8002
    
    [root@docker01 learn_docker]# docker ps
    CONTAINER ID   IMAGE        COMMAND                  CREATED         STATUS         PORTS                                   NAMES
    d9f2f83d16bd   nana_flask   "python3 nana_flask.…"   2 minutes ago   Up 2 minutes   0.0.0.0:90->8080/tcp, :::90->8080/tcp   nana_flask_web01
    
    6. 访问宿主主机,查看容器内的flask web网站
    浏览器输入: http://192.168.15.80:90/nana
    # From Docker,nana是只臭猪猪!!!
    

    如何修改容器内的网站的内容

    方法一:修改宿主机的代码,以及dockerfile,重新构建镜像

    [root@docker01 ~]# vim nana_flask.py 
    ...
    def nana():
        return "From Docker,nana是只臭猪猪!!!"				# 修改return值,重新生成镜像
    ...
    

    方法二:你可以经入到已经运行的容器内,修改代码,重启容器即可

    1. 进入容器内部
    [root@docker01 learn_docker]# docker exec -it d9f2f83d16bd bash
    [root@d9f2f83d16bd opt]# ls                
    nana_flask.py
    
    2. 修改容器内的代码
    [root@d9f2f83d16bd opt]# vi nana_flask.py 
    #coding:utf8
    from flask import Flask
    app=Flask(__name__)
    # @app.route(装饰器),网站的route,指的是url地址后面的文件路径
    @app.route("/nana")
    def nana():
        return "From Docker,nana是只臭猪猪!!!ABC!!!"
    if __name__=="__main__":
        app.run(host="0.0.0.0",port=8080)
    
    3. 退出容器并重启容器
    [root@d9f2f83d16bd opt]# exit
    exit
    [root@docker01 learn_docker]# docker restart d9f2f83d16bd
    d9f2f83d16bd
    [root@docker01 learn_docker]# docker ps
    CONTAINER ID   IMAGE        COMMAND                  CREATED         STATUS              PORTS                                   NAMES
    d9f2f83d16bd   nana_flask   "python3 nana_flask.…"   4 minutes ago   Up About a minute   0.0.0.0:90->8080/tcp, :::90->8080/tcp   nana_flask_web01
    
    4. 访问宿主主机,查看容器内的flask web网站
    浏览器输入: http://192.168.15.80:90/nana
    # From Docker,nana是只臭猪猪!!!ABC!!!
    

    更多精彩内容请关注微信公众号:新猿技术生态圈

    Docker基础复习

    Docker容器文件系统

    容器是docker的一个核心概念,容器使用一个或者一组应用,他的运行状态如下:

    • docker利用容器运行应用程序

    • 容器是镜像的运行实例,可以被run、start、stop、rm

    • 每个容器都是互相隔离,保证平台暗转

    • 容器可以看作是一个简易版Linux环境(没有Linux内核,有root权限、进程、用户空间、网络)

    • 镜像是只读的,容器在启动的时候创建一层可写层
      在这里插入图片描述
      dokcerfile面向开发,docker image(镜像)作为交付标准,docker container(容器)涉及部署和运维,三者合起来完成docker体系。

      FROM ubuntu:14.04				选择基础镜像
      ADD run.sh					 	添加文件镜像,这一层镜像只有一个内容,就是这个文件
      VOLUME /data					设定存储目录,并未添加文件,只是更新了镜像的json文件,便于启动时候读取该层信息
      CMD ["./run.sh"] 			    更新json文件,设定程序入口
      

    docker容器管理总结

    # 运行镜像,且进入容器内
    [root@docker01 ~]# docker run -it ubuntu bash
    root@7478064e9fff:/# 
    
    # 容器运行web程序
    # 注意端口使用,数字大一点,建议8000以后开始使用
    
    
    # --restart=always容器在后台挂掉后,默认重启容器
    [root@docker01 ~]# docker run --name my_nginx -d --restart=always -p 8000:80 nginx
    79d7fcfdc60f2c40e6d92790be6ad6f3bf9db49fda0e46cadb196be6677b4f73
    
    [root@docker01 ~]# docker ps | head -2
    CONTAINER ID   IMAGE        COMMAND                  CREATED          STATUS          PORTS                                   NAMES
    79d7fcfdc60f   nginx        "/docker-entrypoint.…"   40 seconds ago   Up 39 seconds   0.0.0.0:8000->80/tcp, :::8000->80/tcp   my_nginx
    
    浏览器访问:http://192.168.15.80:8000/ ==> 可以访问到nginx
    
    # 查看容器内日志,实时刷新
    docker logs -f
    
    # 查看运行时,以及挂掉的容器记录
    docker ps 	在运行的容器
    dokcer ps -a 挂掉以及活着的容器
    
    # 停止启动
    docker start
    docker stop
    
    # 进入容器内
    docker exec -it 容器id bash
    
    # 删除容器
    docker rm 容器id
    docker rm `docker ps -qa`
    # 强制杀死并删除容器
    docker rm -f 容器id
    
    # 查看容器进程资源信息
    docker top 容器id
    
    # 查看容器内资源
    docker stats 容器id
    
    # 查看容器具体信息
    docker inspect 容器id
    
    # 获取容器内的ip地址,容器的格式化参数
    docker inspect --format '{{ .NetworkSettings.IPAddress }}' 容器id
    

    dokcer run启动容器的时候,dokcer后台操作流程是

    • 检查本地是否有该镜像,没有就下载
    • 利用镜像创建且启动一个容器
    • 分配容器文件系统,在只读的镜像层挂载读写层
    • 宿主机的网桥接口会分配一个虚拟接口到容器中
    • 容器获得地址池的ip地址
    • 执行用户指定的程序
    • 若程序里没有进程在运行,容器执行完毕后立即终止

    更多精彩内容请关注微信公众号:新猿技术生态圈
    更多精彩内容请关注微信公众号:新猿技术生态圈
    更多精彩内容请关注微信公众号:新猿技术生态圈

  • 相关阅读:
    Try .NET & Github Gist
    vue & font-awesome
    JSP基础与提高(一).md
    chm转换为html文件
    markdownpad生成目录
    MarkdownPad2的密钥
    删除多余的win10软件
    计算机组成原理与机构期末复习的概念
    sublime text 的小细节设置,让你的代码更优美
    NetBeans主题配色方案加设置.md
  • 原文地址:https://www.cnblogs.com/chenyangqit/p/15077084.html
Copyright © 2011-2022 走看看