4. 应用容器化:编写 Dockerfile、构建镜像、推送至私有仓库的最佳实践
应用容器化是云原生弹性伸缩的基石。本章将围绕 Dockerfile 编写规范、多阶段构建优化、镜像安全扫描 以及 私有仓库推送策略 四个核心环节,提供可直接落地的工程化最佳实践。
4.1 Dockerfile 编写规范与多阶段构建
一个生产级的 Dockerfile 应遵循 分层最小化、缓存友好、安全合规 三大原则。以下是一个基于 Spring Boot 应用的多阶段构建示例:
# 第一阶段:编译构建
FROM maven:3.8.6-eclipse-temurin-17 AS builder
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline -B
COPY src ./src
RUN mvn clean package -DskipTests
# 第二阶段:运行镜像
FROM eclipse-temurin:17-jre-alpine
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app
COPY --from=builder /app/target/*.jar app.jar
EXPOSE 8080
USER appuser
ENTRYPOINT ["java", "-jar", "app.jar"]
最佳实践要点:
- 使用显式基础镜像标签:避免
latest,固定到17-jre-alpine等具体版本。 - 分离依赖与源码:先拷贝
pom.xml并下载依赖,利用 Docker 缓存层加速后续构建。 - 非 root 运行:创建专用用户
appuser,降低容器逃逸风险。 - 多阶段构建:最终镜像仅包含 JRE 和 JAR,体积从 700MB 降至约 180MB。
4.2 镜像构建与标签策略
构建命令应结合 CI 环境变量 生成唯一且可追溯的镜像标签。推荐标签格式如下:
| 标签类型 | 格式示例 | 适用场景 |
|---|---|---|
| 语义化版本 | v1.2.3 |
正式发布 |
| Git 提交 SHA | sha-a1b2c3d4 |
开发/测试环境追溯 |
| 分支+构建序号 | main-42 |
CI 流水线中间产物 |
| 时间戳 | 20250321-1530 |
临时调试镜像 |
# 典型 CI 构建命令
docker build \
--build-arg APP_VERSION=${CI_COMMIT_TAG:-$CI_COMMIT_SHORT_SHA} \
-t registry.example.com/myapp:${CI_COMMIT_SHORT_SHA} \
-t registry.example.com/myapp:latest \
.
避坑提醒: 生产环境禁止推送
latest 标签到私有仓库,它会导致部署时无法精确回滚。仅在开发/测试环境保留 latest 作为快捷引用。
4.3 镜像安全扫描与合规检查
在推送之前,必须对镜像进行 漏洞扫描 和 合规性检查。推荐集成 Trivy 或 Grype 等工具:
# 使用 Trivy 扫描本地镜像
trivy image --severity CRITICAL,HIGH \
--ignore-unfixed \
--exit-code 1 \
registry.example.com/myapp:sha-a1b2c3d4
扫描结果应作为 CI 流水线的 质量门禁:
- CRITICAL 漏洞:阻断构建,必须修复。
- HIGH 漏洞:记录告警,允许人工审批通过。
- MEDIUM/LOW:记录至报告,定期跟踪。
安全基线建议:
- 基础镜像优先选择
distroless或alpine变体,减少攻击面。 - 定期(每周)重新构建基础镜像,拉取最新安全补丁。
- 在 Dockerfile 中删除
apt/apk缓存,避免遗留敏感文件。
4.4 推送至私有仓库的工程化流程
推送流程需要处理 认证、重试、签名 和 清理 四个环节。以下是一个完整的推送脚本片段:
#!/bin/bash
set -euo pipefail
REGISTRY="registry.example.com"
IMAGE_NAME="myapp"
IMAGE_TAG="${CI_COMMIT_SHORT_SHA:-dev}"
# 1. 登录私有仓库(使用 CI 预置的凭据)
echo "${REGISTRY_PASSWORD}" | docker login ${REGISTRY} \
--username "${REGISTRY_USER}" --password-stdin
# 2. 推送镜像(带重试机制)
for i in {1..3}; do
docker push ${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG} && break
sleep 5
done
# 3. 推送语义化标签(仅当有 Git Tag 时)
if [[ -n "${CI_COMMIT_TAG:-}" ]]; then
docker tag ${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG} \
${REGISTRY}/${IMAGE_NAME}:${CI_COMMIT_TAG}
docker push ${REGISTRY}/${IMAGE_NAME}:${CI_COMMIT_TAG}
fi
# 4. 清理本地镜像(释放 CI 节点磁盘空间)
docker rmi ${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG} || true
4.5 容器化交付流程总览
下图展示了从代码提交到镜像就绪的完整容器化交付链路:
4.6 镜像清理与存储成本控制
私有仓库的镜像数量会随着 CI 运行快速膨胀,必须建立 生命周期管理策略:
| 策略 | 保留规则 | 实现方式 |
|---|---|---|
| 按版本保留 | 保留最近 10 个语义化版本 | Harbor 保留策略 / 脚本清理 |
| 按时间保留 | 保留最近 30 天的构建产物 | CI 定时任务 + docker registry gc |
| 按标签保留 | 保留所有 v* 和 sha-* 标签 |
正则匹配 + 批量删除 |
| 按引用保留 | 保留正在运行的 Pod 引用的镜像 | Kubernetes API 查询 + 差异清理 |
推荐工具: Harbor 内置镜像清理规则引擎,支持基于标签、时间、引用数的自动清理;对于自建 Registry,可使用
regclient 或 crane 命令行工具编写清理脚本。
4.7 本章小结
应用容器化的最佳实践可归纳为以下检查清单:
- Dockerfile:多阶段构建、固定基础镜像版本、非 root 运行、合并 RUN 指令减少层数。
- 构建与标签:使用 Git SHA 或语义化版本作为唯一标签,避免
latest污染。 - 安全扫描:集成 Trivy/Grype,设置 CRITICAL 漏洞阻断门禁。
- 推送与签名:使用 CI 凭据登录、实现推送重试、启用 Cosign 镜像签名。
- 清理策略:按版本/时间/引用自动清理,控制存储成本。
以上实践已在多个生产集群中验证,可显著提升镜像交付的 安全性、可追溯性 和 运维效率。下一环节将进入 CI/CD 流水线集成,将容器化产物自动部署至 Kubernetes 集群。