写在前面
- Dockerfile的默认相对路径是Dockerfile所在的目录;
- Dockerfile中的每一行会被视为一层镜像
一、Dockerfile 原理
1.1 镜像定义
首先我们先来回顾一下 Docker 镜像,它由多个只读层堆叠到一起,每一层是上一层的增量修改。基于镜像创建新容器时,将在基础层的顶部添加一个新的可写层。该层通常称为“容器层”。下图展示了一个基于 docker.io/centos 基础镜像构建的应用镜像,创建出容器时的视图。
1.2 镜像来源
镜像主要是 Docker 通过读取、运行 Dockerfile 的指令来生成。我们将上篇文章中的Dockerfile拿过来
FROM gcc
MAINTAINER panda
COPY ./hello.c ./
RUN pwd
LABEL myhello 1.0.0
LABEL env prod
RUN gcc hello.c -o hello
CMD ["./hello"]
Dockerfile的核心逻辑就是先定义引用的基础镜像(FROM base image),然后再COPY或ADD上下文到容器,然后再RUN执行用户自定义脚本构建,最后定义容器的CMD或ENTRYPOINT。
1.3 Dockerfile 关键字
FROM
设置镜像使用的基础镜像,必须是第一条指令。如果不以任何镜像为基础镜像,则可以不写,但是,接下来的第一条指令会被作为镜像的第一层使用。
用法:FROM image:tag 其中 tag 是可选项,如果不写,默认为最新latest。
FROM nginx
MAINTAINER
设置镜像的作者。
用法:MAINTAINER name
MAINTAINER panda
RUN
编译镜像时,要执行的命令,经常被用来安装软件包、拉取和编译代码。
RUN apt-get install python3
RUN git clone https://github.com/golang/example
RUN gcc hello.c -o hello
RUN pwd
LABEL
设置镜像标签,一个Dockfile中可以有多个LABEL。
LABEL helloc 1.0.0
LABEL env prod
EXPOSE
设置镜像运行时对外暴露的端口
ENV
设置容器的环境变量
ENV env1=v1
ENV env2=v2
COPY 和 ADD
都是复制上下文中的文件到镜像中
COPY ./hello.c ./
ADD ./nginx.tar.gz ./
ADD http://www.panda.com/nginx.tar.gz
区别:
- COPY 只能复制本地文件
- ADD 在复制压缩文件的同时会解压该文件,COPY不能解压
- ADD + 网址,会从执行网址下载文件(注意,如果下载的是压缩包,此时不会解压)
补充:Exec和 Shell 格式
在学习CMD 和 ENTRYPOINT 之前,我们先来了解下Exec和 Shell 这两种格式。
Exec格式,当指令执行时,会直接调用 ,不会被 shell 解析。
# <instruction> ["executable", "param1", "param2", ...]
RUN ["apt-get", "install", "gcc"]
CMD ["echo", "Hello,world!"]
ENTRYPOINT ["echo", "Hello,world!"]
Shell 格式,当指令执行时,shell 格式底层会调用 /bin/sh -c
RUN apt-get install gcc
CMD echo "Hello,world!"
ENTRYPOINT echo "Hello,world!"
CMD
设置容器的启动执行的命令和参数,但是会被 docker run 后面的命令行参数替换掉,忽略掉CMD。
CMD ["./hello"] # 运行hello程序
CMD ["echo", "hello,world"] # 等价于在命令行执行 echo hello,world
案例1(Exec格式):
#Dockerfile部分
ENV name panda
CMD ["echo", "hello, $name"] # 输出 hello, $name 注意name没被解析为panda
如果希望name被解析,可以做以下修改
CMD ["/bin/sh", "-c", "echo hello, $name"] # 输出 hello, panda
案例2(Shell 格式):
#Dockerfile部分
ENV name panda
CMD echo "hello, $name" # 输出 hello, panda
小结:
CMD命令有三种格式:
- Exec格式:CMD [“executable”,“param1”,“param2”],推荐使用这种格式
- Shell 格式:CMD command param1 param2
- CMD [“param1”,“param2”] 为 ENTRYPOINT 提供额外的参数,此时 ENTRYPOINT 必须使用 Exec 格式。
ENRYPOINT
设置容器的入口程序。
ENTRYPOINT 用法与 CMD 很像,它们都可以指定要执行的命令及其参数。不同的地方在于 ENTRYPOINT 不会被忽略,一定会被执行,即使运行 docker run 时指定了其他命令。
VOLUME
设置容器的挂载卷,可以将宿主机目录挂载到容器中,用来持久化容器中的重要数据。
VOLUME ["/var/log/"]
VOLUME /var/log
USER
设置 RUN CMD ENTRYPOINT 的用户名,使用前需要保证USER 有相应的权限。
WORKER
设置RUN CMD ENTRYPOINT COPY ADD 指令的工作目录,如果该目录不存在会自动创建。
ARG
设置编译镜像时加入的参数。ARG命令定义了一个变量,在docker build镜像的时候,需要加 --build-arg arg=val 来指定参数. 可以同时定义多个变量。
#Dockerfile 部分
ARG work_dir code_addr
RUN git clone $code_addr
docker build -t helloc --build-arg work_dir=hello --build-arg code_addr=https://github.com/golang/example .
ONBUILD
ONBUILD影响的是下游的镜像构建,本次不会执行。比如,镜像S0的Dockerfile如下:
#Dockerfile 部分
ONBUILD RUN ls -a
则在构建镜像S0的时候,不会执行。另一个镜像S1是基于S0的,那么在构建镜像S1的时候会执行RUN ls -a
STOPSIGNAL
设置容器退出时候的信号量,默认退出信号量是15. nginx 修改了退出信号量为3.
二、Dockerfile优化技巧
- 使用国内的基础镜像,加速构建;
- 使用尽量小的、够用的基础镜像即可;
- 减少上下文关联目录文件
- 减少层的数量、控制层的大小
- 将不变层放到前面,可变层放到后面
- 使用多阶段来分离 build 和 runtime