Docker-Dockerfile
参考文献
Dockerfile
指令
1 | # 注释 |
RUN,CMD,ENTRYPOINT
命令区别
RUN
命令执行命令并创建新的镜像层,通常用于安装软件包CMD
命令设置容器启动后默认执行的命令及其参数,但CMD
设置的命令能够被docker run
命令后面的命令行参数替换ENTRYPOINT
配置容器启动时的执行命令(不会被忽略,一定会被执行,即使运行docker run
时指定了其他命令)
Dockerfile
书写原则
-
单一职责: 不同功能的应用应该尽量拆分为不同的容器,每个容器只负责单一业务进程
-
提供注释信息: 晦涩难懂的代码尽量添加注释
-
保持容器最小化: 应该避免安装无用的软件包
-
合理选择基础镜像: 容器的核心是应用,只要基础镜像能够满足应用的运行环境即可
-
使用
.dockerignore
文件忽略一些不需要做版本管理的文件 -
尽量使用构建缓存
- 从当前构建层开始,比较所有的子镜像,检查所有的构建指令是否与当前完全一致,如果不一致,则不使用缓存;
- 一般情况下,只需要比较构建指令即可判断是否需要使用缓存,但是有些指令除外(例如
ADD
和COPY
); - 对于ADD和COPY指令不仅要校验命令是否一致,还要为即将拷贝到容器的文件计算校验和(根据文件内容计算出的一个数值,如果两个文件计算的数值一致,表示两个文件内容一致 ),命令和校验和完全一致,才认为命中缓存.
- 基于 Docker 构建时的缓存特性,我们可以把不轻易改变的指令放到
Dockerfile
前面(例如安装软件包),而可能经常发生改变的指令放在Dockerfile
末尾(例如编译应用程序)
-
正确设置时区
-
从
Docker Hub
拉取的官方操作系统镜像大多数都是UTC
时间(世界标准时间).如果想要在容器中使用中国区标准时间(东八区),请根据使用的操作系统修改相应的时区信息,常用操作系统的修改方式:1
2
3
4
5
6Ubuntu 和Debian 系统可以向 Dockerfile 中添加以下指令:
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN echo "Asia/Shanghai" >> /etc/timezone
CentOS 系统则向 Dockerfile 中添加以下指令:
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
-
-
使用国内软件源加快镜像构建速度
-
最小化镜像层数
1
2
3
4
5
6要在 CentOS 系统中安装make和net-tools两个软件包,应该在 Dockerfile 中使用以下指令:
RUN yum install -y make net-tools
而不应该写成这样:
RUN yum install -y make
RUN yum install -y net-tools
Dockerfile
指令书写建议
RUN
-
RUN指令在构建时将会生成一个新的镜像层并且执行RUN指令后面的内容.使用RUN指令时应该尽量遵循以下原则:
-
当RUN指令后面跟的内容比较复杂时,建议使用反斜杠() 结尾并且换行;
-
RUN指令后面的内容尽量按照字母顺序排序,提高可读性.
-
例如,在官方的
CentOS
镜像下安装一些软件,一个建议的Dockerfile
指令如下:
1
2
3
4
5FROM centos:7
RUN yum install -y automake \
curl \
python \
vim -
CMD和ENTRYPOINT
-
CMD
和ENTRYPOINT
指令都是容器运行的命令入口,这两个指令使用中有很多相似的地方,但是也有一些区别.这两个指令的相同之处,CMD
和ENTRYPOINT
的基本使用格式分为两种.- 第一种为
CMD/ENTRYPOINT["command","param"]
.这种格式是使用 Linux 的exec实现的, 一般称为exec
模式,这种书写格式为CMD/ENTRYPOINT
后面跟 json 数组,也是Docker 推荐的使用格式. - 另外一种格式为
CMD/ENTRYPOINT command param
,这种格式是基于 shell 实现的, 通常称为shell
模式.当使用shell
模式时,Docker 会以/bin/sh -c command
的方式执行命令.
- 第一种为
-
使用
exec
模式启动容器时,容器的 1 号进程就是CMD/ENTRYPOINT
中指定的命令,而使用shell
模式启动容器时相当于我们把启动命令放在了shell
进程中执行,等效于执行/bin/sh -c "task command"
命令.因此shell
模式启动的进程在容器中实际上并不是 1 号进程. -
这两个指令的区别:
Dockerfile
中如果使用了ENTRYPOINT
指令,启动 Docker 容器时需要使用--entrypoint
参数才能覆盖Dockerfile
中的ENTRYPOINT
指令 ,而使用CMD
设置的命令则可以被docker run
后面的参数直接覆盖.ENTRYPOINT
指令可以结合CMD
指令使用,也可以单独使用,而CMD
指令只能单独使用.- 如果你希望你的镜像足够灵活,推荐使用
CMD
指令.如果你的镜像只执行单一的具体程序,并且不希望用户在执行docker run时覆盖默认程序,建议使用ENTRYPOINT
. - 最后再强调一下,无论使用
CMD
还是ENTRYPOINT
,都尽量使用exec
模式.- exec 可以保证我们的业务进程就是 1 号进程,这对于需要处理
SIGTERM
信号量实现优雅终止十分重要
- exec 可以保证我们的业务进程就是 1 号进程,这对于需要处理
ADD和COPY
-
ADD
和COPY
指令功能类似,都是从外部往容器内添加文件.但是COPY
指令只支持基本的文件和文件夹拷贝功能,ADD
则支持更多文件来源类型,比如自动提取 tar 包,并且可以支持源文件为 URL 格式. -
推荐你使用
COPY
指令,因为COPY
指令更加透明,仅支持本地文件向容器拷贝,而且使用COPY
指令可以更好地利用构建缓存,有效减小镜像体积. -
当你想要使用ADD向容器中添加 URL 文件时,请尽量考虑使用其他方式替代.例如你想要在容器中安装 memtester(一种内存压测工具),你应该避免使用以下格式:
1
2
3ADD http://pyropus.ca/software/memtester/old-versions/memtester-4.3.0.tar.gz /tmp/
RUN tar -xvf /tmp/memtester-4.3.0.tar.gz -C /tmp
RUN make -C /tmp/memtester-4.3.0 && make -C /tmp/memtester-4.3.0 install- 下面是推荐写法:
1
2
3RUN wget -O /tmp/memtester-4.3.0.tar.gz http://pyropus.ca/software/memtester/old-versions/memtester-4.3.0.tar.gz \
&& tar -xvf /tmp/memtester-4.3.0.tar.gz -C /tmp \
&& make -C /tmp/memtester-4.3.0 && make -C /tmp/memtester-4.3.0 install
WORKDIR
- 为了使构建过程更加清晰明了,推荐使用
WORKDIR
来指定容器的工作路径,应该尽量避免使用RUN cd /work/path && do some work
这样的指令.
实例
1 | FROM node:20.6.0 AS builder |
1 | # 将编译以及运行环境区分 减少最终应用程序docker镜像的体积 |
1 | FROM python:3.9.16-slim |