Docker 镜像制作 dockerfiles的指令讲解 链接
CMD和ENTRYPOINT
CMD 和 ENTRYPOINT 是 Dockerfile
中用于指定容器启动时运行命令的两个指令。它们在功能上有一些相似之处,但也存在重要区别。
在编辑Dockerfile时,ENTRYPOINT 或者CMD 命令会自动覆盖之前的ENTRYPOINT 或者CMD 命令,但是ENTRYPOINT 和CMD不会相互覆盖。
如果容器启动时通过 docker run
提供了命令行参数,CMD 的内容会被覆盖,而 ENTRYPOINT 的内容可以作为基础命令,并接收 CMD 或运行参数作为附加参数。
下面通过实例来进行讲解:
编辑Dockerfile1文件:
cpp
FROM busybox
CMD echo "test cmd1"
CMD echo "TEST CMD1"
创建镜像并运行容器
bash
docker build -t cmd:0.1 -f ./Dockerfile1 .
docker run --name cmd1 --rm cmd:0.1
TEST CMD1
编辑Dockerfile2文件:
c
FROM busybox
ENTRYPOINT echo "test cmd1"
ENTRYPOINT echo "TEST CMD1"
创建镜像并运行容器
bash
docker build -t cmd:0.2 -f ./Dockerfile2 .
docker run --name cmd1 --rm cmd:0.2
TEST CMD1
编辑Dockerfile3文件:
c
FROM busybox
ENTRYPOINT echo "test cmd1"
CMD echo "TEST CMD1"
创建镜像并运行容器
bash
docker build -t cmd:0.3 -f ./Dockerfile3 .
docker run --name cmd1 --rm cmd:0.3
test cmd1
执行指令不会被覆盖,但只是执行一条语句
编辑Dockerfile0文件:
c
FROM ubuntu:latest
ENTRYPOINT ["echo"]
CMD ["Hello from CMD"]
启动容器时,默认执行 echo "Hello from CMD"
。
如果运行 docker run myimage "Hello from run"
,输出 Hello from run,因为 CMD 的内容被覆盖,但 ENTRYPOINT 仍然是 echo
。
Shell形式和exec形式
exec形式:
c
ENTRYPOINT ["executable", "param1", "param2"]
Shell形式:
bash
ENTRYPOINT command param1 param2
当使用shell表示法时,命令行程序作为sh程序的子程序运行;docker用bin/sh -c来调用 ;
当用docker ps 查看时就能看出我们运行的是/bin/sh -c 的命令;这样运行的PID不是1;这也意味着Unix不会接收任何信号;
exec语法中,没有启动/bin/sh的命令,而是直接运行提供的命令,命令PID是1.
编辑dockerfile4文件 :
shell模式
c
FROM ubuntu:22.04
RUN apt-get update -y && apt install -y iputils-ping
CMD ping localhost
创建镜像并运行容器:
bash
docker build -t cmd:0.4 -f ./Dockerfile4 .
docker run -d --name cmd1 --rm cmd:0.4
docker ps

查看容器可以看到命令为 /bin/sh -c为主程序
进入容器内进行查看,可以看到PID1是/bin/sh
bash
docker exec -it cmd1 bash
ps -ef

编辑dockerfile5文件 :
exec模式
c
FROM ubuntu:22.04
RUN apt-get update -y && apt install -y iputils-ping
CMD ["ping","localhost"]
创建镜像并运行容器:
bash
docker build -t cmd:0.5 -f ./Dockerfile5 .
docker run -d --name cmd1 --rm cmd:0.5
通过docker ps 查看:
进入到容器中查看
bash
docker exec -it cmd1 bash
ps -ef

ENTRYPOINT也是一样的操作
组合模式
ENTRYPOINT可以作为默认的命令,CMD指定默认运行参数 ;
也就是说,docker会把CMD的命令拼接到ENTRYPOINT命令后面
编辑Dockerfie6文件内容:
c
FROM ubuntu:22.04
RUN apt-get update -y && apt install -y iputils-ping
ENTRYPOINT ["/bin/ping","-c","3"]
CMD ["localhost"]
bash
docker build -t cmd:0.6 -f ./Dockerfile6 .
docker run --name cmd1 --rm cmd:0.6
PING localhost(localhost (::1)) 56 data bytes
64 bytes from localhost (::1): icmp_seq=1 ttl=64 time=0.020 ms
64 bytes from localhost (::1): icmp_seq=2 ttl=64 time=0.032 ms
64 bytes from localhost (::1): icmp_seq=3 ttl=64 time=0.031 ms
可以看到CMD的内容被添加进去了;
CMD参数替换
bash
docker run --name cmd1 --rm cmd:0.6 www.baidu.com

善用.dockerignore 文件
.dockerignore
文件是 Docker 构建镜像时的重要工具,用于指定在构建过程中应忽略的文件和目录。合理使用 .dockerignore
可以显著减少镜像体积、加快构建速度,并避免将敏感信息打包到镜像中。
默认情况下,Docker 会将 Dockerfile
所在目录的所有文件和子目录复制到构建上下文(build context)中。如果目录中包含大量无关文件(如日志文件、临时文件、IDE 配置文件等),会导致构建上下文过大,从而增加镜像体积。
构建上下文越大,Docker 在构建镜像时需要传输和处理的数据就越多,导致构建时间变长。通过忽略不必要的文件,可以显著缩短构建时间。
如果构建上下文中包含敏感文件(如 .env、密钥文件、配置文件等),可能会被意外打包到镜像中,导致安全风险。
.dockerignore
文件的语法与 .gitignore
类似,支持通配符和模式匹配。
每一行定义一个匹配规则,空行和以 # 开头的行会被忽略。
常见模式
*
:匹配任意文件或目录(不包括目录本身)。
**
:匹配任意层级的子目录。
?
:匹配单个字符。
[abc]
:匹配括号内的任意一个字符。
!
:取反,表示不忽略匹配的文件。
如:
bash
# 忽略所有日志文件
*.log
# 忽略临时文件
*.tmp
temp/
# 忽略特定目录
node_modules/
.git/
# 忽略所有隐藏文件(以.开头的文件)
.*
# 包含特定文件(即使它匹配了其他规则)
!important.txt
下面通过实例来验证:
建立dockerfile
c
FROM centos:7
COPY ./* /
编写.dockerignore
c
*.txt
创建几个文件
创建镜像
bash
docker build -t test_ignore:1.0 ./
运行容器,然后查看目录内容
bash
docker run -it test_ignore:1.0

发现对应目录后缀txt文件都没有了
镜像的多阶段构建
多阶段构建是 Docker 提供的一种优化镜像构建流程的技术,允许在单个 Dockerfile 中使用多个 FROM 指令,每个 FROM 指令开启一个新的构建阶段。最终镜像只保留最后一个阶段的构建结果,从而大幅减小镜像体积,提升安全性和性能。
传统构建方式会将所有构建依赖(如编译器、构建工具)和中间产物打包到最终镜像中,导致镜像体积庞大。
构建工具和依赖可能包含已知漏洞,移除这些工具可以降低攻击面。
将构建逻辑集中在一个 Dockerfile 中,避免维护多个独立的构建脚本。
更小的镜像意味着更快的上传、下载和启动时间。
关键点 :
每个 FROM 指令开启一个新阶段。
阶段可以通过 AS
关键字命名,方便后续引用。
使用 COPY --from= < stage >
从其他阶段复制文件。
实例:
编写dockerfile文件:
c
#第一阶段构建
FROM centos:7 AS basebuilder
ENV VERSION="1.0"
RUN sed -e 's|^mirrorlist=|#mirrorlist=|g' \
-e 's|^#baseurl=http://mirror.centos.org/centos|baseurl=https://mirrors.ustc.edu.cn/centos-vault/centos|g' \
-i.bak \
/etc/yum.repos.d/CentOS-Base.repo
WORKDIR /src
COPY demo.c .
RUN yum makecache && yum install gcc -y
RUN gcc -o demo demo.c && \
rm -f demo.c && \
yum remove -y gcc
CMD ["/src/demo"]
我们将以这个镜像作为第一阶段的构建,编写第一阶段的Dockerfile,所占内存:

编写第二阶段的Dockerfile:
c
#第一阶段构建
FROM centos:7 AS basebuilder
ENV VERSION="1.0"
RUN sed -e 's|^mirrorlist=|#mirrorlist=|g' \
-e 's|^#baseurl=http://mirror.centos.org/centos|baseurl=https://mirrors.ustc.edu.cn/centos-vault/centos|g' \
-i.bak \
/etc/yum.repos.d/CentOS-Base.repo
WORKDIR /src
COPY demo.c .
RUN yum makecache && yum install gcc -y
RUN gcc -o demo demo.c && \
rm -f demo.c && \
yum remove -y gcc
CMD ["/src/demo"]
# 第二阶段构建
FROM CentOS7
# 拷贝第一阶段生成的可执行程序
COPY --from=basebuilder /src/demo /src/demo
# 运行可执行程序
CMD ["/src/demo"]
domo文件:
c
#include <stdio.h>
int main()
{
printf("Test commit image\n");
return 0;
}
创建镜像及运行容器
bash
docker build -t multi:0.2 .
docker run --name multi --rm multi:0.2
Test commit image
查看镜像大小:
像我们这种只是跑一个可执行程序文件而已,如果我们将第二阶段基本镜像换成busybox的话,那么所占内存会更小:
bash
docker build -t multi:0.3 .
docker run --name multi --rm multi:0.3
Test commit image

合理利用缓存
Docker 构建镜像时,默认会利用缓存机制加速构建过程:
如果某层(RUN、COPY、ADD等指令)的输入未变化,Docker 会直接复用缓存结果,跳过重复执行。
优势:显著减少构建时间,尤其是在依赖较多或构建步骤复杂时。
Docker 镜像由多个层(Layer)组成,每层对应一个构建指令。
缓存以层为单位存储,只有当某层的输入(如文件内容、环境变量)变化时,该层及其后续层才会失效。
缓存失效规则
COPY/ADD 指令:
- 如果源文件内容变化,缓存失效。
- 如果仅文件名或目录结构变化,但内容未变,缓存可能仍有效(取决于文件哈希值)。
RUN 指令:
- 如果依赖的上一层缓存失效,则当前层的缓存也会失效。
环境变量:
- ARG 和 ENV 的变化会导致后续层缓存失效。
所以要利用好缓存的规则
- 将不常变化的指令放在前面,常变化的指令放在后面
- 避免在关键层使用 COPY
- 使用 ARG 参数控制构建环境
实例:
利用c++制作的镜像Dockerfile文件进行使用:
dockerfile:
c
FROM centos:7 AS basebuilder
ENV VERSION="1.0"
RUN sed -e 's|^mirrorlist=|#mirrorlist=|g' \
-e 's|^#baseurl=http://mirror.centos.org/centos|baseurl=https://mirrors.ustc.edu.cn/centos-vault/centos|g' \
-i.bak \
/etc/yum.repos.d/CentOS-Base.repo
RUN yum makecache && yum install gcc -y
WORKDIR /src
COPY demo.c .
RUN gcc -o demo demo.c && \
rm -f demo.c && \
yum remove -y gcc
CMD ["/src/demo"]
demo.c
c
#include <stdio.h>
int main()
{
printf("Test CACHE image\n");
return 0;
}
第一次创建镜像
bash
docker build -t cache:1.0 .
花费了40s的时间
第二次创建镜像之前,我们修改下demo.c:
再次创建镜像
bash
docker build -t cache:2.0 .

会发现这次创建时间还是很多,这是因为demo.c的改变,影响了Dockerfile文件内容COPY demo.c . 后面全部内容,后面的内容都要进行重新创建;所以,安排好我们的执行语句顺序非常重要,将不常修改的语句放到前头,经常修改的语句放到后头;这里是我们安装gcc影响到了,但是对于我们来说又没有经常修改,所以我们将这条语句放到前面的位置
再次创建镜像
bash
docker build -t cache:3.0 .
[+] Building 42.4s (11/11) FINISHED
由于我们改变了执行语句的顺序,所以需要重建
再次修改demo.c文件再创建镜像
bash
docker build -t cache:4.0 .
[+] Building 1.9s (11/11) FINISHED