本节摘要:Docker 将应用与依赖打包为不可变镜像;Kubernetes 编排容器部署、扩缩与滚动更新。本节给出安全 Dockerfile、CI 中 build-push-deploy 完整流程,以及 Deployment/Service/Ingress 最小 manifest。
阅读完本节,你应当能够:
FROM python:3.11-slim AS builder WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . FROM python:3.11-slim RUN useradd -m appuser WORKDIR /app COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages COPY --from=builder /app /app USER appuser EXPOSE 8080 CMD ["gunicorn", "-b", "0.0.0.0:8080", "app:application"]
要点:多阶段减体积;非 root 防容器逃逸;slim 基础镜像减 CVE 面。

jobs: docker: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: docker/login-action@v3 with: registry: registry.corp.com username: ${{ secrets.REG_USER }} password: ${{ secrets.REG_PASS }} - uses: docker/build-push-action@v5 with: push: true tags: registry.corp.com/myapp:${{ github.sha }} deploy: needs: docker runs-on: ubuntu-latest steps: - run: | kubectl set image deployment/myapp \ app=registry.corp.com/myapp:${{ github.sha }} \ -n production - run: kubectl rollout status deployment/myapp -n production
同一 SHA tag 从 CI 到 prod——容器化天然支持原则5。
apiVersion: apps/v1 kind: Deployment metadata: { name: myapp, namespace: production } spec: replicas: 3 selector: { matchLabels: { app: myapp } } template: metadata: { labels: { app: myapp } } spec: containers: - name: app image: registry.corp.com/myapp:abc123 ports: [{ containerPort: 8080 }] resources: requests: { cpu: 100m, memory: 128Mi } limits: { cpu: 500m, memory: 512Mi } livenessProbe: httpGet: { path: /health, port: 8080 } initialDelaySeconds: 10 --- apiVersion: v1 kind: Service metadata: { name: myapp } spec: selector: { app: myapp } ports: [{ port: 80, targetPort: 8080 }]
livenessProbe 让 K8s 自动重启 unhealthy pod——与第4章 rollback 互补。
| 维度 | VM + Ansible | Docker + K8s |
|---|---|---|
| 启动 | 分钟 | 秒 |
| 密度 | 低 | 高 |
| 回滚 | 慢 | rollout undo |
| 环境一致 | 需配置管理 | 镜像即环境 |
⚠️ 常见坑:
:latesttag——K8s 默认imagePullPolicy: IfNotPresent,节点缓存旧 latest,「部署了但没更新」。
💡 关键直觉:SOURCE 强调 Docker 是通用构建产物——Java JAR、Node dist 都可封进同一交付单元。
.git node_modules __pycache__ *.md tests/
减少 build context 体积——CI 中 COPY 更快,缓存层更稳。
helm create myapp helm upgrade --install myapp ./myapp -f values-prod.yaml
manifest 模板化后,CD 管道只需 helm upgrade --set image.tag=$SHA。
resources: requests: { cpu: 100m, memory: 128Mi } limits: { cpu: 500m, memory: 512Mi }
无 limits 的 pod 可能 OOM 拖垮节点——CI 生成的 manifest 模板应强制 limits。ResourceQuota 限制 namespace 总 CPU/memory。
Kyverno/OPA Gatekeeper 拒绝 Critical CVE 镜像 deploy:
validate: message: "Critical CVE found" pattern: spec: containers: - name: "*" image: "!*:latest"
与 Trivy CI 扫描双保险——坏镜像进不了 cluster。
Istio sidecar 注入后,金丝雀 traffic split 在 mesh 层——app 无感知。代价:每 pod 多一个 envoy 容器,内存 +128Mi 量级。
- uses: docker/build-push-action@v5 with: cache-from: type=registry,ref=registry/app:buildcache cache-to: type=registry,ref=registry/app:buildcache,mode=max
跨 CI job 复用 layer——Java Maven dependency layer 尤其受益。
下一节 5.2 Terraform 与 IaC 如何 provisioning 环境。
容器化 CI/CD 的核心收益是「交付单元统一」:无论 Java、Node 还是 Python,都以镜像为交付单元,构建、扫描、部署、回滚的操作全部统一。落地时先从 Dockerfile 开始:多阶段构建分离构建环境与运行环境,非 root 用户降低逃逸风险,基础镜像固定 tag 而非 latest。镜像 tag 一律用 commit SHA 或含版本号的不变标识,禁止 latest——latest 在 K8s 下会因 IfNotPresent 导致「部署了但没更新」。
镜像安全要在 CI 阶段扫描:Trivy/Grype 扫 CVE,Cosign 签名保证供应链完整,Kyverno 准入控制拒绝 Critical 镜像进集群。K8s 侧配置 resources 与探针(liveness/readiness)是上线的基本要求,配合 HPA 实现弹性。对团队规模较小的项目,先从单个 Deployment+Service 开始,逐步引入 Helm 与 GitOps,避免一开始就背上 Argo CD 的复杂度。
对没有容器基础的团队,建议按四步渐进:第一步把服务打包进镜像并在本地验证;第二步将镜像推送到私有仓库并在 CI 中构建;第三步部署到 K8s 并配置探针与资源限制;第四步加入镜像扫描与签名。每一步都先解决「跑得起来」,再谈优化与安全。容器化不是终点,而是为后续 IaC、GitOps 与弹性伸缩打基础的统一交付形态。