Docker 一篇文章带你理解Dockerfile_docker

 

关于Dockerfile


在Docker中创建镜像最常用的方式,就是使用Dockerfile。Dockerfile是一个Docker镜像的描述文件,我们可以理解成火箭发射的A、B、C、D…的步骤。Dockerfile其内部包含了一条条的指令每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建

Docker 一篇文章带你理解Dockerfile_html_02

一个Dockerfile的示例如下所示: 

#基于centos镜像
FROM centos

#维护人的信息
MAINTAINER The CentOS Project

#安装httpd软件包
RUN yum -y update
RUN yum -y install httpd

#开启80端口
EXPOSE 80

#复制网站首页文件至镜像中web站点下
COPY index.html /var/www/html/index.html

#复制该脚本至镜像中,并修改其权限
COPY run.sh /run.sh
RUN chmod 775 /run.sh

#当启动容器时执行的脚本文件
CMD ["/run.sh"]

由上可知,Dockerfile结构大致分为四个部分:

  (1)基础镜像信息

  (2)维护者信息

  (3)镜像操作指令

  (4)容器启动时执行指令。

  Dockerfile每行支持一条指令,每条指令可带多个参数,支持使用以#号开头的注释。下面会对上面使用到的一些常用指令做一些介绍。

 

DockerFile构建过程解析


基础知识

  • 每条保留字指令都必须为大写字母后面要跟随至少一个参数
  • 指令从上到下顺序执行
  • #表示注释
  • 每条指令都会创建一个新的镜像层,并对镜像进行提交

大致流程

  1. docker从基础镜像运行一个容器
  2. 执行一条指令并对容器进行修改
  3. 执行类似于docker commit的操作提交一个新的镜像
  4. docker再基于别提交的新的镜像运行一个新的容器
  5. 执行dockerfile的下一个指令再从执行第2点直到没有指令

 

 

构建镜像对的命令


docker build -t nginx:v1 .

docker build -t 构建镜像   nginx:v1 构建镜像的名称和标签      . 代表当前目录(不完全正确,这里指的上下文)

举两个例子:

错误用法   copy ../index.html  /app (超越了上下文路径)
正确用法 copy ./test.html /app

      首先我们要理解 docker build 的工作原理。Docker 在运行时分为 Docker 引擎(也就是服务端守护进程)和客户端工具。Docker 的引擎提供了一组 REST API,被称为 Docker Remote API,而如 docker 命令这样的客户端工具,则是通过这组 API 与 Docker 引擎交互,从而完成各种功能。因此,虽然表面上我们好像是在本机执行各种 docker 功能,但实际上,一切都是使用的远程调用形式在服务端(Docker 引擎)完成。也因为这种 C/S 设计,让我们操作远程服务器的 Docker 引擎变得轻而易举。

    当我们进行镜像构建的时候,并非所有定制都会通过 RUN 指令完成,经常会需要将一些本地文件复制进镜像,比如通过 COPY 指令、ADD 指令等。而 docker build 命令构建镜像,其实并非在本地构建,而是在服务端,也就是 Docker 引擎中构建的。那么在这种客户端/服务端的架构中,如何才能让服务端获得本地文件呢?

     这就引入了上下文的概念。当构建的时候,用户会指定构建镜像上下文的路径,docker build 命令得知这个路径后,会将路径下的所有内容打包,然后上传给 Docker 引擎。这样 Docker 引擎收到这个上下文包后,展开就会获得构建镜像所需的一切文件。

 

Dockerfile常用指令


FROM:指明构建的新镜像是来自于哪个基础镜像,例如:

FROM centos:6

 

MAINTAINER:指明镜像维护着及其联系方式(一般是邮箱地址),例如:

MAINTAINER Edison Zhou <edisonchou@hotmail.com>

 

RUN:构建镜像时运行的Shell命令,例如:

RUN ["yum", "install", "httpd"]
RUN yum install httpd

   又如,我们在使用微软官方ASP.NET Core Runtime镜像时往往会加上以下RUN命令,弥补无法在默认镜像下使用Drawing相关接口的缺憾:

FROM microsoft/dotnet:2.2.1-aspnetcore-runtime
RUN apt-get update
RUN apt-get install -y libgdiplus
RUN apt-get install -y libc6-dev
RUN ln -s /usr/lib/libgdiplus.so /lib/x86_64-linux-gnu/libgdiplus.so

 

CMD:启动容器时执行的Shell命令,例如:

CMD ["-C", "/start.sh"]
CMD ["/usr/sbin/sshd", "-D"]
CMD /usr/sbin/sshd -D

CMD类似于RUN的命令,用于运行程序,但二者的运行时间不同:

  • CMD 在docker run 时运行。
  • RUN 是在 docker build。

 

 EXPOSE:声明容器运行的服务端口,例如:

EXPOSE 80 443

EXPOSE指令是声明运行时容器提供服务端口,这只是一个声明,在运行时并不会因为这个声明应用就会开启这个端口的服务.

在Dockerfile中写入这样的声明有两个好处:

  1. 帮助镜像使用者理解这个镜像服务的端口,以方便配置映射;
  2. 另个用处是在运行时使用随机端口映射时,也就是 docker run -P,会自动随机映射

启动tomcat:docker run -p 8080:8080 tomcat

    -P:左边宿主机端口,右边容器端口

    -p:宿主机段端口随机分配一个端口,让外部进行访问

 

 ENV:设置环境内环境变量,例如:

ENV MYSQL_ROOT_PASSWORD 123456
ENV JAVA_HOME /usr/local/jdk1.8.0_45

 

ADD:拷贝文件或目录到镜像中,例如:

ADD <src>...<dest>
ADD html.tar.gz /var/www/html
ADD https://xxx.com/html.tar.gz /var/www/html

ADD 命令和COPY命令差不多,比COPY高级

  • ADD 的优点:在执行 <源文件> 为 tar 压缩文件的话,压缩格式为 gzip, bzip2 以及 xz 的情况下,会自动复制并解压到 <目标路径>。
  • ADD 的缺点:在不解压的前提下,无法复制 tar 压缩文件。会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。具体是否使用,可以根据是否需要自动解压来决定。
  • PS:如果是URL或压缩包,会自动下载或自动解压。

 

COPY:拷贝文件或目录到镜像中,用法同ADD,只是不支持自动下载和解压,例如:

COPY ./start.sh /start.sh

COPY 复制文件:

    COPY <源路径>... <目标路径>

    COPY ["<源路径1>",... "<目标路径>"]    目标路径也可以成为镜像的目录

    COPY 指令将从构建上下文目录中 <源路径> 的文件/目录复制到新的一层的镜像内的 <目标路径> 位置。

    例如:COPY package.json /usr/src/app/

 

ENTRYPOINT:

  启动容器时执行的Shell命令,同CMD类似,只是由ENTRYPOINT启动的程序不会被docker run命令行指定的参数所覆盖,而且,这些命令行参数会被当作参数传递给ENTRYPOINT指定指定的程序,例如:

ENTRYPOINT ["/bin/bash", "-C", "/start.sh"]
ENTRYPOINT /bin/bash -C '/start.sh'

PS:Dockerfile文件中也可以存在多个ENTRYPOINT指令,但仅有最后一个会生效。

 

VOLUME:指定容器挂载点到宿主机自动生成的目录或其他容器,例如:

VOLUME ["/var/lib/mysql"]

PS:一般不会在Dockerfile中用到,更常见的还是在docker run的时候指定-v数据卷。

 作用:Docker容器的数据持久化.

定义:数据卷是一个可以提供一个或多个容器使用的特殊目录,它绕过UFS

 特性:

  • 数据卷可以在容器之间共享和重用
  • 对数据卷的修改会立即生效
  • 对数据卷的更新,不会影响镜像
  • 数据卷会默认一直存在,即使容器被删除
  • 命令:-v数据卷启动
docker run -p 8080:8080 --name tomcat -d -v usr/local/docker/tomcat/ROOT:usr/local/tomcat/webapps/ROOT tomcat

 

USER:为RUN、CMD和ENTRYPOINT执行Shell命令指定运行用户,例如:

USER <user>[:<usergroup>]
USER <UID>[:<UID>]
USER edisonzhou

 

WORKDIR:为RUN、CMD、ENTRYPOINT以及COPY和AND设置工作目录,例如:

WORKDIR /data

 

HEALTHCHECK:告诉Docker如何测试容器以检查它是否仍在工作,即健康检查,例如:

HEALTHCHECK --interval=5m --timeout=3s --retries=3 \
CMD curl -f http:/localhost/ || exit 1

其中,一些选项的说明:

  • --interval=DURATION (default: 30s):每隔多长时间探测一次,默认30秒
  • -- timeout= DURATION (default: 30s):服务响应超时时长,默认30秒
  • --start-period= DURATION (default: 0s):服务启动多久后开始探测,默认0秒
  • --retries=N (default: 3):认为检测失败几次为宕机,默认3次

  一些返回值的说明:

  • 0:容器成功是健康的,随时可以使用
  • 1:不健康的容器无法正常工作
  • 2:保留不使用此退出代码

 

ARG

在构建镜像时,指定一些参数,例如:

FROM centos:6
ARG user # ARG user=root
USER $user

这时,我们在docker build时可以带上自定义参数user了,如下所示:

docker build --build-arg user=edisonzhou Dockerfile .

 

综合Dockerfile案例


下面是一个Java Web应用的镜像Dockerfile,综合使用到了上述介绍中最常用的几个命令:

FROM centos:7
MAINTANIER www.edisonchou.com

ADD jdk-8u45-linux-x64.tar.gz /usr/local
ENV JAVA_HOME /usr/local/jdk1.8.0_45

ADD apache-tomcat-8.0.46.tar.gz /usr/local
COPY server.xml /usr/local/apache-tomcat-8.0.46/conf

RUN rm -f /usr/local/*.tar.gz

WORKDIR /usr/local/apache-tomcat-8.0.46
EXPOSE 8080
ENTRYPOINT ["./bin/catalina.sh", "run"]

有了Dockerfile,就可以创建镜像了:

docker build -t edc-tomcat:v1 .

最后,可以通过以下命令创建容器:

docker run -itd --name=tomcate -p 8080:8080 \
-v /app/webapps/:/usr/local/apache-tomcat-8.0.46/webapps/ \
edc-tomcat:v1