Skip to content
Published on

ArgoCD GitOpsマルチクラスターデプロイ戦略と運用自動化ガイド

Authors
  • Name
    Twitter
ArgoCD GitOps Multi-Cluster Deployment

はじめに

マルチクラスターKubernetes環境を運用する組織が増えるにつれて、一貫したデプロイパイプラインの重要性が高まっています。各クラスターに手動でkubectl applyを実行していた時代は終わりました。GitOpsはGitリポジトリをSingle Source of Truth(唯一の信頼できる情報源)として、宣言的にインフラとアプリケーションの状態を管理する運用パラダイムです。

ArgoCDはCNCF Graduatedプロジェクトであり、KubernetesネイティブGitOpsツールの事実上の標準です。本記事では、ArgoCDを活用したマルチクラスターデプロイのアーキテクチャ設計からApplicationSet、App of Appsパターン、Sync Wave、セキュリティ、障害復旧まで、実運用に必要なすべてを解説します。

ArgoCDアーキテクチャとコアコンポーネント

アーキテクチャ概要

ArgoCDは以下のコアコンポーネントで構成されます:

┌─────────────────────────────────────────────────┐
ArgoCD Server│  ┌──────────┐  ┌───────────┐  ┌──────────────┐  │
│  │ API Server│Repo Server│App Controller││  └──────────┘  └───────────┘  └──────────────┘  │
│  ┌──────────────────┐  ┌─────────────────────┐  │
│  │ ApplicationSet    │  │ Notification         │  │
│  │ Controller        │  │ Controller           │  │
│  └──────────────────┘  └─────────────────────┘  │
└─────────────────────────────────────────────────┘
         │                    │
    ┌────┴────┐          ┌───┴────┐
Git Repo │          │ K8s     (Source) │          │Clusters│
    └─────────┘          └────────┘
  • API Server:UIとCLIのリクエスト処理、認証・認可
  • Repo Server:Gitリポジトリからマニフェストを取得してレンダリング(Helm、Kustomize、Jsonnetなど)
  • Application Controller:実際のクラスター状態とGitの状態を比較して同期
  • ApplicationSet Controller:テンプレートベースで複数のApplicationを自動生成
  • Notification Controller:Sync状態の変化をSlack、Teams、Webhookなどに通知

マルチクラスターにおけるArgoCDデプロイモデル

マルチクラスター環境では、ArgoCD自体をどこにどのようにデプロイするかが最初の設計判断です。

モデル構造メリットデメリット
Hub-Spoke管理クラスターにArgoCD 1つ、ワーカークラスターにデプロイ集中管理、一貫したポリシーSPOF、ネットワーク依存
Standalone各クラスターにArgoCDをインストール独立性、ネットワーク分離管理オーバーヘッド、設定の不一致
HybridHubで共通インフラ、各クラスターでアプリデプロイバランスの取れたアプローチアーキテクチャの複雑さ

ほとんどの組織ではHub-Spokeモデルを採用しています。このモデルでは、管理(Management)クラスターにArgoCDをインストールし、リモートクラスターを登録してデプロイします。

マルチクラスター登録とシークレット管理

クラスター登録

ArgoCD CLIを使用してリモートクラスターを登録します:

# kubeconfigコンテキストの確認
kubectl config get-contexts

# クラスター追加(ArgoCDがServiceAccountを自動作成)
argocd cluster add staging-cluster \
  --name staging \
  --label env=staging \
  --label region=ap-northeast-2

argocd cluster add production-apne2 \
  --name production-apne2 \
  --label env=production \
  --label region=ap-northeast-2

argocd cluster add production-use1 \
  --name production-use1 \
  --label env=production \
  --label region=us-east-1

内部的に、ArgoCDは各クラスター情報をSecretリソースとして保存します。宣言的に管理するには、次のようにSecretを直接作成します:

apiVersion: v1
kind: Secret
metadata:
  name: production-apne2-cluster
  namespace: argocd
  labels:
    argocd.argoproj.io/secret-type: cluster
    env: production
    region: ap-northeast-2
type: Opaque
stringData:
  name: production-apne2
  server: 'https://kubernetes.production-apne2.internal:6443'
  config: |
    {
      "bearerToken": "<service-account-token>",
      "tlsClientConfig": {
        "insecure": false,
        "caData": "<base64-encoded-ca-cert>"
      }
    }

シークレット管理戦略

マルチクラスター環境でシークレットをGitに平文で保存すると、セキュリティインシデントが発生します。以下のツールをArgoCDと組み合わせます:

  • Sealed Secrets:公開鍵で暗号化してGitに保存、クラスターで復号化
  • External Secrets Operator (ESO):AWS Secrets Manager、HashiCorp Vaultなどからシークレットを同期
  • SOPS + age/KMS:ファイル単位の暗号化

ESOをArgoCDと組み合わせるパターンが最も広く使用されています:

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: database-credentials
  namespace: production
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: aws-secrets-manager
    kind: ClusterSecretStore
  target:
    name: database-credentials
    creationPolicy: Owner
  data:
    - secretKey: DB_HOST
      remoteRef:
        key: production/database
        property: host
    - secretKey: DB_PASSWORD
      remoteRef:
        key: production/database
        property: password

ApplicationSet Controllerとジェネレーターパターン

ApplicationSetはマルチクラスターデプロイの中核ツールです。1つのApplicationSetで数十のクラスターに同一のアプリケーションを自動デプロイできます。

Cluster Generator

ArgoCDに登録されたクラスター情報に基づいてApplicationを生成します:

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: prometheus-stack
  namespace: argocd
spec:
  goTemplate: true
  goTemplateOptions: ['missingkey=error']
  generators:
    - clusters:
        selector:
          matchLabels:
            env: production
        values:
          helmReleaseName: kube-prometheus
  template:
    metadata:
      name: 'prometheus-{{.name}}'
    spec:
      project: infrastructure
      source:
        repoURL: 'https://github.com/org/k8s-infrastructure.git'
        targetRevision: main
        path: 'clusters/{{.metadata.labels.region}}/prometheus'
        helm:
          releaseName: '{{.values.helmReleaseName}}'
          valueFiles:
            - 'values.yaml'
            - 'values-{{.metadata.labels.env}}.yaml'
      destination:
        server: '{{.server}}'
        namespace: monitoring
      syncPolicy:
        automated:
          prune: true
          selfHeal: true
        syncOptions:
          - CreateNamespace=true
          - ServerSideApply=true

この設定は、env=productionラベルを持つすべてのクラスターにPrometheusスタックをデプロイします。新しいクラスターを追加して同じラベルを付けると、自動的にApplicationが作成されます。

Matrix Generator - クラスター x サービスの組み合わせ

複数のクラスターに複数のサービスをデプロイする場合、Matrix Generatorが有用です:

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: microservices
  namespace: argocd
spec:
  goTemplate: true
  generators:
    - matrix:
        generators:
          - clusters:
              selector:
                matchLabels:
                  env: production
          - list:
              elements:
                - service: order-service
                  replicas: '3'
                  memory: '512Mi'
                - service: payment-service
                  replicas: '2'
                  memory: '1Gi'
                - service: notification-service
                  replicas: '2'
                  memory: '256Mi'
  template:
    metadata:
      name: '{{.service}}-{{.name}}'
      annotations:
        notifications.argoproj.io/subscribe.on-sync-failed.slack: deploy-alerts
    spec:
      project: applications
      source:
        repoURL: 'https://github.com/org/k8s-apps.git'
        targetRevision: main
        path: 'apps/{{.service}}'
        helm:
          parameters:
            - name: replicaCount
              value: '{{.replicas}}'
            - name: resources.requests.memory
              value: '{{.memory}}'
      destination:
        server: '{{.server}}'
        namespace: '{{.service}}'
      syncPolicy:
        automated:
          prune: true
          selfHeal: true
        syncOptions:
          - CreateNamespace=true

Matrix Generatorは、クラスター3つ x サービス3つ = 9つのApplicationを自動生成します。これがApplicationSetの最も強力な点です。

App of Appsパターン設計

App of Appsパターンは、ArgoCD Applicationを管理するApplicationを作成する階層的アプローチです。GitリポジトリにApplicationマニフェストを保存し、ルートApplicationがそれらを参照します。

ディレクトリ構造

k8s-gitops/
├── root-apps/
│   ├── infrastructure.yaml      # インフラApp of Apps
│   ├── platform.yaml            # プラットフォームApp of Apps
│   └── applications.yaml        # ビジネスアプリApp of Apps
├── infrastructure/
│   ├── cert-manager.yaml
│   ├── external-secrets.yaml
│   ├── ingress-nginx.yaml
│   └── prometheus-stack.yaml
├── platform/
│   ├── istio.yaml
│   ├── kafka.yaml
│   └── redis.yaml
└── applications/
    ├── order-service.yaml
    ├── payment-service.yaml
    └── notification-service.yaml

ルートApplication設定

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: root-infrastructure
  namespace: argocd
  annotations:
    argocd.argoproj.io/sync-wave: '-2'
spec:
  project: default
  source:
    repoURL: 'https://github.com/org/k8s-gitops.git'
    targetRevision: main
    path: infrastructure
  destination:
    server: https://kubernetes.default.svc
    namespace: argocd
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
---
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: root-platform
  namespace: argocd
  annotations:
    argocd.argoproj.io/sync-wave: '-1'
spec:
  project: default
  source:
    repoURL: 'https://github.com/org/k8s-gitops.git'
    targetRevision: main
    path: platform
  destination:
    server: https://kubernetes.default.svc
    namespace: argocd
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
---
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: root-applications
  namespace: argocd
  annotations:
    argocd.argoproj.io/sync-wave: '0'
spec:
  project: default
  source:
    repoURL: 'https://github.com/org/k8s-gitops.git'
    targetRevision: main
    path: applications
  destination:
    server: https://kubernetes.default.svc
    namespace: argocd
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

Sync WaveとHookによるデプロイ順序制御

Sync Waveの基本概念

Sync Waveはargocd.argoproj.io/sync-waveアノテーションでリソースのデプロイ順序を定義します。小さい数値から順にデプロイされ、同じWave内では並列処理されます。

# Wave -1: NamespaceとRBACを先に作成
apiVersion: v1
kind: Namespace
metadata:
  name: payment-service
  annotations:
    argocd.argoproj.io/sync-wave: '-1'
---
# Wave 0: ConfigMapとSecret
apiVersion: v1
kind: ConfigMap
metadata:
  name: payment-config
  namespace: payment-service
  annotations:
    argocd.argoproj.io/sync-wave: '0'
data:
  DATABASE_URL: 'postgresql://db.internal:5432/payments'
  KAFKA_BROKERS: 'kafka-0.kafka:9092'
---
# Wave 1: メインDeployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: payment-service
  namespace: payment-service
  annotations:
    argocd.argoproj.io/sync-wave: '1'
spec:
  replicas: 3
  selector:
    matchLabels:
      app: payment-service
  template:
    metadata:
      labels:
        app: payment-service
    spec:
      containers:
        - name: payment
          image: registry.internal/payment-service:v2.3.1
          envFrom:
            - configMapRef:
                name: payment-config
---
# Wave 2: Serviceの公開
apiVersion: v1
kind: Service
metadata:
  name: payment-service
  namespace: payment-service
  annotations:
    argocd.argoproj.io/sync-wave: '2'
spec:
  selector:
    app: payment-service
  ports:
    - port: 8080
      targetPort: 8080

Sync Hookの活用

Hookは、Syncライフサイクルの特定のタイミングで実行されるリソース(主にJob)です:

# PreSync Hook: デプロイ前のデータベースマイグレーション
apiVersion: batch/v1
kind: Job
metadata:
  name: db-migration
  namespace: payment-service
  annotations:
    argocd.argoproj.io/hook: PreSync
    argocd.argoproj.io/hook-delete-policy: BeforeHookCreation
    argocd.argoproj.io/sync-wave: '-1'
spec:
  template:
    spec:
      containers:
        - name: migrate
          image: registry.internal/payment-service:v2.3.1
          command: ['python', 'manage.py', 'migrate']
          envFrom:
            - secretRef:
                name: database-credentials
      restartPolicy: Never
  backoffLimit: 3
---
# PostSync Hook: デプロイ後のスモークテスト
apiVersion: batch/v1
kind: Job
metadata:
  name: smoke-test
  namespace: payment-service
  annotations:
    argocd.argoproj.io/hook: PostSync
    argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
  template:
    spec:
      containers:
        - name: smoke-test
          image: registry.internal/smoke-tester:latest
          command: ['./run-tests.sh']
          env:
            - name: TARGET_URL
              value: 'http://payment-service.payment-service:8080'
            - name: TEST_SUITE
              value: 'smoke'
      restartPolicy: Never
  backoffLimit: 1

Hookの種類と実行タイミング:

Hook実行タイミング使用例
PreSyncマニフェスト同期前DBマイグレーション、バックアップ
Syncマニフェスト同期と同時特殊な順序でのデプロイ
PostSyncすべてのリソースがHealthy後スモークテスト、通知
SyncFailSync失敗時ロールバック、アラート通知
Skip同期から除外手動管理リソース

ArgoCD vs Flux vs Jenkins GitOps 比較

項目ArgoCDFlux v2Jenkins + GitOps
CNCFステータスGraduatedGraduated該当なし
UIダッシュボード内蔵(豊富)Weave GitOps(別途)Jenkins内蔵
マルチクラスターHub-Spokeネイティブクラスター毎インストール推奨別途構成が必要
ApplicationSetネイティブ対応Kustomizationで類似未対応
HelmサポートネイティブHelmRelease CRDプラグイン
イメージ自動更新Image Updater(別途)ネイティブ対応パイプライントリガー
RBACプロジェクトベースの細分化Kubernetes RBACJenkins独自のRBAC
Sync Wave/Hookネイティブ対応Health Checkベースパイプラインステージ
通知Notification ControllerAlert Providerプラグイン
学習コスト中程度中~高高(Groovyが必要)
コミュニティ規模非常に活発(17k+ Stars)活発(6k+ Stars)非常に活発

ArgoCDはUIの直感性、ApplicationSetの強力なテンプレート機能、そしてHub-Spokeマルチクラスター対応で優位性を示しています。Fluxはイメージの自動更新とKubernetesネイティブな設計思想が強みです。

障害シナリオと復旧

シナリオ1:Syncがスタック状態になる

症状:ApplicationがSyncing状態で止まり、進行しない

# Applicationの状態確認
argocd app get payment-service --show-operation

# Sync操作の強制終了
argocd app terminate-op payment-service

# Syncのリトライ
argocd app sync payment-service --retry-limit 3 --retry-backoff-duration 10s

原因:PreSync Hook Jobの失敗、またはリソースがHealthy状態に到達できない場合

シナリオ2:Gitリポジトリにアクセスできない

症状:すべてのApplicationがUnknown状態で表示される

# Repo Serverのログ確認
kubectl logs -n argocd deployment/argocd-repo-server --tail=100

# Git接続テスト
argocd repo list

# リポジトリの再登録
argocd repo add https://github.com/org/k8s-gitops.git \
  --username deploy-bot \
  --password "$GITHUB_TOKEN"

復旧:ArgoCDは最後に成功したマニフェストをキャッシュしているため、Gitが一時的にアクセスできなくても既存のデプロイ状態は維持されます。ただし、新しいデプロイは実行できません。

シナリオ3:リモートクラスターとの接続切断

# クラスターの状態確認
argocd cluster list

# 特定クラスターのすべてのアプリ状態
argocd app list --dest-server https://kubernetes.production-apne2.internal:6443

# クラスター認証情報の更新
argocd cluster set production-apne2 \
  --server https://kubernetes.production-apne2.internal:6443 \
  --bearer-token "new-token-here"

シナリオ4:誤ったデプロイのロールバック

# Applicationの履歴確認
argocd app history payment-service

# 特定リビジョンへのロールバック
argocd app rollback payment-service 42

# またはGit revert後に自動Sync
git revert HEAD
git push origin main

GitOpsで推奨されるロールバック方法は、argocd app rollbackよりもGit revertです。Gitが真実の情報源であるため、ロールバックの記録はGit履歴に残すべきです。

運用自動化のベストプラクティス

通知設定

apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-notifications-cm
  namespace: argocd
data:
  trigger.on-sync-failed: |
    - description: Application sync has failed
      send:
        - slack-deploy-alert
      when: app.status.operationState.phase in ['Error', 'Failed']
  trigger.on-health-degraded: |
    - description: Application health has degraded
      send:
        - slack-deploy-alert
      when: app.status.health.status == 'Degraded'
  template.slack-deploy-alert: |
    message: |
      Application *{{.app.metadata.name}}* sync {{.app.status.operationState.phase}}.
      Revision: {{.app.status.sync.revision}}
      Cluster: {{.app.spec.destination.server}}
    slack:
      attachments: |
        [{
          "color": "#E96D76",
          "title": "{{.app.metadata.name}}",
          "title_link": "https://argocd.internal/applications/{{.app.metadata.name}}",
          "fields": [
            {"title": "Sync Status", "value": "{{.app.status.sync.status}}", "short": true},
            {"title": "Health", "value": "{{.app.status.health.status}}", "short": true}
          ]
        }]
  service.slack: |
    token: $slack-token
    username: ArgoCD
    icon: ":argocd:"

RBACによるプロジェクト分離

# argocd-rbac-cm ConfigMapのpolicy.csv
p, role:platform-team, applications, *, infrastructure/*, allow
p, role:platform-team, clusters, get, *, allow

p, role:dev-team-order, applications, get, applications/order-*, allow
p, role:dev-team-order, applications, sync, applications/order-*, allow
p, role:dev-team-order, applications, action/*, applications/order-*, allow

p, role:dev-team-payment, applications, get, applications/payment-*, allow
p, role:dev-team-payment, applications, sync, applications/payment-*, allow

g, platform-admins, role:platform-team
g, order-team, role:dev-team-order
g, payment-team, role:dev-team-payment

おわりに

ArgoCDを活用したマルチクラスターGitOpsは、単にツールをインストールすることではなく、組織のデプロイ文化を変えることです。ApplicationSetでクラスターを動的に管理し、App of Appsパターンで階層的な構造を作り、Sync WaveとHookでデプロイ順序を制御します。そして、すべての変更はGit PRを通じてレビューされ、自動的に同期されます。

最も重要な原則はGitが真実の情報源であるということです。緊急時にkubectl editで直接修正したくなる誘惑がありますが、ArgoCDのselfHeal機能がそれを元の状態に戻します。Gitを通じた変更のみが永続的であり、追跡可能であり、ロールバック可能です。

参考資料

  1. ArgoCD公式ドキュメント - Declarative Setup
  2. ArgoCD ApplicationSet - Generators
  3. ArgoCD Sync Waves and Hooks
  4. Codefresh - ArgoCD ApplicationSet Multi-Cluster Deployment
  5. Red Hat - How to Automate Multi-Cluster Deployments Using Argo CD
  6. ArgoCD Notifications Documentation
  7. DigitalOcean - Manage Multi-Cluster Deployments with ArgoCD