文章目录
一、cmd与entrypoint🔍
基本概念:
CMD指令: 指定容器启动时默认执行的命令。它不会在构建时执行,只是在镜像中记录了"当有人运行这个容器时,默认应该执行什么"。
ENTRYPOINT指令: 类似CMD默认执行一个指令,但它是将配置容器作为一个可执行程序来运行。它让容器表现得像一个独立的二进制文件。
特性:
共同特性:
- 覆盖特性: 如果在
Dockerfile中写了很多个这样的指令,那么后一个的会覆盖前面的。区别:
cmd:docker run时指定命令会把cmd命令覆盖。entrypoint:docker run 时添加的指令,会默认作为ENTRYPOINT的参数;若需覆盖ENTRYPOINT,需通过--entrypoint参数指定
指令的两种格式:
exec模式:把命令放在Json数组shell模式:直接把命令放在后面- 效果上的差异:
exec模式:指令程序被直接启动,pid为1,可直接与程序进行交互。shell模式:不是直接启动指令程序,pid不是1,而是作为sh的子程序运行。需要通过sh与程序进行交互。示例:
CMD nginx -g "daemon off"(shell模式)
执行:/bin/sh -c "nginx -g "daemon off"CMD ["nginx","-g","daemon off"](exec模式)
执行:nginx -g "daemon off"
使用推荐:
使用
exec模式编辑,CMD和ENTRYPOINT配合使用:entrypoint负责可执行程序,cmd负责参数传入。
覆盖测试
- 创建Dockerfile
- 填写多条CMD语句
- 创建镜像
- 启动容器
- Dockerfile文件中CMD改为ENTRYPOINT
Dockerfile文件:
json
FROM busybox
CMD echo "hello world"
CMD echo "hello docker"
构建镜像:docker build -t cmd:v0.1 .
运行镜像:
CMD测试:

ENTRYPOINT测试:

可使用--entrypoint选项指定指令执行:

shell与exec
exec模式:

shell模式:

cmd与entrypoint组合

二、优雅构建镜像(过滤文件)📝
很多时候我们会将当前目录(构建上下文)中的所有文件拷贝给docker容器。但很多时候,我们的项目中包含一些不需要被打进镜像的文件,这些文件不仅会增加镜像大小,延长构建时间,还可能带来安全风险。这时候.dockerignore就该上场了。
🎯 什么是 .dockerignore?
.dockerignore 文件是 Docker 构建过程中的一个重要工具,它的作用类似于 .gitignore,用于指定在构建 Docker 镜像时应该忽略哪些文件和目录,防止它们被复制到镜像中。
📝 为什么要使用 .dockerignore?
主要好处:
- 减小镜像大小 - 避免将不必要的文件打包进镜像
- 提高构建速度 - 减少
COPY和ADD指令处理的数据量 - 增强安全性 - 防止敏感文件(如密钥、配置文件)意外进入镜像
- 避免缓存失效 - 不重要的文件变化不会导致构建缓存失效
🛠️ 如何使用 .dockerignore
基本语法:
*匹配任意数量字符?匹配单个字符**匹配任意层级的目录!表示例外(不忽略)
示例:
# * - 匹配任意数量字符(除了路径分隔符)
*.log # 忽略所有 .log 文件
src/*.js # 忽略 src 目录下的所有 .js 文件
#-------------------------------------------------------
# ? - 匹配单个字符
file?.txt # 忽略 file1.txt, file2.txt 等
#-------------------------------------------------------
# ** - 匹配任意层级的目录
**/test # 忽略所有 test 目录
src/**/*.js # 忽略 src 下任意层级的 .js 文件
#-------------------------------------------------------
# ! 表示例外(不忽略)
!README.md #保留README.md 文件
*.md #忽略其他 .md 文件
#-------------------------------------------------------
文件位置:.dockerignore 应该放在 Dockerfile 同一目录下。
实战示例:

三、多阶段构建🚀
Docker 多阶段构建允许你在一个 Dockerfile 中定义多个"构建阶段"(图中的 Stage1, Stage2, ...),每个阶段都是一个独立的镜像构建过程,并且你可以有选择地将前一个阶段的产物复制到后一个阶段,最终只产出最后一个阶段的镜像。
这解决了传统单阶段构建模式(图中的"单阶段")所带来的问题。

- 单阶段构建
- 模式:在一个 Docker 镜像中完成所有事情(编译、测试、打包、运行)。
- 问题 :层次多,体积大、部署时间长、安全性差。
- 手动拆分
- 模式:开发人员意识到单阶段的问题,于是手动拆分流程。
- 问题 :流程复杂、不标准。
- 多阶段构建
- 模式 :用一个
Dockerfile定义多个阶段,自动完成"按需构建和复制"。 - 如何工作 :
stage1(编译阶段) :使用一个包含java/maven或gcc的较大镜像,任务是将源代码编译成可执行的二进制文件(如 JAR 包或可执行文件)。这个阶段可以包含多个步骤,比如用junit进行测试。stage2(运行阶段) :使用一个非常精简的镜像(比如只包含java运行时或glibc)。然后,使用COPY --from指令,只从stage1里把编译好的最终产物(JAR 包)复制过来。- 最终镜像 :只包含
stage2的内容,也就是一个最精简的运行环境和你的应用程序。stage1中的所有构建工具(Maven, GCC)都被抛弃了,不会出现在最终镜像里。
测试
测试文件:
c
#include <stdio.h>
int main()
{
printf("hello docker");
return 0;
}
单阶段构建:
FROM ubuntu:22.04
# 设置国内源(阿里云Ubuntu镜像)
RUN sed -i 's/archive.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list && \
sed -i 's/security.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list
# 更新软件包列表并安装gcc
RUN apt-get update && \
apt-get install -y gcc && \
gcc -v
# 设置工作目录
WORKDIR /src
# 拷贝源文件
COPY demo.c .
# 编译源文件并清理
RUN gcc demo.c -o demo && \
rm -f demo.c && \
apt-get remove -y gcc && \
apt-get autoremove -y && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
# 运行可执行文件
CMD ["/src/demo"]
多阶构建:
FROM ubuntu:22.04 as buildstage
# 设置国内源(阿里云Ubuntu镜像)
RUN sed -i 's/archive.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list && \
sed -i 's/security.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list
# 更新软件包列表并安装gcc
RUN apt-get update && \
apt-get install -y gcc && \
gcc -v
# 设置工作目录
WORKDIR /src
# 拷贝源文件
COPY demo.c .
# 编译源文件并清理
RUN gcc demo.c -o demo && \
rm -f demo.c && \
apt-get remove -y gcc && \
apt-get autoremove -y && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
FROM busybox
COPY --from=buildstage /src/demo /src/
CMD ["/src/demo"]

四、合理使用缓存⚡
Docker 层缓存原理
Docker 镜像由只读层组成,每一层对应 Dockerfile 里的一条指令(RUN、COPY、ADD 等)。构建时:从第一条指令开始,Docker 检查是否已有缓存层(基于指令字符串和文件内容);如果缓存存在,就复用该层,继续下一条;一旦某条指令的缓存没命中(指令内容变化,或 COPY/ADD 的文件内容变化),后续所有指令都会重新执行,不再使用缓存。

💡所以为了提高构建效率,通常会把变更频繁的指令或文件放在后面执行。示例:代码常常会被频繁修改,所以我们把它放在后面执行,把编译软件的安装放在前面就能提高二次构建的效率。

❗注意:两种方式初次构建的时间是一样的,主要影响的是二次构建。
测试方式:
- 1️⃣copy在前run安装在后,完成初次构建
- 2️⃣更改源代码,然后再进行二次构建并记录时间
- 3️⃣run安装前copy放在后,完成初次构建
- 4️⃣更改源代码,然后再进行二次构建并记录时间
- 5️⃣ 对比两次构建的时间
非常感谢您能耐心读完这篇文章。倘若您从中有所收获,还望多多支持呀!🎉
