Dockerfile实战

一.Dockerfile语法梳理及最佳实践

1,关键字

  • FROM
    • 用在Dockerfile的开头,用来设定docker image的base image,想在哪个base image之上build我们自己的image
    • 尽量使用官方提供的image作为base image,因为安全
    • 示例代码
      FROM scratch #从头制作base image
      FROM centos  #使用centos作为base image
      FROM ubuntu:14.04 #选择其他的image作为base image
      
  • LABEL
    • 定义image的meta data,用来书写帮助信息
    • 示例代码
      LABEL maintainer="[email protected]"#定义作者
      LABEL version="1.0"#定义版本
      LABEL description="This is description"#定义描述
      
  • RUN
    • RUN用于运行命令
    • 每次运行RUN都会增加一个分层。为了美观,复杂的RUN请用反斜线换行!避免无用分层,合并多条命令成一行(用&&)。
    • 示例代码
      RUN yum update&&yum install -y vim \
          python-dev #反斜线表示换行
      RUN apt-get update&&apt-get install -y perl \
          pwgen --no-install-recommends&&rm -rf \
          /var/lib/apt/lists/* #注意清理cache
      RUN /bin/bash -c 'source $HOME/.bashrc;echo $HOME'
      
  • WORKDIR
    • WORKDIR用于设定当前工作目录,类似于cd到指定目录
    • 示例代码
    WORKDIR /root #进入到root文件夹下
    
    WORKDIR /test #如果没有自动创建test目录
    WORKDIR demo
    RUN pwd #结果输出应该是/test/demo
    
    • 用WORKDIR,不要用RUN cd!用WORKDIR应尽量使用绝对目录!
  • ADD and COPY
    • COPY可以把本地的文件添加到Docker image里面;ADD可以把本地文件添加到目的地址并解压缩
    • 示例代码
      ADD hello / #将当前目录中的hello添加到image的根路径下
      
      ADD test.tar.gz / #添加到根目录并解压
      
      WORKDIR /root
      ADD hello test/ #hello的路径是/root/test/hello
      
      WORKDIR /root
      COPY hello test/
      
    • 大部分的情况下,COPY优于ADD!ADD除了COPY还有额外功能(解压)!添加远程文件/目录请使用curl或者wget!
  • ENV
    • ENV设定环境变量(常量)
    • 示例代码
      ENV MYSQL_VERSION 5.6 #设置常量
      RUN apt-get install -y mysql-server="${MYSQL_VERSION}" && rm -rf /var/lib/apt/lists/* #引用常量
      
    • 尽量使用ENV,可以增加代码的可维护性
  • VOLUME and EXPOSE
    • 用于存储和网络

二.RUN vs CMD vs ENTRYPOINT

  • RUN:执行命令并创建新的Image Layer
  • CMD:设置容器启动后默认执行的命令和参数
  • ENTRYPOINT:设置容器启动时运行的命令

1.Shell和Exec格式

  • Shell格式[把要运行的命令当成shell命令去执行]
    RUN apt-get install -y vim
    CMD echo "hello docker"
    ENTRYPOINT echo "hello docker"
    
  • Exec格式[通过特定的格式指明要运行的命令,以及命令的参数]
    RUN ["apt-get","install","-y","vim"]
    CMD ["/bin/echo","hello docker"]
    ENTRYPOINT ["/bin/echo","hello docker"]
    
  • Dockerfile1示例代码
    FROM centos
    ENV name Docker
    ENTRYPOINT echo "hello $name"
    
    • 结果是hello Docker
  • Dockerfile2示例代码
    FROM centos
    ENV name Docker
    ENTRYPOINT ["/bin/echo","hello $name"]
    
    • 结果是hello $name
    • 执行的是单纯的echo命令,而不是执行shell的echo命令,所以拿不到环境变量的值
    • 如果想执行shell中的echo则应修改dockerfile如下
    FROM centos
    ENV name Docker
    ENTRYPOINT ["/bin/bash","-c","echo hello $name"]
    
    • 结果是hello Docker

2.CMD

  • 容器启动时默认执行的命令
  • 如果docker run指定了其他命令,CMD命令被忽略
  • 如果定义了多个CMD,只有最后一个会执行
  • 示例代码
    FROM centos
    ENV name Docker
    CMD echo "hello $name"
    
    • 当执行docker run [image]时输出:hello Docker
    • 当执行docker run -it [image] /bin/bash时输出:被忽略掉

3.ENTRYPOINT

  • 让容器以应用程序或者服务的形式运行[通常用于启动后台的进程,比如启动mysql服务器]
  • 不会被忽略,一定会执行
  • 最佳实践:写一个shell脚本作为entrypoint
    COPY docker-entrypoint.sh /usr/local/bin/
    ENTRYPOINT ["docker-entrypoint.sh"]
    EXPOSE 27017
    CMD ["mongod"]
    

三.镜像的发布

  • Docker hub[类似于GitHub]
    • 可以使用docker pull image名称从registry上拉取image
  • 向Docker hub中提交代码[前缀名需要时docker hub的用户名]
    • 执行docker login进行登陆
    • 执行docker image push进行提交[简写:docker push]
    • 提交实例
      docker push wangzhejack/hello:latest
      
    • 提交成功后可以到docker hub的repositories中查看,其中latest是设置tag
    • 测试发布是否成功
      docker rmi id号 #删除本地image
      docker image ls #查看本地image是否删除hello镜像
      docker pull wangzhejack/hello #从远程仓库拉取hello镜像
      docker image ls #查看本地image是否存在hello镜像
      
  • 可以在Docker hub中创建repository时关联github的代码仓库存储dockerfile,每次pull时,dockerhub会自动进行build(每次维护dockerfile文件即可)
  • 如何公司搭建自己的Docker registry,供公司内部使用的?
    • 执行docker run -d -p 5000:5000 --restart always --name registry registry:2启动私有的registry【启动在5000端口上】
    • 每次提交前的build变成docker build -t ip地址:端口号/image名 .
    • 此时计算机可能会觉得指定的ip的dockerhub是不可信任不安全的,需要执行以下操作
      • 在/etc/docker文件夹下创建daemon.json文件,并添加如下内容,表示信任以下registry
      {"insecure-registries":["ip地址:端口号"]}
      
      • 执行sudo vim /lib/systemd/system/docker.service,添加以下内容:EnvironmentFile=~/etc/docker/daemon.json表示当运行docker服务的时候会读取daemon.json文件,从而加载对应registry
      • 执行sudo service docker restart重启docker服务
    • 执行docker push ip地址:端口号/image名向自己的仓库中push
    • 执行docker pull ip地址:端口号/image名从自己的仓库中pull

4.Dockerfile实战:将python程序打包成docker image,再将image运行成container

  • 将如下python的web项目打包成docker image,之后运行成container
    from flask import Flask
    app = Flask(__name__)
    @app.route('/')
    def hello():
      return "hello docker"
    if __name__ == "__main__":
      app.run()
    
  • 创建Dockerfile文件
    FROM python:2.7 #使用python2.7版本的image作为base image
    LABEL maintainer="Jack"
    RUN pip install flask #在container上安装flask
    COPY app.py /app/
    WORKDIR /app
    EXPOSE 5000
    CMD ["python","app.py"]
    
    • 如果程序直接运行则会运行在image的127.0.0.1的5000端口上,只能image内可访问。所以需要使用EXPOSE将运行的container的指定端口暴漏出来。
    • 因为每次执行的时候都会创建image,所以如果其中某一步出现问题,可以取上一步的id值进行交互式run测试查看是否进行了对应的操作docker run -it ID值 /bin/bash
    • 执行docker run wangzhejack/flask-hello-world -d运行image创建container(-d表示后台执行,用docker ps可以查看)

四.容器的操作

1.对运行中的容器执行命令docker exec 命令

  • 执行docker exec -it containerID号 命令内容其中-it表示执行交互式命令
  • docker exec -it be393ca08145 /bin/bash进入指定container中的交互式命令行中
  • docker exec -it be393ca08145 python进入container并执行python进入python交互式命令行中

2.停掉运行中的容器

  • docker container stop containerID其中containerID也可以是name
  • 简写docker stop contianerID其中containerID也可以是name

3.清理停掉的容器

  • docker rm $(docker ps -aq)

4.指定docker运行的container的名字

  • docker run -d --name=demo wangzhejack/flask-hello-world指定运行container的名字为demo,如果不指定则会自动分配一个name

5.启动container容器

  • docker start containerID/name启动容器可以使用id或是name

6.显示container详细信息

  • docker inspect containerID/name查看container详细信息时可以使用id或name

7.查看container运行产生的输出日志

  • docker logs containerID/name查看container的日志记录可以使用id或name

2.通过Dockerfile运行一个linux的命令行工具实战

1.在ubuntu的image的container上安装stress

docker run -it ubuntu #启动ubuntu的image,创建container

apt-get update&&apt-get install -y stress #安装stress

2.stress工具的应用

  • stress --vm1启动一个worker(创建要给进程),默认分配的内存是256M

3.将stress打包成一个docker image

FROM ubuntu
RUN apt-get update&&apt-get install -y stress
ENTRYPOINT ["/usr/bin/stress"]
CMD []
* 使用ENTRYPOINT+空CMD的方式可以使输入的时候携带指定的参数
* 例如,执行`docker run -it wangzhejack/stress-test --vm 1`则会执行`stress --vm 1`的命令

五.容器的资源限制(防止使用过大内存的container导致内存溢出)

  • 执行docker run时可以通过参数进行资源的限制,如指定cpu的个数/内存的大小等

1.设置container的内存限制--memory=指定大小

  • docker run --memory=200M wangzhejack/stress-test --vm 1 --verbose其中–verbose相当于debug模式启动vm压力,vm 1的默认分配内存为256M,因为memory给了200M,默认情况下的memory wrap与memory相等,则相当于container有400M,其中有进程占用256M,则可以正常运行
  • docker run --memory=200M wangzhejack/stress-test --vm 1 --verbose --vm-bytes 500M则相当于container有400M,其中有进程占用500M,运行container时会提示run失败

2.设置container的cpu使用情况--cpu-shares 指定大小

  • 通过--cpu-shares int设定cpu shares的权重值,是多个contianer之间的相对权重比较,当同时启用时,根据cpu shares权重比划分cpu
  • 实例测试
    #启用三个container占用cpu
    docker run --cpu-shares=5 --name=demo1 wangzhejack/stress-test --cpu 1
    docker run --cpu-shares=10 --name=demo2 wangzhejack/stress-test --cpu 1
    
    • 其中--cpu 1表示运行的container通过stress工具占用cpu
    • 再启动一个container,通过top查看内存则可以发现,当只运行一个container时cpu占用近乎100%,当同时运行了第二个container时,由于设置的cpu占用权重比时5:10,则可以看到第一个stress占用33%左右,第二个占用66%左右

3.Linux底层对docker的技术支持

  • Namespaces:做隔离pid,net,ipc,mnt,uts
  • Control groups:做资源限制
  • Union file system:Container和image的分层

你可能感兴趣的:(Docker)