这是《Docker 与 Kubernetes 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 Dockerfile 决定镜像是否可复现、是否安全、是否够小、是否能稳定运行。生产镜像从 Dockerfile 设计开始。
4.1 基础示例
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
COPY target/order-service.jar app.jar
RUN addgroup -S app && adduser -S app -G app
USER app
ENV JAVA_OPTS="-XX:MaxRAMPercentage=70" \
TZ=Asia/Shanghai
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD wget -qO- http://127.0.0.1:8080/actuator/health || exit 1
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar /app/app.jar"]
4.2 指令解析
| 指令 | 作用 |
|---|---|
| FROM | 基础镜像 |
| RUN | 构建期执行命令 |
| COPY | 复制上下文文件 |
| ADD | 复制并支持自动解压,慎用 |
| ENV | 环境变量 |
| ARG | 构建参数 |
| WORKDIR | 工作目录 |
| USER | 运行用户 |
| EXPOSE | 声明端口 |
| HEALTHCHECK | 容器健康检查 |
| ENTRYPOINT | 主命令 |
| CMD | 默认参数 |
4.3 层缓存
低频变化放前,高频变化放后:
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn package -DskipTests
多阶段构建:
FROM maven:3.9-eclipse-temurin-17 AS build
WORKDIR /src
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn package -DskipTests
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
COPY --from=build /src/target/app.jar app.jar
USER app
ENTRYPOINT ["java", "-jar", "/app/app.jar"]
4.4 ENTRYPOINT 与 CMD
ENTRYPOINT ["java", "-jar", "app.jar"]
CMD ["--spring.profiles.active=prod"]
区别:
| 指令 | 覆盖方式 |
|---|---|
| ENTRYPOINT | 需要 --entrypoint 覆盖 |
| CMD | docker run image args 直接覆盖 |
应用容器应保证 PID 1 能转发 SIGTERM。Shell 形式可能不会转发信号,exec 形式更常用。
4.5 镜像安全
原则:
- 固定基础镜像版本;
- 使用官方或可信镜像;
- 非 root 运行;
- 不把 Secret 写入镜像;
- 清理包缓存和测试文件;
- 使用镜像扫描;
- 启用内容信任;
- 最小化安装包。
扫描:
trivy image shop/order-service:v1.2.0
4.6 构建变量与 Secret
普通构建参数:
ARG APP_VERSION=dev
ENV APP_VERSION=${APP_VERSION}
构建命令:
docker build --build-arg APP_VERSION=1.2.0 -t order-service:1.2.0 .
不要用 ARG 传密码。敏感值应通过运行时 Secret、构建挂载或 CI 密钥管理注入。
4.7 常见反模式
| 反模式 | 问题 |
|---|---|
| 使用 latest | 不可复现 |
| root 运行 | 安全风险 |
| 一个镜像包含多个服务 | 边界混乱 |
| 日志写容器层 | 数据丢失 |
| 只写 CMD 不处理信号 | 优雅停机失败 |
ADD url 下载依赖 |
不可控 |
| 把数据库密码写入 ENV | 泄露 |
| 依赖交互式初始化 | 容器无法自愈 |
本章小结
好的 Dockerfile 应固定版本、多阶段构建、非 root 运行、按缓存优化层顺序、处理信号和健康检查,并把 Secret 留在运行时注入。
思考题
- 为什么依赖文件先 COPY?
- ENTRYPOINT 和 CMD 如何配合?
- 多阶段构建解决什么问题?
- 为什么 Shell 形式可能影响优雅停机?
- 镜像漏洞治理流程是什么?