一、为什么镜像优化值得单独做¶
镜像越大,意味着:
- 拉取越慢
- 传输越慢
- 存储成本越高
- 发布和回滚效率越低
因此镜像优化本质上不是“为了好看”,而是为了让交付更快、更省资源、更稳定。
二、基础镜像的选择会直接决定下限¶
原始示例里,基于 CentOS 创建一个简单用户,镜像大小仍然在 232MB 左右;但换成 Alpine 之后,镜像大小可以直接降到 5MB 级别。
例如:
FROM registry.cn-hangzhou.aliyuncs.com/abroad_images/alpine:3.12
RUN adduser -D dot
构建后镜像只有约 5.59MB。
这说明一个非常关键的原则:
- 如果运行时真的不需要完整系统环境,就优先考虑轻量级基础镜像
当然,前提是你的应用依赖和运行方式确实适配 Alpine 这类轻量发行版。
三、多阶段构建为什么这么有效¶
多阶段构建的核心思想是:
- 编译阶段使用功能完整的镜像
- 运行阶段只保留真正需要的产物
这能极大减少最终镜像中的冗余工具链。
3.1 单阶段构建的问题¶
例如 Go 示例中,使用带完整编译环境的镜像直接构建并运行:
FROM registry.cn-hangzhou.aliyuncs.com/abroad_images/golang:1.14.4-alpine
WORKDIR /opt
COPY hw.go /opt
RUN go build /opt/hw.go
CMD "./hw"
最终镜像大小约 372MB,但真正编译出来的可执行文件只有大约 2MB。
3.2 多阶段构建的收益¶
更合理的写法是:
FROM registry.cn-hangzhou.aliyuncs.com/abroad_images/golang:1.14.4-alpine AS builder
WORKDIR /opt
COPY hw.go /opt
RUN go build /opt/hw.go
FROM scratch
COPY --from=builder /opt/hw .
ENTRYPOINT ["./hw"]
这样最终镜像只保留编译后的程序,镜像大小会显著下降到几 MB 级别。
四、为什么“容器内改文件权限”会让镜像暴涨¶
原始笔记给了一个非常典型的反例:先把大压缩包 ADD 进镜像,再通过 RUN chmod 递归改权限。
这种方式的问题在于:
ADD会形成一层chmod又会形成一层- 大量文件变更会导致新层体积非常大
结果就是镜像能从约 373MB 膨胀到 741MB。
五、正确做法:尽量在容器外处理文件¶
如果你需要对大量文件做权限调整,更高效的方式通常是:
- 在宿主机上提前解压和赋权
- 再重新打包
- 最后把处理好的文件直接加入镜像
这样构建过程中只会保留更干净的结果层,而不会把中间的权限变更过程再次额外记录成一个大层。
这也是镜像优化里非常实用的一条经验:
- 能在构建前完成的文件处理,尽量不要留到构建中做
六、指令合并为什么能减少层数¶
Dockerfile 中每条 RUN 指令通常都会形成一个镜像层,因此如果你把多个本可一次完成的操作拆成很多条 RUN,镜像层数会明显增加。
例如优化前:
FROM registry.cn-hangzhou.aliyuncs.com/abroad_images/alpine:3.12
RUN sed -i "s@dl-cdn.alpinelinux.org@mirrors.aliyun.com@g" /etc/apk/repositories
RUN apk update
RUN apk add nginx curl
优化后:
FROM registry.cn-hangzhou.aliyuncs.com/abroad_images/alpine:3.12
RUN sed -i "s@dl-cdn.alpinelinux.org@mirrors.aliyun.com@g" /etc/apk/repositories && \
apk update && \
apk add nginx curl
合并后,优点很明确:
- 层数更少
- 中间态更少
- 构建逻辑更集中
七、镜像优化时最值得优先做的四件事¶
结合 Day004 的内容,实战里最推荐优先处理的就是下面四件事:
7.1 先选轻量级基础镜像¶
如果业务兼容,优先 Alpine、distroless、scratch 这类更轻量的运行时底座。
7.2 优先使用多阶段构建¶
尤其是 Go、Java、Node 这类常见业务项目,编译环境和运行环境通常没有必要塞进同一个最终镜像。
7.3 大文件处理尽量前置到容器外¶
权限修改、解压缩、重新打包等大批量文件操作,尽量在宿主机或构建前阶段完成。
7.4 合并可连续执行的命令¶
把多个紧密相关的 RUN 合并,减少层数和中间状态。
八、镜像优化的目标不是极限瘦身,而是合理权衡¶
最后要注意,镜像优化并不是越小越好,而是要在下面几项之间找到平衡:
- 体积
- 可维护性
- 可调试性
- 兼容性
- 安全性
比如 scratch 很小,但调试不方便;Alpine 很轻,但某些依赖兼容性未必最佳。因此真正好的镜像优化,不是盲目追求数字,而是让镜像在你的业务场景里更高效、更合理。