Dockerfile详解及优化技巧

写在前面

  • 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

区别:

  1. COPY 只能复制本地文件
  2. ADD 在复制压缩文件的同时会解压该文件,COPY不能解压
  3. 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命令有三种格式:

  1. Exec格式:CMD [“executable”,“param1”,“param2”],推荐使用这种格式
  2. Shell 格式:CMD command param1 param2
  3. 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优化技巧

  1. 使用国内的基础镜像,加速构建;
  2. 使用尽量小的、够用的基础镜像即可;
  3. 减少上下文关联目录文件
  4. 减少层的数量、控制层的大小
  5. 将不变层放到前面,可变层放到后面
  6. 使用多阶段来分离 build 和 runtime

推荐一个零声学院免费教程,个人觉得老师讲得不错,
分享给大家:[Linux,Nginx,ZeroMQ,MySQL,Redis,
fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,
TCP/IP,协程,DPDK等技术内容,点击立即学习:

猜你喜欢

转载自blog.csdn.net/weixin_46935110/article/details/129100391