我要提问
PROJECT CASE / 006

自动化运维平台实战

Docker + Kubernetes + ArgoCD,从手工运维到 GitOps 自动发布的演进。

自动化运维平台实战:Docker + K8s + GitOps 灰度发布平台

自动化运维平台实战项目示意图

项目背景

某成长型公司的发布流程长期是「SSH 登服务器 + git pull + 手动重启」,十几台机器逐台操作,发一次版要 2 小时,中途还经常出现「某台漏拉代码」导致版本不一致。故障定位全靠看日志文件,没有统一监控,出了问题往往是用户先投诉运维才知道。

本次目标是搭一套从代码提交到上线全自动的运维平台:用 Docker 做容器化交付、Kubernetes 做编排、Helm 管理多环境配置、ArgoCD 做 GitOps 自动同步,并补齐监控告警。要求发布时间从 2 小时降到 10 分钟内、支持灰度、线上故障 1 分钟内告警。

运维自动化的核心不是工具多先进,而是「一切皆代码、一切可回滚、一切可观测」——GitOps 把这三条用一个 Git 仓库兜住了。

架构设计

整体分「交付流水线」与「运行平台」两块。交付流水线:代码 push → CI 构建镜像 → 推镜像仓库 → 更新 GitOps 仓库的镜像 tag;运行平台:ArgoCD 监听 GitOps 仓库变化,自动同步到 K8s 集群,配置与声明全部在 Git 里。

交付链路:
Git(代码) → CI(构建镜像) → Registry(镜像仓库)
                              ↓
Git(运维配置库) ← 改 image tag ← CI 自动提交
        ↓
ArgoCD(监听配置库) → 同步 → Kubernetes 集群
                        ↓
Prometheus + Grafana(监控告警)

关键设计:环境差异(dev/staging/prod)通过 Helm values 覆盖,不维护多套 YAML;灰度用 Argo Rollouts 的 Canary 策略,按 10%→30%→100% 分批;所有配置变更走 PR review,合并即触发 ArgoCD 同步,回滚就是 git revert。

核心实现

1)Dockerfile 与镜像瘦身:多阶段构建,编译产物与运行环境分离,基础镜像用 alpine,把镜像从 1.2GB 压到 78MB。

# Dockerfile —— 多阶段构建,镜像瘦身
FROM golang:1.21-alpine AS builder
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download              # 依赖层单独缓存
COPY . .
RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o /app/server ./cmd

FROM alpine:3.19
RUN apk add --no-cache ca-certificates tzdata
COPY --from=builder /app/server /app/server
USER nobody                      # 非 root 运行
ENTRYPOINT ["/app/server"]

2)Helm 多环境配置:用 values-dev / values-prod 覆盖基础 values,环境差异只改副本数、资源、镜像 tag。

# helm/values-prod.yaml —— 生产环境覆盖
replicaCount: 4
image:
  repository: registry.cn/app/server
  tag: ""            # 由 GitOps CI 自动写入
resources:
  requests: { cpu: 200m, memory: 256Mi }
  limits:   { cpu: 1,    memory: 512Mi }
env:
  LOG_LEVEL: info
  DB_HOST: prod-db.internal

3)Argo Rollouts 灰度发布:用 Rollout 资源替代 Deployment,按 Canary 分批放量,每批暂停等人工或自动指标确认。

# rollout.yaml —— Canary 分批灰度
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: server
spec:
  replicas: 10
  strategy:
    canary:
      steps:
        - setWeight: 10           # 先放 10% 流量
        - pause: { duration: 5m } # 等 5 分钟观察指标
        - setWeight: 30
        - pause: { duration: 5m }
        - setWeight: 100
  template:
    spec:
      containers:
        - name: server
          image: registry.cn/app/server:placeholder
          resources: { { {requests: {cpu: 200m}} } }
  • 发布时长:2 小时 → 8 分钟(含灰度观察)
  • 镜像体积:1.2GB → 78MB
  • 故障告警延迟:用户投诉 → 40 秒
  • 回滚时长:30 分钟 → 1 分钟(git revert)

技术难点

难点一:配置漂移。运维同事有时会手动 kubectl edit 改线上配置,导致 Git 仓库与集群不一致。ArgoCD 的同步策略默认是「以 Git 为准」,但手动改会被覆盖引发意外。我们的方案是把 ArgoCD 设为 syncPolicy: { prune: true, selfHeal: true },明确「Git 是唯一真相源」,并把生产集群的直连权限收到极少数人,强制走 PR。

# ArgoCD Application —— 强制 Git 为唯一真相源
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: server-prod
spec:
  source:
    repoURL: git@repo:ops/config.git
    path: helm/server
    targetRevision: main
  destination:
    server: https://k8s.prod
    namespace: prod
  syncPolicy:
    automated:
      prune: true       # 删除 Git 里已删的资源
      selfHeal: true    # 自动纠偏手动修改
    syncOptions:
      - CreateNamespace=true

难点二:灰度时的指标判定。Canary 分批放量后,靠人盯 Grafana 容易漏。我们接了 Argo Rollouts 的 AnalysisTemplate,自动对比 Canary 与 Stable 的错误率与 P99 延迟,超阈值自动回滚。

# analysis.yaml —— 自动指标判定,失败即回滚
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
  name: success-rate
spec:
  metrics:
    - name: error-rate
      interval: 30s
      successCondition: result[0] < 0.01   # 错误率 < 1%
      failureLimit: 2
      provider:
        prometheus:
          address: http://prometheus:9090
          query: |
            sum(rate(http_errors{rollout="server"}[1m]))
            / sum(rate(http_requests{rollout="server"}[1m]))

难点三:K8s 资源请求没设好导致节点 OOM。早期没设 requests/limits,某服务突发内存把节点拖垮。规范了「所有 Pod 必须设 requests,limits 按 2 倍 requests 设」并用 OPA/Gatekeeper 在 admission 阶段拦截不合规配置。

踩坑复盘

坑 1:镜像 layer 缓存失效。Dockerfile 里 COPY . . 写在 go mod download 之前,导致每次代码变动都重下依赖。调整顺序「先拷 go.mod 下依赖,再拷源码」后构建时间从 4 分钟降到 50 秒。教训:Dockerfile 顺序要把「变化频率低的」放前面。

坑 2:Helm 模板里 {{ }} 与 Go template 冲突。配置里要渲染一个 JSON 的 {{requests}} 被 Helm 当占位符报错。用 {{`{{ }}`}} 转义后正常。教训:Helm 模板里写含大括号的内容必须转义。

坑 3:ArgoCD selfHeal 把热修回滚了。某次线上紧急热修直接改了 ConfigMap,selfHeal 几秒后把它还原引发二次故障。事后规范「热修也必须先提 PR 再同步」,selfHeal 的代价就是不能绕过 Git。教训:GitOps 的纪律一旦建立就不能破坏,否则 selfHeal 会成为定时炸弹。

GitOps 的三道命门:Git 是唯一真相源、灰度靠指标自动判定、镜像靠多阶段瘦身——守不住纪律,工具再先进也会翻车。

项目总结

平台上线后发布从 2 小时降到 8 分钟,回滚从 30 分钟降到 1 分钟,故障发现从「用户投诉」提前到「40 秒告警」,运维同事终于不用半夜登服务器。整个项目最大的认知更新是:自动化不是堆工具,而是用「Git 为源、声明式同步、指标驱动灰度」把纪律固化到流程里,工具只是纪律的执行者。

  • 发布时长:2 小时 → 8 分钟
  • 镜像体积:1.2GB → 78MB
  • 故障告警延迟:用户投诉 → 40 秒
  • 回滚时长:30 分钟 → 1 分钟
返回项目列表