Docker镜像构建优化指南:CMD/ENTRYPOINT、多阶段构建与缓存优化

文章目录

一、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模式编辑,CMDENTRYPOINT配合使用:entrypoint负责可执行程序,cmd负责参数传入。

覆盖测试

  1. 创建Dockerfile
  2. 填写多条CMD语句
  3. 创建镜像
  4. 启动容器
  5. 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

主要好处:

  • 减小镜像大小 - 避免将不必要的文件打包进镜像
  • 提高构建速度 - 减少 COPYADD 指令处理的数据量
  • 增强安全性 - 防止敏感文件(如密钥、配置文件)意外进入镜像
  • 避免缓存失效 - 不重要的文件变化不会导致构建缓存失效

🛠️ 如何使用 .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, ...),每个阶段都是一个独立的镜像构建过程,并且你可以有选择地将前一个阶段的产物复制到后一个阶段,最终只产出最后一个阶段的镜像。

这解决了传统单阶段构建模式(图中的"单阶段")所带来的问题。

  1. 单阶段构建
  • 模式:在一个 Docker 镜像中完成所有事情(编译、测试、打包、运行)。
  • 问题层次多,体积大、部署时间长、安全性差。
  1. 手动拆分
  • 模式:开发人员意识到单阶段的问题,于是手动拆分流程。
  • 问题流程复杂、不标准。
  1. 多阶段构建
  • 模式 :用一个 Dockerfile 定义多个阶段,自动完成"按需构建和复制"。
  • 如何工作
    • stage1(编译阶段) :使用一个包含 java/mavengcc 的较大镜像,任务是将源代码编译成可执行的二进制文件(如 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 里的一条指令(RUNCOPYADD 等)。构建时:从第一条指令开始,Docker 检查是否已有缓存层(基于指令字符串和文件内容);如果缓存存在,就复用该层,继续下一条;一旦某条指令的缓存没命中(指令内容变化,或 COPY/ADD 的文件内容变化),后续所有指令都会重新执行,不再使用缓存。

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

❗注意:两种方式初次构建的时间是一样的,主要影响的是二次构建。

测试方式:

  • 1️⃣copy在前run安装在后,完成初次构建
  • 2️⃣更改源代码,然后再进行二次构建并记录时间
  • 3️⃣run安装前copy放在后,完成初次构建
  • 4️⃣更改源代码,然后再进行二次构建并记录时间
  • 5️⃣ 对比两次构建的时间

非常感谢您能耐心读完这篇文章。倘若您从中有所收获,还望多多支持呀!🎉

相关推荐
❀͜͡傀儡师5 小时前
docker安装mac系统
macos·docker·容器
0***147 小时前
PHP在微服务中的架构设计
微服务·云原生·架构
uzong8 小时前
Mermaid: AI 时代画图的魔法工具
后端·架构
ζั͡山 ั͡有扶苏 ั͡✾9 小时前
EFK 日志系统搭建完整教程
运维·jenkins·kibana·es·filebeat
TH_19 小时前
腾讯云-(8)-宝塔面板-安装Docker
docker·云计算·腾讯云
RUNNING123!9 小时前
RedHat 7.9 docker 安装 zabbix
docker·容器·zabbix
jun_bai10 小时前
python写的文件备份网盘程序
运维·服务器·网络
m***567210 小时前
Win10下安装 Redis
数据库·redis·缓存
爱吃牛肉的大老虎10 小时前
网络传输架构之gRPC讲解
网络·架构