第十篇:《Dockerfile 最佳实践与镜像瘦身》

写完 Dockerfile 只是第一步,写出高效、安全、小巧的镜像才是生产级应用的要求。一个臃肿的镜像不仅浪费存储和带宽,还会拖慢部署和安全扫描。本文将总结 Dockerfile 的最佳实践,并介绍多种镜像瘦身技巧,让你的镜像"轻装上阵"。

一、基础镜像的选择

1.2 使用 scratch 空镜像

适用于静态编译的二进制文件(如 Go、Rust),可以构建只包含一个可执行文件的镜像,大小仅几 MB。

dockerfile

FROM scratch

COPY mybinary /mybinary

CMD "/mybinary"

二、层优化:减少层数 & 利用缓存

2.1 合并 RUN 命令

每一条 RUN、COPY 都会创建一个新层。将相关命令用 && 连接,减少层数。

dockerfile

差

RUN apt-get update

RUN apt-get install -y curl

RUN rm -rf /var/lib/apt/lists/*

好

RUN apt-get update && apt-get install -y curl

&& rm -rf /var/lib/apt/lists/*

2.2 合理安排指令顺序,最大化缓存

Docker 会缓存每一层,如果某层指令没变,后续层可以复用。因此,将不常变化的指令放在前面。

dockerfile

先复制依赖清单(变化少)

COPY package*.json ./

RUN npm ci

最后复制源代码(变化频繁)

COPY . .

2.3 使用 .dockerignore

排除版本控制文件、日志、临时文件等,避免它们进入构建上下文,也减少镜像层中的无用内容。

text

node_modules

.git

*.log

.DS_Store

三、多阶段构建(Multi-stage Builds)

多阶段构建允许在一个 Dockerfile 中使用多个 FROM,只将最终需要的文件复制到最终镜像,从而彻底排除编译工具和中间产物。

示例:编译 Go 程序

dockerfile

阶段1:构建

FROM golang:1.20 AS builder

WORKDIR /app

COPY go.mod go.sum ./

RUN go mod download

COPY . .

RUN CGO_ENABLED=0 GOOS=linux go build -o myapp .

阶段2:最终镜像

FROM alpine:latest

RUN apk add --no-cache ca-certificates

COPY --from=builder /app/myapp /usr/local/bin/myapp

CMD "myapp"

最终镜像仅包含 Alpine 基础层 + ca-certificates + 二进制文件,大小可能从几百 MB 降到十几 MB。

四、清理临时文件

在同一个 RUN 中,安装完软件后立即清理缓存。

Debian/Ubuntu:

dockerfile

RUN apt-get update && apt-get install -y

package1

package2

&& rm -rf /var/lib/apt/lists/*

Alpine:

dockerfile

RUN apk add --no-cache package1 package2

--no-cache 自动不保留索引缓存

yum (CentOS):

dockerfile

RUN yum install -y package1 && yum clean all

五、减少不必要的文件

避免安装文档、手册:某些包管理器有 --no-docs 选项。

删除临时下载的源码包:例如 RUN wget ... && tar xf ... && make && make install && rm -rf /tmp/*。

仅复制需要的文件:使用 COPY --chmod 或指定文件列表,而非整个目录。

六、使用 --squash 实验性功能(可选)

Docker 支持 --squash 参数将多层合并为一层,但会失去层的缓存优势,一般用于最终发布前。需要开启实验性特性。

bash 复制代码
docker build --squash -t myimage .

七、使用工具分析镜像大小

docker history:查看每层大小。

dive:交互式工具,显示各层内容,找出大文件。

bash 复制代码
dive myimage

docker scout:官方安全与大小分析工具。

八、安全最佳实践

以非 root 用户运行:

dockerfile

RUN addgroup -g 1000 app && adduser -u 1000 -G app -D app

USER app

使用固定标签:不用 latest,指定具体版本(如 alpine:3.18)。

扫描漏洞:docker scan 或 docker scout 检测已知漏洞。

不要存储 secrets:使用 Docker Secret 或外部密钥管理。

九、完整优化示例:Python 应用

dockerfile

多阶段构建

FROM python:3.11-slim AS builder

WORKDIR /app

COPY requirements.txt .

RUN pip install --user --no-cache-dir -r requirements.txt

FROM python:3.11-slim

WORKDIR /app

COPY --from=builder /root/.local /root/.local

COPY . .

ENV PATH=/root/.local/bin:$PATH

USER 1000

EXPOSE 8000

CMD "python", "app.py"

注意:pip install --user 将包安装到用户目录,避免污染系统 Python。

十、常见问题与解决

十一、小结

镜像瘦身是 Docker 生产化的重要环节。核心原则:

选择合适的基础镜像(优先 Alpine / slim)。

合并层并清理缓存。

多阶段构建剔除编译工具。

以非 root 运行提高安全性。

使用分析工具持续优化。

一个几十 MB 的镜像比几百 MB 的镜像在分发、部署、安全扫描方面都有巨大优势。现在就开始优化你的 Dockerfile 吧!

相关推荐
对象存储与RustFS17 小时前
网盘限速逼你开会员?用 RustFS 自己搭一个不限速的“私人网盘
服务器·后端·docker
程序员老赵19 小时前
Docker 部署 Filestash:轻松搭建浏览器文件管理平台
前端·docker·开源
艾伦_耶格宇19 小时前
【DOCKER容器实战】-5 Docker容器镜像的构建
运维·docker·容器
程序员小孔.1 天前
基于 SpringBoot + UniApp + Vue 的家政服务系统设计与实现
前端·数据库·spring boot·python·docker·微信小程序
我不会起名字3221 天前
Redis 从 0 到 1(九):主库宕机 30 秒自动切主——1 主 2 从 3 哨兵 + Go 客户端
redis·docker·sentinel·高可用·哨兵
姜鱼问生1 天前
每次开机手动检查 6 样东西?我写了个 2 秒自检脚本
运维·网络·nginx·docker·容器
wangqiaowq1 天前
PostgresSQL server Docker 安装部署
运维·docker·容器
程序员阿明2 天前
docker搭建视频流服务go2rtc,webRTC
docker·容器·webrtc
TTATTC2 天前
让 16 个 AI 员工在飞书群自我协同:基于 OpenClaw 的多 Agent 敏捷 Squad 拓扑与一人公司实操
docker·开源
TTATTC2 天前
从分片续传到 SPI 插件化扩展:Free-FS 企业级文件系统核心架构与万能预览技术拆解
docker·开源