Dockerfile 编写最佳实践
参考:https://docs.docker.com/develop/develop-images/dockerfile_best-practices/
1 基本结构
# 指定基础镜像
FROM debian:bookworm
# 元信息
LABEL maintainer="ops@example.com"
# 设置环境变量
ENV APP_HOME=/usr/local/app \
APP_ENV=production
# 切到工作目录
WORKDIR $APP_HOME
# 拷贝文件(先拷贝依赖清单,再拷贝源码,最大化利用缓存)
COPY requirements.txt .
# 执行安装命令(把多个命令合并到单层)
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc \
libpq-dev \
&& rm -rf /var/lib/apt/lists/*
# 复制应用代码
COPY . .
# 声明运行时监听端口(仅文档性质,不真正端口映射)
EXPOSE 8080
# 容器启动命令
CMD ["python", "app.py"]
2 核心实践
2.1 镜像分层与缓存
Dockerfile 的每条指令都生成一层镜像,docker build 会基于层做缓存。把不变的内容放前面、易变的内容放后面,可最大化缓存命中:
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt # 依赖层:requirements.txt 不变则命中缓存
COPY . ./ # 代码层:最后拷贝
2.2 使用多阶段构建
单个 FROM 无法在最终镜像中剔除编译工具,多阶段构建可显著减小体积:
# 阶段一:构建
FROM golang:1.22 AS builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 go build -o app .
# 阶段二:运行
FROM alpine:3.20
COPY --from=builder /app/app /usr/local/bin/app
EXPOSE 8080
ENTRYPOINT ["app"]
2.3 优先使用官方镜像与固定版本
- 尽量选用基于 alpine 的镜像(
node:22-alpine、python:3.12-slim等),减少体积与攻击面。 - 不要使用
latest等可变标签,锁定具体版本号。
2.4 清理不需要的文件
RUN apt-get update && apt-get install -y --no-install-recommends <pkg> \
&& rm -rf /var/lib/apt/lists/* # 清理 apt 索引
2.5 ENTRYPOINT 与 CMD 的配合
ENTRYPOINT:固定入口,不宜被参数覆盖;CMD:默认参数,可以被docker run的命令行参数替换。
ENTRYPOINT ["gunicorn"]
CMD ["-w", "4", "app:app"]
2.6 使用 .dockerignore
构建上下文里排除无关文件,加快传输并避免把敏感文件送进镜像:
.git
.gitignore
*.md
node_modules/
**/__pycache__/
*.log
3 常见问题
- 层过多、镜像过大:把多个
RUN合并,或在多阶段构建中仅拷贝产物。 - 敏感信息入镜像:禁止把密码/密钥写进 Dockerfile 或 ENV,改用
--secret、构建 secret 或运行期环境变量注入。 - 挂载文件权限问题:容器默认 root 或无权限用户,如需写入宿主机挂载卷,明确
USER与文件权限。 docker commit不可复制:任何需要长期维护的镜像都应该用 Dockerfile 管理。
4 快速检查镜像体积与分层
docker history <image> # 查看每一层大小
docker inspect <image> # 查看镜像元数据
docker system df # 全局占用情况
阅读 —
·
全站 —