GitOps 部署实践: ArgoCD 与 Flux

深入理解 GitOps 理念,学习使用 ArgoCD 和 Flux 实现声明式部署,以及构建可靠的 GitOps 工作流。

概述

GitOps 是一种以 Git 为唯一信任源的运维理念和实践模式。通过将基础设施和应用声明式地存储在 Git 仓库中,实现自动化、可审计、可回滚的部署体验。本文将深入探讨:

学习目标

  • 理解 GitOps 核心理念与优势
  • 掌握 ArgoCD 的安装与使用
  • 学会 Flux 的集群同步配置
  • 构建多环境 GitOps 工作流
  • 实现密钥管理与安全加固

GitOps 核心理念

什么是 GitOps?

┌─────────────────────────────────────────────────────────────────┐
│                    GitOps 理念                                   │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   GitOps = Git + Operations                                     │
│                                                                 │
│   核心原则:                                                    │
│   ✓ 声明式配置(Everything as Code)                           │
│   ✓ Git 是唯一事实来源(Single Source of Truth)               │
│   ✓ 自动化同步(Automated Synchronization)                    │
│   ✓ 拉取式部署(Pull-based Deployment)                        │
│                                                                 │
│   ┌─────────────────────────────────────────────────────────┐   │
│   │                                                          │   │
│   │   ┌─────────┐      ┌─────────┐      ┌─────────┐        │   │
│   │   │   Git   │ ────▶│  CD     │ ────▶│ Cluster │        │   │
│   │   │   Repo  │      │  Tool   │      │         │        │   │
│   │   │         │      │         │      │         │        │   │
│   │   │ YAML/   │      │ ArgoCD  │      │ K8s     │        │   │
│   │   │ Helm/   │      │  Flux   │      │ Deploy  │        │   │
│   │   │ Kustom  │      │         │      │         │        │   │
│   │   └─────────┘      └─────────┘      └─────────┘        │   │
│   │       │                                      │            │   │
│   │       │                                      │            │   │
│   │       ◀────────────────────────────────────────            │   │
│   │              自动同步(观察实际状态 vs 期望状态)           │   │
│   │                                                          │   │
│   └─────────────────────────────────────────────────────────┘   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

GitOps vs 传统 CD

┌─────────────────────────────────────────────────────────────────┐
│                    GitOps vs 传统 CI/CD                         │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  传统 CI/CD(Push-based)                                      │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  Code → CI → Build → Push → kubectl apply               │   │
│  │                                          │               │   │
│  │                                          ▼               │   │
│  │                                   ┌─────────┐            │   │
│  │                                   │ Cluster │            │   │
│  │                                   └─────────┘            │   │
│  └─────────────────────────────────────────────────────────┘   │
│  问题:                                                         │
│  - 凭证需要推送到 CI 系统                                       │
│  - 难以审计和回滚                                              │
│  - 配置漂移难以检测                                            │
│                                                                 │
│  GitOps(Pull-based)                                          │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │                                                          │   │
│  │      ┌─────────┐          ┌─────────┐         ┌─────┐ │   │
│  │      │   Git   │◀─────────│   CD    │         │     │ │   │
│  │      │   Repo  │          │  Agent  │◀────────│ K8s │ │   │
│  │      └─────────┘          └────┬────┘         └──┬──┘ │   │
│  │                               │                 │     │   │
│  │                               ▼                 ▼     │   │
│  │                          Watch Git        Deploy     │   │
│  │                                                          │   │
│  └─────────────────────────────────────────────────────────┘   │
│  优点:                                                         │
│  - 无需集群凭证在 CI 系统                                       │
│  - 自动检测和修复配置漂移                                       │
│  - 完整的审计日志(git commit)                                 │
│  - 一键回滚(git revert)                                       │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

ArgoCD

ArgoCD 简介

┌─────────────────────────────────────────────────────────────────┐
│                    ArgoCD 核心概念                              │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   ArgoCD 是声明式的 GitOps 持续交付 工具                        │
│                                                                 │
│   特性:                                                        │
│   ✓ 声明式应用定义(Application CRD)                           │
│   ✓ 自动部署到目标集群                                         │
│   ✓ 实时状态同步                                                │
│   ✓ Web UI 和 CLI                                              │
│   ✓ 多环境/多集群支持                                          │
│   ✓ RBAC 权限控制                                              │
│   ✓ 可观测性(指标、日志、事件)                                │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

安装 ArgoCD

# 方式1:kubectl 安装
kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/v2.10.0/manifests/install.yaml

# 方式2:HA 安装
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/v2.10.0/manifests/ha/install.yaml

# 方式3:Helm 安装
helm repo add argo https://argoproj.github.io/argo-helm
helm install argocd argo/argo-cd -n argocd --create-namespace

# 访问 ArgoCD Server
# 方式1:LoadBalancer
kubectl patch svc argocd-server -n argocd -p '{"spec":{"type":"LoadBalancer"}}'

# 方式2:NodePort
kubectl patch svc argocd-server -n argocd -p '{"spec":{"type":"NodePort"}}'

# 方式3:Ingress
kubectl apply -f ingress.yaml

# 获取初始密码
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d

Application CRD

# application.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: myapp
  namespace: argocd
spec:
  project: default

  source:
    repoURL: https://github.com/myorg/myapp.git
    targetRevision: main
    path: deploy/k8s          # Chart 或 K8s YAML 目录
    helm:
      valueFiles:
      - values-prod.yaml
      parameters:
      - name: image.tag
        value: v1.2.0

  destination:
    server: https://kubernetes.default.svc
    namespace: production

  syncPolicy:
    automated:
      prune: true              # 自动删除清理
      selfHeal: true           # 自动修复漂移
    syncOptions:
    - CreateNamespace=true
    retry:
      limit: 5
      backoff:
        duration: 5s
        factor: 2
        maxDuration: 3m

多集群部署

# app-of-apps.yaml - 应用集
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: appset
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/myorg/infra.git
    path: apps
  destination:
    server: https://kubernetes.default.svc
    namespace: argocd
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

---
# clusters/staging.yaml
- name: staging
  server: https://staging.example.com
  namespaces:
  - app
  - monitoring

---
# clusters/production.yaml
- name: production
  server: https://production.example.com
  namespaces:
  - app
  - monitoring

ArgoCD 模板

# application-template.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: {{ name }}
  namespace: argocd
  finalizers:
  - resources-finalizer.argocd.argoproj.io
spec:
  project: default
  source:
    repoURL: {{ repoURL }}
    targetRevision: {{ branch | default "main" }}
    path: {{ path }}
    helm:
      valueFiles:
      {{- range .values }}
      - {{ . }}
      {{- end }}
  destination:
    server: {{ server }}
    namespace: {{ namespace }}
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

Flux

Flux 简介

┌─────────────────────────────────────────────────────────────────┐
│                    Flux 工作原理                                │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   Flux v2 组件:                                                │
│                                                                 │
│   ┌─────────────┐  ┌─────────────┐  ┌─────────────┐           │
│   │  Source     │  │   Kustomize │  │  Helm      │           │
│   │  Controller │  │   Controller│  │  Controller │           │
│   ├─────────────┤  ├─────────────┤  ├─────────────┤           │
│   │  GitRepo    │  │  Kustomize │  │  HelmRelease│           │
│   │  HelmRepo   │  │  Overlay   │  │             │           │
│   │  Bucket     │  │             │  │             │           │
│   └─────────────┘  └─────────────┘  └─────────────┘           │
│         │                  │                  │                │
│         ▼                  ▼                  ▼                │
│   ┌─────────────────────────────────────────────────────────┐ │
│   │                   Reconciliation                         │ │
│   │                                                          │ │
│   │   检测 Git 变化 → 应用 Kustomize/Helm → 部署到集群       │ │
│   └─────────────────────────────────────────────────────────┘ │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

安装 Flux

# 安装 Flux CLI
curl -s https://fluxcd.io/install.sh | bash

# 验证安装
flux --version

# 引导安装(创建所有 CRD 和控制器)
flux bootstrap github \
  --owner=myorg \
  --repository=flux-infra \
  --branch=main \
  --path=./clusters/production \
  --token-auth

# 或 bootstrap gitlab
flux bootstrap gitlab \
  --owner=myorg \
  --repository=flux-infra \
  --path=./clusters/production

# 检查状态
flux get all -n default

GitRepository

# gitrepository.yaml
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: GitRepository
metadata:
  name: myapp
  namespace: flux-system
spec:
  interval: 1m
  url: https://github.com/myorg/myapp.git
  ref:
    branch: main
    # 或使用 tag
    # tag: v1.0.0
    # semver: ">=1.0.0"
  secretRef:
    name: git-credentials
  ignore: |
    # 忽略特定文件/目录
    .git/
    *.md
---
# 创建 credentials
apiVersion: v1
kind: Secret
metadata:
  name: git-credentials
  namespace: flux-system
type: Opaque
stringData:
  username: git
  password: <token>

Kustomization

# kustomization.yaml
apiVersion: kustomize.toolkit.fluxcd.io/v1beta2
kind: Kustomization
metadata:
  name: myapp
  namespace: flux-system
spec:
  interval: 5m                    # 同步间隔
  path: ./deploy/production      # Kustomize 路径
  prune: true                     # 删除清理
  wait: true                      # 等待资源就绪
  sourceRef:
    kind: GitRepository
    name: myapp
    namespace: flux-system
  targetNamespace: production    # 部署到目标命名空间
  postBuild:
    substitute:
      IMAGE_TAG: v1.2.0
      ENVIRONMENT: production
  dependsOn:
  - name: common-config           # 依赖其他 Kustomization

HelmRelease

# helmrelease.yaml
apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
  name: myapp
  namespace: production
spec:
  interval: 5m
  chart:
    spec:
      chart: myapp
      version: "1.x"
      sourceRef:
        kind: HelmRepository
        name: myapp-charts
        namespace: flux-system
  values:
    image:
      repository: myregistry/myapp
      tag: v1.2.0
    replicaCount: 3
    resources:
      limits:
        cpu: 1000m
        memory: 1Gi
  # 覆盖 values
  postRenderers:
  - kustomize:
      patches:
      - target:
          kind: Deployment
          name: myapp
        patch: |
          - op: replace
            path: /spec/replicas
            value: 5

多租户隔离

# tenants.yaml - 多租户 Flux 配置
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: GitRepository
metadata:
  name: app1
  namespace: app1-namespace
  annotations:
    toolkit.fluxcd.io/tenant: app1
spec:
  url: https://github.com/app1/deploy.git
  # ...

---
# RBAC 配置
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: flux-app1-reconciler
  namespace: app1-namespace
rules:
- apiGroups: ["*"]
  resources: ["*"]
  verbs: ["*"]

多环境管理

目录结构

repository/
├── clusters/                    # 集群配置
│   ├── production/
│   │   ├── kustomization.yaml
│   │   └── apps.yaml
│   └── staging/
│       ├── kustomization.yaml
│       └── apps.yaml
├── apps/                        # 应用配置
│   ├── myapp/
│   │   ├── base/
│   │   │   ├── kustomization.yaml
│   │   │   ├── deployment.yaml
│   │   │   └── service.yaml
│   │   ├── overlays/
│   │   │   ├── staging/
│   │   │   │   ├── kustomization.yaml
│   │   │   │   └── replica-count.yaml
│   │   │   └── production/
│   │   │       ├── kustomization.yaml
│   │   │       └── replica-count.yaml
│   │   └── releases/
│   │       ├── v1.0.yaml
│   │       └── v1.1.yaml
├── infrastructure/              # 基础设施
│   ├── cert-manager/
│   ├── ingress-nginx/
│   └── monitoring/
└── README.md

Kustomize 多环境

# apps/myapp/overlays/production/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
- ../../base

patches:
- path: replica-count.yaml

images:
- name: myapp
  newName: myregistry/myapp
  newTag: v1.2.0-prod

configMapGenerator:
- name: app-config
  behavior: merge
  literals:
  - ENVIRONMENT=production
  - LOG_LEVEL=warn

replicas:
- name: myapp
  count: 5

环境晋升流程

┌─────────────────────────────────────────────────────────────────┐
│                    环境晋升流程                                  │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   ┌─────────┐    PR    ┌─────────┐    PR    ┌─────────┐       │
│   │ develop │ ──────▶ │ staging │ ──────▶ │production│       │
│   │         │          │         │          │         │        │
│   │ 开发环境 │          │ 预发环境 │          │ 生产环境 │        │
│   │ 自动部署 │          │ 自动部署 │          │ 手动部署 │        │
│   └─────────┘          └─────────┘          └─────────┘       │
│       │                    │                    │               │
│       ▼                    ▼                    ▼               │
│   feature/*           develop           release/*             │
│   分支开发              分支              标签/版本            │
│                                                                 │
│   Promotion 策略:                                              │
│   - develop: 任何合并自动部署                                   │
│   - staging: main 分支自动部署                                  │
│   - production: 需要手动审批                                    │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

应用集管理

# root-app.yaml - 根应用(管理所有应用)
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: root-app
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/myorg/infra.git
    path: apps
  destination:
    server: https://kubernetes.default.svc
    namespace: argocd
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

密钥管理

Sealed Secrets

# 安装 Sealed Secrets
helm repo add sealed-secrets https://bitnami-labs.github.io/sealed-secrets
helm install sealed-secrets sealed-secrets/sealed-secrets -n kube-system

# 加密 Secret
kubectl create secret generic db-credentials \
  --dry-run=client \
  -o yaml | \
kubeseal --cert pub-cert.pem --format yaml > sealed-secret.yaml

# 解密由集群自动完成

SOPS + Age

# 安装 age 和 SOPS
brew install age sops

# 生成密钥对
age-keygen -o key.txt

# 创建 .sops.yaml
cat > .sops.yaml << EOF
creation_rules:
  - age: <public-key>
    namespaces:
    - production
    - staging
    path_regex: .*.yaml$
EOF

# 加密文件
sops --encrypt secret.yaml > secret.enc.yaml

# GitOps 仓库只存储加密文件

External Secrets Operator

# external-secrets.yaml
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: db-credentials
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: vault-backend
    kind: ClusterSecretStore
  target:
    name: db-credentials
    creationPolicy: Owner
  data:
  - secretKey: password
    remoteRef:
      key: secret/data/db
      property: password

CI/CD 集成

GitHub Actions

# .github/workflows/deploy.yml
name: Deploy

on:
  push:
    branches: [main]
  workflow_dispatch:

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4

    - name: Configure kubectl
      run: |
        echo "${{ secrets.KUBE_CONFIG }}" | base64 -d > kubeconfig

    - name: Trigger ArgoCD Sync
      uses: octo-cli/argocd@v1
      with:
        args: >
          app sync myapp
          --auth-token ${{ secrets.ARGOCD_TOKEN }}
          --server ${{ secrets.ARGOCD_SERVER }}

Flux Image Updates

# image-policy.yaml
apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImagePolicy
metadata:
  name: myapp
  namespace: production
spec:
  imageRepositoryRef:
    name: myapp
  policy:
    semver:
      range: ">=1.0.0"
    # 或使用 alphabetical
    # alphabetical:
    #   order: asc
---
# ImageRepository
apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImageRepository
metadata:
  name: myapp
  namespace: production
spec:
  image: myregistry/myapp
  interval: 1m
  source: dockerhub

自动镜像更新

# 启用 Image Update Automation
apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImageUpdateAutomation
metadata:
  name: update-myapp
  namespace: production
spec:
  git:
    checkout:
      ref:
        branch: main
    commit:
      author:
        email: flux@example.com
        name: flux
    push:
      branch: main
  update:
    strategy: Setters
    path: ./deploy
  sourceRef:
    kind: GitRepository
    name: myapp

最佳实践

仓库结构

.gitops/
├── apps/
│   ├── frontend/
│   │   ├── base/
│   │   │   ├── kustomization.yaml
│   │   │   └── templates/
│   │   └── overlays/
│   │       ├── staging/
│   │       └── production/
│   ├── backend/
│   └── worker/
├── infra/
│   ├── cert-manager/
│   ├── ingress-nginx/
│   ├── monitoring/
│   └── secrets/
└── clusters/
    ├── staging/
    │   └── flux-system/
    └── production/
        └── flux-system/

团队协作

┌─────────────────────────────────────────────────────────────────┐
│                    GitOps 协作模式                              │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   应用团队(App Teams)                                         │
│   ├── 修改 apps/myapp/overlays/*/kustomization.yaml            │
│   ├── 创建 Pull Request                                         │
│   └── 审查后合并到 main                                         │
│                                                                 │
│   基础设施团队(Platform Team)                                 │
│   ├── 管理 clusters/ 目录                                       │
│   ├── 配置 ArgoCD/Flux 全局设置                                 │
│   └── 审批敏感配置变更                                          │
│                                                                 │
│   安全团队(Security Team)                                     │
│   ├── 管理 secrets/ 目录                                        │
│   ├── 审计配置变更                                             │
│   └── 处理安全漏洞                                              │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

审查流程

# 强制代码审查
# .github/workflows/review.yml
name: Require Review

on:
  pull_request:
    branches: [main]

jobs:
  require-review:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4

    - name: Check approvals
      run: |
        count=$(gh pr view ${{ github.event.pull_request.number }} --json reviews --jq '.reviews | length')
        if [ "$count" -lt 1 ]; then
          echo "At least 1 approval required"
          exit 1
        fi

常见问题与避坑指南

Q1:同步失败?

# 排查步骤
# 1. 查看 ArgoCD 应用状态
argocd app get myapp

# 2. 查看同步差异
argocd app diff myapp

# 3. 查看日志
argocd app logs myapp

# 4. 强制同步
argocd app sync myapp --force

# Flux 日志
flux logs -n flux-system
kubectl logs -n flux-system -l app=source-controller

Q2:回滚问题?

# ArgoCD 回滚
argocd app rollback myapp

# 指定版本
argocd app sync myapp --revision v1.0.0

# Flux 回滚
# 修改 Git 中的版本即可,Flux 会自动同步
git revert <commit>
git push

Q3:多集群配置?

# ArgoCD 多集群
# 1. 注册集群到 ArgoCD
argocd cluster add staging --kubeconfig staging.kubeconfig

# 2. 应用部署到不同集群
spec:
  destination:
    server: https://staging.example.com
    namespace: production

Q4:配置漂移?

# ArgoCD 自动修复
spec:
  syncPolicy:
    automated:
      selfHeal: true    # 自动修复漂移

# 查看漂移
argocd app get myapp --show-diffs

# 强制同步(覆盖集群状态)
argocd app sync myapp --force

总结

┌─────────────────────────────────────────────────────────────────┐
│                    核心要点回顾                                  │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  GitOps 理念                                                   │
│  ├── Git 是唯一事实来源                                         │
│  ├── 声明式配置                                                │
│  └── 拉取式部署                                                │
│                                                                 │
│  ArgoCD                                                        │
│  ├── Application CRD 定义应用                                   │
│  ├── Web UI 和 CLI                                             │
│  ├── 多集群支持                                                │
│  └── 自动化同步和回滚                                           │
│                                                                 │
│  Flux                                                          │
│  ├── Source/Kustomization/Helm Controller                     │
│  ├── GitRepository 自动同步                                     │
│  └── Image Update Automation                                   │
│                                                                 │
│  多环境管理                                                    │
│  ├── Kustomize overlays                                        │
│  ├── 渐进式环境晋升                                             │
│  └── 权限隔离                                                  │
│                                                                 │
│  密钥管理                                                       │
│  ├── Sealed Secrets                                            │
│  ├── SOPS + Age                                               │
│  └── External Secrets Operator                                 │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

思考题

  1. GitOps 和传统 Push 方式的 CI/CD 相比,各自的适用场景是什么?
  2. 如何处理多租户场景下的权限隔离?
  3. 在 GitOps 模式下,如何保证敏感信息的安全?

引用与参考

  1. ArgoCD Documentation
  2. Flux Documentation
  3. GitOps Principles

下篇预告

下一篇文章我们将探讨 可观测性体系,包括:

  • Prometheus + Grafana 监控
  • 日志收集(ELK/Loki)
  • 分布式追踪(Jaeger)
  • AlertManager 告警

敬请期待!