Skip to content
Published on

ArgoCD + Argo Rolloutsで実現するGitOpsデプロイ戦略 — Blue-Green、Canary完全ガイド

Authors
  • Name
    Twitter
ArgoCD GitOps Deployment

はじめに

GitOpsは、GitをSingle Source of Truthとして使用し、インフラストラクチャとアプリケーションの望ましい状態を宣言的に管理する運用パラダイムです。ArgoCDはKubernetes環境でGitOpsを実現する最も広く使われているツールであり、Argo Rolloutsはそこに段階的デプロイ戦略を追加します。

本記事では、ArgoCD + Argo Rolloutsの組み合わせでBlue-GreenとCanaryデプロイを実装する方法を、実践コードとともに解説します。

GitOpsの基本原則

GitOpsの4つの原則:

  1. 宣言的(Declarative):システムの望ましい状態を宣言的に記述
  2. バージョン管理(Versioned):すべての状態がGitに保存されバージョン管理
  3. 自動適用(Automated):承認された変更が自動的にシステムに適用
  4. 継続的調整(Continuously Reconciled):エージェントが実際の状態と望ましい状態を継続的に比較・調整

ArgoCD基本構成

Applicationリソース

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: my-app
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/myorg/k8s-manifests.git
    targetRevision: main
    path: apps/my-app/overlays/production
  destination:
    server: https://kubernetes.default.svc
    namespace: production
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
      allowEmpty: false
    syncOptions:
      - CreateNamespace=true
      - PrunePropagationPolicy=foreground
    retry:
      limit: 5
      backoff:
        duration: 5s
        factor: 2
        maxDuration: 3m

ApplicationSetによるマルチクラスター管理

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: my-app-set
  namespace: argocd
spec:
  generators:
    - clusters:
        selector:
          matchLabels:
            env: production
  template:
    metadata:
      name: 'my-app-{{name}}'
    spec:
      project: default
      source:
        repoURL: https://github.com/myorg/k8s-manifests.git
        targetRevision: main
        path: 'apps/my-app/overlays/{{metadata.labels.region}}'
      destination:
        server: '{{server}}'
        namespace: production

Argo Rolloutsのインストール

# Argo Rollouts Controllerのインストール
kubectl create namespace argo-rollouts
kubectl apply -n argo-rollouts -f https://github.com/argoproj/argo-rollouts/releases/latest/download/install.yaml

# kubectlプラグインのインストール
brew install argoproj/tap/kubectl-argo-rollouts

# ダッシュボードの起動
kubectl argo rollouts dashboard -n argo-rollouts

Blue-Greenデプロイ

コンセプト

Blue-Greenデプロイフロー:

[ユーザー] --> [Active Service] --> [Blue (v1)] <-- 現在のプロダクション
                                    [Green (v2)] <-- 新バージョン待機

切り替え後:
[ユーザー] --> [Active Service] --> [Green (v2)] <-- 新プロダクション
                                    [Blue (v1)] <-- 旧バージョン(ロールバック待機)

Rolloutリソース

apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: my-app
  namespace: production
spec:
  replicas: 5
  revisionHistoryLimit: 3
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
        - name: my-app
          image: myorg/my-app:v2.0.0
          ports:
            - containerPort: 8080
          readinessProbe:
            httpGet:
              path: /health
              port: 8080
            initialDelaySeconds: 10
            periodSeconds: 5
          resources:
            requests:
              cpu: 200m
              memory: 256Mi
            limits:
              cpu: 500m
              memory: 512Mi
  strategy:
    blueGreen:
      activeService: my-app-active
      previewService: my-app-preview
      autoPromotionEnabled: false
      scaleDownDelaySeconds: 300
      prePromotionAnalysis:
        templates:
          - templateName: smoke-test
        args:
          - name: service-name
            value: my-app-preview
      postPromotionAnalysis:
        templates:
          - templateName: success-rate
        args:
          - name: service-name
            value: my-app-active
---
apiVersion: v1
kind: Service
metadata:
  name: my-app-active
spec:
  selector:
    app: my-app
  ports:
    - port: 80
      targetPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: my-app-preview
spec:
  selector:
    app: my-app
  ports:
    - port: 80
      targetPort: 8080

デプロイ管理コマンド

# デプロイ状態の確認
kubectl argo rollouts get rollout my-app -n production -w

# 手動プロモーション(Blue → Green切り替え)
kubectl argo rollouts promote my-app -n production

# ロールバック
kubectl argo rollouts undo my-app -n production

# 特定リビジョンへのロールバック
kubectl argo rollouts undo my-app --to-revision=2 -n production

# デプロイの中断
kubectl argo rollouts abort my-app -n production

Canaryデプロイ

段階的Canary戦略

apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: my-app-canary
spec:
  replicas: 10
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
        - name: my-app
          image: myorg/my-app:v2.0.0
          ports:
            - containerPort: 8080
  strategy:
    canary:
      canaryService: my-app-canary
      stableService: my-app-stable
      steps:
        # 5%のトラフィックで開始
        - setWeight: 5
        - pause:
            duration: 5m
        # 自動分析の実行
        - analysis:
            templates:
              - templateName: error-rate-check
            args:
              - name: service-name
                value: my-app-canary
        # 20%に拡大
        - setWeight: 20
        - pause:
            duration: 10m
        - analysis:
            templates:
              - templateName: latency-check
        # 50%に拡大
        - setWeight: 50
        - pause:
            duration: 10m
        - analysis:
            templates:
              - templateName: comprehensive-check
        # 80%に拡大
        - setWeight: 80
        - pause:
            duration: 5m
        # 100%(プロモーション完了)
      maxSurge: '25%'
      maxUnavailable: 0

Istio連携によるトラフィック管理

apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: my-app-istio
spec:
  replicas: 10
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
        - name: my-app
          image: myorg/my-app:v2.0.0
  strategy:
    canary:
      canaryService: my-app-canary
      stableService: my-app-stable
      trafficRouting:
        istio:
          virtualServices:
            - name: my-app-vsvc
              routes:
                - primary
          destinationRule:
            name: my-app-destrule
            canarySubsetName: canary
            stableSubsetName: stable
      steps:
        - setWeight: 10
        - pause: { duration: 5m }
        - setWeight: 30
        - pause: { duration: 5m }
        - setWeight: 60
        - pause: { duration: 5m }
---
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: my-app-vsvc
spec:
  hosts:
    - my-app.example.com
  http:
    - name: primary
      route:
        - destination:
            host: my-app-stable
          weight: 100
        - destination:
            host: my-app-canary
          weight: 0
---
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: my-app-destrule
spec:
  host: my-app
  subsets:
    - name: stable
      labels:
        app: my-app
    - name: canary
      labels:
        app: my-app

AnalysisTemplate — 自動分析

Prometheusベースの分析

apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
  name: error-rate-check
spec:
  args:
    - name: service-name
  metrics:
    - name: error-rate
      interval: 60s
      count: 5
      successCondition: result[0] < 0.05
      failureLimit: 3
      provider:
        prometheus:
          address: http://prometheus.monitoring:9090
          query: |
            sum(rate(http_requests_total{service="{{args.service-name}}",status=~"5.."}[5m]))
            /
            sum(rate(http_requests_total{service="{{args.service-name}}"}[5m]))
---
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
  name: latency-check
spec:
  metrics:
    - name: p99-latency
      interval: 60s
      count: 5
      successCondition: result[0] < 500
      failureLimit: 2
      provider:
        prometheus:
          address: http://prometheus.monitoring:9090
          query: |
            histogram_quantile(0.99,
              sum(rate(http_request_duration_milliseconds_bucket{service="my-app"}[5m]))
              by (le)
            )

Webhookベースの分析

apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
  name: smoke-test
spec:
  args:
    - name: service-name
  metrics:
    - name: smoke-test
      count: 1
      successCondition: result.status == "pass"
      provider:
        web:
          url: 'http://test-runner.ci/api/v1/smoke-test'
          method: POST
          headers:
            - key: Content-Type
              value: application/json
          body: |
            {
              "service": "{{args.service-name}}",
              "tests": ["health", "basic-crud", "auth"]
            }
          jsonPath: '{$.result}'
          timeoutSeconds: 120

Gitリポジトリ構造

k8s-manifests/
├── apps/
│   └── my-app/
│       ├── base/
│       │   ├── kustomization.yaml
│       │   ├── rollout.yaml
│       │   ├── service.yaml
│       │   └── analysis-templates.yaml
│       └── overlays/
│           ├── staging/
│           │   ├── kustomization.yaml
│           │   └── patches/
│           │       └── rollout-strategy.yaml
│           └── production/
│               ├── kustomization.yaml
│               └── patches/
│                   ├── rollout-strategy.yaml
│                   └── replicas.yaml
├── infrastructure/
│   ├── argocd/
│   │   ├── argocd-cm.yaml
│   │   └── projects/
│   └── argo-rollouts/
│       └── install.yaml
└── applicationsets/
    └── my-app.yaml

Kustomizeパッチの例

# overlays/production/patches/rollout-strategy.yaml
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: my-app
spec:
  replicas: 20
  strategy:
    canary:
      steps:
        - setWeight: 5
        - pause: { duration: 10m }
        - analysis:
            templates:
              - templateName: error-rate-check
        - setWeight: 25
        - pause: { duration: 15m }
        - setWeight: 50
        - pause: { duration: 15m }
        - setWeight: 75
        - pause: { duration: 10m }

通知設定

# ArgoCD Notification ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-notifications-cm
  namespace: argocd
data:
  service.slack: |
    token: $slack-token
  template.app-sync-status: |
    message: |
      Application {{.app.metadata.name}} sync status: {{.app.status.sync.status}}
      Health: {{.app.status.health.status}}
      Revision: {{.app.status.sync.revision}}
  trigger.on-sync-failed: |
    - when: app.status.sync.status == 'OutOfSync' and app.status.health.status == 'Degraded'
      send: [app-sync-status]
  trigger.on-health-degraded: |
    - when: app.status.health.status == 'Degraded'
      send: [app-sync-status]

運用ベストプラクティス

1. Sync Waveでデプロイ順序を制御

# ConfigMapを最初にデプロイ
apiVersion: v1
kind: ConfigMap
metadata:
  annotations:
    argocd.argoproj.io/sync-wave: '-1'
---
# 次にRolloutをデプロイ
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  annotations:
    argocd.argoproj.io/sync-wave: '0'
---
# 最後にHPAをデプロイ
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  annotations:
    argocd.argoproj.io/sync-wave: '1'

2. 緊急ロールバックプロセス

# 方法1:Argo Rollouts CLI
kubectl argo rollouts undo my-app -n production

# 方法2:Git Revert(GitOpsアプローチ)
git revert HEAD
git push origin main
# ArgoCDが自動的に以前の状態に同期

# 方法3:ArgoCD UIから以前のリビジョンにSync
argocd app sync my-app --revision <previous-commit-sha>

3. シークレット管理

# Sealed Secretsの使用
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
  name: my-app-secrets
  namespace: production
spec:
  encryptedData:
    DATABASE_URL: AgBy3i4OJSWK+PiTySYZZA9rO...
    API_KEY: AgBy3i4OJSWK+PiTySYZZA9rO...

クイズ

Q1. GitOpsの4つの基本原則は何ですか?

宣言的(Declarative)、バージョン管理(Versioned)、自動適用(Automated)、継続的調整(Continuously Reconciled)です。

Q2. Blue-GreenデプロイでautoPromotionEnabled: falseの意味は?

新バージョン(Green)が準備できても、自動的にトラフィックを切り替えません。kubectl argo rollouts promoteによる手動承認が必要です。

Q3. CanaryデプロイにおけるAnalysisTemplateの役割は?

各Canaryステップで自動的にメトリクス(エラー率、レイテンシなど)を分析し、成功/失敗を判定します。失敗時は自動ロールバックされます。

Q4. ArgoCDのselfHealオプションはどのような動作をしますか?

誰かがkubectlで直接クラスターリソースを変更すると、ArgoCDがそれを検知し、Gitで定義された状態に自動復元します。

Q5. Argo RolloutsでIstioと連携するとどのようなメリットがありますか?

Pod数ではなくトラフィック比率で精密にCanaryトラフィックを制御できます。VirtualServiceのweightをArgo Rolloutsが自動調整します。

Q6. Sync Waveアノテーションの用途は?

リソースのデプロイ順序を制御します。小さい数値が先にデプロイされます(例:ConfigMap(-1) → Rollout(0) → HPA(1))。

Q7. GitOpsでシークレットを安全に管理する方法は?

Sealed Secrets、SOPS、External Secrets Operatorなどを使用して、暗号化されたシークレットのみをGitに保存します。平文のシークレットは絶対にコミットしません。

まとめ

ArgoCDとArgo Rolloutsの組み合わせは、Kubernetes環境で最も強力なGitOpsデプロイパイプラインを提供します。Gitベースの宣言的管理、自動分析ベースのプロモーション/ロールバック、そしてIstio連携による精密なトラフィック制御まで――プロダクションデプロイの安定性と速度を同時に確保できます。

参考資料