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️⃣ 对比两次构建的时间

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

相关推荐
大树8822 分钟前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠25 分钟前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
霸道流氓气质1 小时前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
东方佑1 小时前
FRSM 规模效应与架构对比补充报告
架构
Inhand陈工2 小时前
基于台达PLC与映翰通IG502的智慧水产养殖精准投喂与远程运维解决方案
运维·人工智能·物联网·阿里云·信息与通信
Alsn862 小时前
等待学习-学习目录:Docker 容器安全攻防
学习·安全·docker
酣大智2 小时前
ARP代理--工作原理
运维·网络·arp·arp代理
shushangyun_2 小时前
2026年快消品B2B系统推荐:支持终端门店订货、促销政策自动化的工具?
java·运维·网络·数据库·人工智能·spring·自动化
隔窗听雨眠2 小时前
大模型加爬虫上篇:技术融合与架构革新
爬虫·架构
ofoxcoding3 小时前
在AI API聚合平台配置DeepSeek V3.2提示词缓存实战:快速接入与成本优化指南
人工智能·spring·缓存·ai