Skip to content
Published on

GitOps完全ガイド2025:ArgoCD vs Flux、ApplicationSet、Image Updater、マルチクラスター

Authors

目次(もくじ)

1. はじめに:GitOpsとは何(なに)か

GitOpsは、Gitリポジトリをインフラとアプリケーションの単一(たんいつ)の真実(しんじつ)の源(みなもと)(Single Source of Truth)として使用する運用方法論(うんようほうほうろん)だ。宣言的(せんげんてき)な設定(せってい)ファイルをGitに保存(ほぞん)し、自動化(じどうか)されたプロセスがクラスターの実際(じっさい)の状態(じょうたい)をGitの望(のぞ)ましい状態と同期(どうき)する。

GitOpsの4つの原則(げんそく):

  1. 宣言的(Declarative) - システムの望ましい状態を宣言的に記述(きじゅつ)
  2. バージョン管理(Versioned) - 望ましい状態がGitに保存され、完全(かんぜん)な履歴(りれき)追跡(ついせき)が可能
  3. 自動適用(Pulled) - エージェントが望ましい状態の変更(へんこう)を自動的に検知(けんち)して適用(Pull型)
  4. 継続的調整(Continuously Reconciled) - 実際の状態と望ましい状態の差異(さい)を継続的(けいぞくてき)に検知し修正(しゅうせい)

2. ArgoCD深掘(ふかぼ)り

2.1 アーキテクチャ

ArgoCDは以下(いか)のコアコンポーネントで構成(こうせい)される:

  • API Server: UI、CLI、CI/CDシステムとのインターフェース
  • Repository Server: Gitリポジトリからマニフェストを生成(せいせい)する内部(ないぶ)サービス
  • Application Controller: 実行中(じっこうちゅう)のアプリケーション状態を監視(かんし)し同期
# ArgoCDインストール
kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

# CLIログイン
argocd login argocd.example.com --grpc-web

# クラスター登録
argocd cluster add production-context --name production

2.2 Application CRD

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: api-server
  namespace: argocd
  finalizers:
    - resources-finalizer.argocd.argoproj.io
spec:
  project: production
  source:
    repoURL: https://github.com/myorg/k8s-manifests.git
    targetRevision: main
    path: apps/api-server/overlays/production
    kustomize:
      namePrefix: prod-
      commonLabels:
        env: production
  destination:
    server: https://kubernetes.default.svc
    namespace: production
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
      allowEmpty: false
    syncOptions:
      - CreateNamespace=true
      - PrunePropagationPolicy=foreground
      - PruneLast=true
      - ServerSideApply=true
    retry:
      limit: 5
      backoff:
        duration: 5s
        factor: 2
        maxDuration: 3m
  ignoreDifferences:
    - group: apps
      kind: Deployment
      jsonPointers:
        - /spec/replicas
    - group: autoscaling
      kind: HorizontalPodAutoscaler
      jqPathExpressions:
        - .spec.metrics[].resource.target

2.3 Sync Hooks

# Pre-sync: DBマイグレーション
apiVersion: batch/v1
kind: Job
metadata:
  name: db-migration
  annotations:
    argocd.argoproj.io/hook: PreSync
    argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
  template:
    spec:
      containers:
        - name: migrate
          image: myapp/db-migrate:latest
          command: ["./migrate", "up"]
          env:
            - name: DATABASE_URL
              valueFrom:
                secretKeyRef:
                  name: db-credentials
                  key: url
      restartPolicy: Never
  backoffLimit: 3
---
# Post-sync: Slack通知
apiVersion: batch/v1
kind: Job
metadata:
  name: notify-deploy
  annotations:
    argocd.argoproj.io/hook: PostSync
    argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
  template:
    spec:
      containers:
        - name: notify
          image: curlimages/curl
          command:
            - /bin/sh
            - -c
            - |
              curl -X POST -H 'Content-type: application/json' \
                --data '{"text":"API Server deployed successfully to production"}' \
                https://hooks.slack.com/services/T00/B00/xxx
      restartPolicy: Never
---
# SyncFail: 失敗時通知
apiVersion: batch/v1
kind: Job
metadata:
  name: notify-fail
  annotations:
    argocd.argoproj.io/hook: SyncFail
    argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
  template:
    spec:
      containers:
        - name: notify
          image: curlimages/curl
          command:
            - /bin/sh
            - -c
            - |
              curl -X POST -H 'Content-type: application/json' \
                --data '{"text":"ALERT: API Server deployment FAILED in production"}' \
                https://hooks.slack.com/services/T00/B00/xxx
      restartPolicy: Never

2.4 Health Checkのカスタマイズ

apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-cm
  namespace: argocd
data:
  resource.customizations.health.argoproj.io_Rollout: |
    hs = {}
    if obj.status ~= nil then
      if obj.status.currentStepIndex ~= nil then
        hs.status = "Progressing"
        hs.message = "Rollout in progress"
      end
      if obj.status.phase == "Healthy" then
        hs.status = "Healthy"
        hs.message = "Rollout is healthy"
      end
      if obj.status.phase == "Degraded" then
        hs.status = "Degraded"
        hs.message = "Rollout is degraded"
      end
    end
    return hs
  resource.customizations.health.cert-manager.io_Certificate: |
    hs = {}
    if obj.status ~= nil then
      if obj.status.conditions ~= nil then
        for i, condition in ipairs(obj.status.conditions) do
          if condition.type == "Ready" and condition.status == "False" then
            hs.status = "Degraded"
            hs.message = condition.message
            return hs
          end
          if condition.type == "Ready" and condition.status == "True" then
            hs.status = "Healthy"
            hs.message = condition.message
            return hs
          end
        end
      end
    end
    hs.status = "Progressing"
    hs.message = "Waiting for certificate"
    return hs

2.5 RBAC設定(せってい)

apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-rbac-cm
  namespace: argocd
data:
  policy.default: role:readonly
  policy.csv: |
    # プロジェクト管理者
    p, role:project-admin, applications, *, production/*, allow
    p, role:project-admin, applications, sync, production/*, allow
    p, role:project-admin, logs, get, production/*, allow

    # 開発者: 読み取り + syncのみ
    p, role:developer, applications, get, */*, allow
    p, role:developer, applications, sync, staging/*, allow
    p, role:developer, logs, get, */*, allow

    # CI/CDシステム
    p, role:ci-cd, applications, sync, */*, allow
    p, role:ci-cd, applications, get, */*, allow

    # グループマッピング
    g, platform-team, role:admin
    g, backend-team, role:project-admin
    g, frontend-team, role:developer
    g, github-actions, role:ci-cd
  scopes: '[groups, email]'

3. Flux深掘り

3.1 コアリソース

# GitRepository: Gitソース定義
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
  name: platform-repo
  namespace: flux-system
spec:
  interval: 1m
  url: https://github.com/myorg/platform-config
  ref:
    branch: main
  secretRef:
    name: git-credentials
  ignore: |
    # exclude all
    /*
    # include deploy dir
    !/deploy
---
# Kustomization: 何をどこにデプロイするか定義
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: api-server
  namespace: flux-system
spec:
  interval: 5m
  retryInterval: 2m
  timeout: 3m
  sourceRef:
    kind: GitRepository
    name: platform-repo
  path: ./deploy/apps/api-server/production
  prune: true
  wait: true
  healthChecks:
    - apiVersion: apps/v1
      kind: Deployment
      name: api-server
      namespace: production
  patches:
    - patch: |
        apiVersion: apps/v1
        kind: Deployment
        metadata:
          name: api-server
        spec:
          replicas: 5
      target:
        kind: Deployment
        name: api-server
---
# HelmRelease: Helmチャートデプロイ
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
  name: redis
  namespace: production
spec:
  interval: 10m
  chart:
    spec:
      chart: redis
      version: "18.x"
      sourceRef:
        kind: HelmRepository
        name: bitnami
        namespace: flux-system
  values:
    architecture: replication
    auth:
      enabled: true
      existingSecret: redis-auth
    replica:
      replicaCount: 3
      resources:
        requests:
          cpu: 250m
          memory: 256Mi
        limits:
          cpu: "1"
          memory: 1Gi
  valuesFrom:
    - kind: ConfigMap
      name: redis-values
      valuesKey: values.yaml
  upgrade:
    remediation:
      retries: 3
  rollback:
    cleanupOnFail: true

3.2 Flux通知(つうち)設定

# Provider: Slack通知
apiVersion: notification.toolkit.fluxcd.io/v1beta3
kind: Provider
metadata:
  name: slack
  namespace: flux-system
spec:
  type: slack
  channel: deployments
  secretRef:
    name: slack-webhook
---
# Alert: 特定イベントの通知
apiVersion: notification.toolkit.fluxcd.io/v1beta3
kind: Alert
metadata:
  name: deployment-alerts
  namespace: flux-system
spec:
  providerRef:
    name: slack
  eventSeverity: error
  eventSources:
    - kind: Kustomization
      name: "*"
    - kind: HelmRelease
      name: "*"
  exclusionList:
    - ".*upgrade.*retries exhausted.*"
  summary: "Flux deployment issue detected"

4. ArgoCD vs Flux比較(ひかく)

項目(こうもく)ArgoCDFlux
UI豊富(ほうふ)なWeb UI内蔵別途UI必要(Weave GitOps等)
アーキテクチャ中央集権型(ちゅうおうしゅうけんがた)分散型、ネームスペース単位
CRD数少数(Application、AppProject)多数(GitRepo、Kustomization、HelmRelease等)
Helmサポートテンプレートレンダリング後適用ネイティブHelmRelease CRD
Kustomize内蔵サポート内蔵サポート
マルチテナンシーAppProjectで隔離(かくり)ネームスペースベースの隔離
Image自動更新ArgoCD Image UpdaterFlux Image Automation
SSOOIDC/SAML/LDAPサポートUIツールに依存
RBAC細粒度(さいりゅうど)のロールベースアクセス制御Kubernetes RBAC活用
CLIargocd CLIflux CLI
通知Notification設定Alert/Provider CRD
Health CheckカスタムLuaスクリプトKustomizationヘルスチェック
Drift検知リアルタイムUI表示定期的Reconciliation
Progressive DeliveryArgo Rollouts連携Flagger連携
学習曲線(がくしゅうきょくせん)中程度(UIが助ける)高い(CRDベース設定)

5. ApplicationSet

5.1 List Generator

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: microservices
  namespace: argocd
spec:
  generators:
    - list:
        elements:
          - cluster: production
            url: https://prod-k8s.example.com
            namespace: production
            replicas: "5"
          - cluster: staging
            url: https://staging-k8s.example.com
            namespace: staging
            replicas: "2"
          - cluster: development
            url: https://dev-k8s.example.com
            namespace: development
            replicas: "1"
  template:
    metadata:
      name: "api-server-{{cluster}}"
    spec:
      project: default
      source:
        repoURL: https://github.com/myorg/k8s-manifests.git
        targetRevision: main
        path: "apps/api-server/overlays/{{cluster}}"
        kustomize:
          commonLabels:
            cluster: "{{cluster}}"
      destination:
        server: "{{url}}"
        namespace: "{{namespace}}"
      syncPolicy:
        automated:
          prune: true
          selfHeal: true

5.2 Cluster Generator

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: platform-monitoring
  namespace: argocd
spec:
  generators:
    - clusters:
        selector:
          matchLabels:
            monitoring: enabled
        values:
          prometheus_retention: "30d"
  template:
    metadata:
      name: "monitoring-{{name}}"
    spec:
      project: platform
      source:
        repoURL: https://github.com/myorg/platform-charts.git
        targetRevision: main
        path: charts/monitoring
        helm:
          values: |
            cluster: "{{name}}"
            prometheus:
              retention: "{{values.prometheus_retention}}"
            grafana:
              ingress:
                host: "grafana-{{name}}.example.com"
      destination:
        server: "{{server}}"
        namespace: monitoring
      syncPolicy:
        automated:
          prune: true
          selfHeal: true
        syncOptions:
          - CreateNamespace=true

5.3 Git Generator

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: team-apps
  namespace: argocd
spec:
  generators:
    - git:
        repoURL: https://github.com/myorg/k8s-manifests.git
        revision: main
        directories:
          - path: "apps/*/overlays/production"
          - path: "apps/legacy-*"
            exclude: true
  template:
    metadata:
      name: "{{path[1]}}"
    spec:
      project: default
      source:
        repoURL: https://github.com/myorg/k8s-manifests.git
        targetRevision: main
        path: "{{path}}"
      destination:
        server: https://kubernetes.default.svc
        namespace: production
      syncPolicy:
        automated:
          prune: true
          selfHeal: true
---
# Git Fileジェネレーター: config.jsonファイルベース
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: dynamic-apps
  namespace: argocd
spec:
  generators:
    - git:
        repoURL: https://github.com/myorg/app-config.git
        revision: main
        files:
          - path: "environments/*/config.json"
  template:
    metadata:
      name: "{{app.name}}-{{environment}}"
    spec:
      project: "{{app.project}}"
      source:
        repoURL: "{{app.repo}}"
        targetRevision: "{{app.revision}}"
        path: "{{app.path}}"
      destination:
        server: "{{cluster.url}}"
        namespace: "{{app.namespace}}"

5.4 Matrix Generator

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: cross-cluster-apps
  namespace: argocd
spec:
  generators:
    - matrix:
        generators:
          - clusters:
              selector:
                matchLabels:
                  env: production
          - list:
              elements:
                - app: api-server
                  chart: api-server
                  namespace: backend
                - app: web-frontend
                  chart: web-frontend
                  namespace: frontend
                - app: worker
                  chart: worker
                  namespace: backend
  template:
    metadata:
      name: "{{app}}-{{name}}"
    spec:
      project: production
      source:
        repoURL: https://github.com/myorg/helm-charts.git
        targetRevision: main
        path: "charts/{{chart}}"
        helm:
          valueFiles:
            - "values-{{metadata.labels.region}}.yaml"
      destination:
        server: "{{server}}"
        namespace: "{{namespace}}"
      syncPolicy:
        automated:
          prune: true
          selfHeal: true

5.5 Pull Request Generator

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: pr-preview
  namespace: argocd
spec:
  generators:
    - pullRequest:
        github:
          owner: myorg
          repo: api-server
          tokenRef:
            secretName: github-token
            key: token
          labels:
            - preview
        requeueAfterSeconds: 30
  template:
    metadata:
      name: "pr-{{number}}-api-server"
      labels:
        preview: "true"
    spec:
      project: previews
      source:
        repoURL: https://github.com/myorg/api-server.git
        targetRevision: "{{head_sha}}"
        path: deploy/preview
        kustomize:
          namePrefix: "pr-{{number}}-"
          commonLabels:
            pr: "{{number}}"
      destination:
        server: https://kubernetes.default.svc
        namespace: "preview-{{number}}"
      syncPolicy:
        automated:
          prune: true
          selfHeal: true
        syncOptions:
          - CreateNamespace=true

6. Image Updater

6.1 ArgoCD Image Updater

# インストール
# kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj-labs/argocd-image-updater/stable/manifests/install.yaml

# Applicationにイメージ更新設定
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: api-server
  namespace: argocd
  annotations:
    # イメージリストと更新戦略
    argocd-image-updater.argoproj.io/image-list: "app=myregistry.com/api-server"
    argocd-image-updater.argoproj.io/app.update-strategy: semver
    argocd-image-updater.argoproj.io/app.allow-tags: "regexp:^v[0-9]+\\.[0-9]+\\.[0-9]+$"
    argocd-image-updater.argoproj.io/app.ignore-tags: "latest, nightly"
    # Git write-back方式
    argocd-image-updater.argoproj.io/write-back-method: git
    argocd-image-updater.argoproj.io/write-back-target: "kustomization:../../base"
    argocd-image-updater.argoproj.io/git-branch: main
spec:
  project: production
  source:
    repoURL: https://github.com/myorg/k8s-manifests.git
    targetRevision: main
    path: apps/api-server/overlays/production
  destination:
    server: https://kubernetes.default.svc
    namespace: production

6.2 Flux Image Automation

# ImageRepository: イメージレジストリ監視
apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImageRepository
metadata:
  name: api-server
  namespace: flux-system
spec:
  image: myregistry.com/api-server
  interval: 5m
  secretRef:
    name: registry-credentials
---
# ImagePolicy: どのタグを選択するか定義
apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImagePolicy
metadata:
  name: api-server
  namespace: flux-system
spec:
  imageRepositoryRef:
    name: api-server
  policy:
    semver:
      range: ">=1.0.0"
  filterTags:
    pattern: "^v(?P<version>[0-9]+\\.[0-9]+\\.[0-9]+)$"
    extract: "$version"
---
# ImageUpdateAutomation: Git自動コミット
apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImageUpdateAutomation
metadata:
  name: auto-update
  namespace: flux-system
spec:
  interval: 5m
  sourceRef:
    kind: GitRepository
    name: platform-repo
  git:
    checkout:
      ref:
        branch: main
    commit:
      author:
        name: flux-bot
        email: flux@myorg.com
      messageTemplate: |
        chore(image): update images

        Automated image update:
        {{range .Changed.Changes}}
        - {{.OldValue}} -> {{.NewValue}}
        {{end}}
    push:
      branch: main
  update:
    path: ./deploy
    strategy: Setters

7. Helm + GitOps

7.1 ArgoCDでのHelm使用(しよう)

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: nginx-ingress
  namespace: argocd
spec:
  project: platform
  source:
    repoURL: https://kubernetes.github.io/ingress-nginx
    chart: ingress-nginx
    targetRevision: 4.9.1
    helm:
      releaseName: nginx-ingress
      values: |
        controller:
          replicaCount: 3
          resources:
            requests:
              cpu: 200m
              memory: 256Mi
            limits:
              cpu: "1"
              memory: 512Mi
          metrics:
            enabled: true
            serviceMonitor:
              enabled: true
          admissionWebhooks:
            enabled: true
      valueFiles:
        - values-production.yaml
      parameters:
        - name: controller.service.type
          value: LoadBalancer
  destination:
    server: https://kubernetes.default.svc
    namespace: ingress-system
  syncPolicy:
    automated:
      prune: true
    syncOptions:
      - CreateNamespace=true

7.2 Umbrella Chartパターン

# Chart.yaml(umbrella chart)
apiVersion: v2
name: platform-stack
version: 1.0.0
dependencies:
  - name: prometheus
    version: 25.x.x
    repository: https://prometheus-community.github.io/helm-charts
    condition: prometheus.enabled
  - name: grafana
    version: 7.x.x
    repository: https://grafana.github.io/helm-charts
    condition: grafana.enabled
  - name: loki
    version: 5.x.x
    repository: https://grafana.github.io/helm-charts
    condition: loki.enabled

8. Kustomize + GitOps

8.1 環境別(かんきょうべつ)Overlays

apps/api-server/
  base/
    deployment.yaml
    service.yaml
    kustomization.yaml
  overlays/
    development/
      kustomization.yaml
      patches/
        replicas.yaml
    staging/
      kustomization.yaml
      patches/
        replicas.yaml
        resources.yaml
    production/
      kustomization.yaml
      patches/
        replicas.yaml
        resources.yaml
        hpa.yaml
# base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - deployment.yaml
  - service.yaml
commonLabels:
  app: api-server
---
# overlays/production/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - ../../base
namePrefix: prod-
patches:
  - path: patches/replicas.yaml
  - path: patches/resources.yaml
  - path: patches/hpa.yaml
configMapGenerator:
  - name: api-config
    literals:
      - LOG_LEVEL=warn
      - ENVIRONMENT=production
---
# overlays/production/patches/replicas.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-server
spec:
  replicas: 5

9. シークレット管理(かんり)

9.1 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

# シークレット暗号化
kubectl create secret generic db-credentials \
  --from-literal=username=admin \
  --from-literal=password=super-secret \
  --dry-run=client -o yaml | \
  kubeseal --controller-name=sealed-secrets \
  --controller-namespace=kube-system \
  --format yaml > sealed-db-credentials.yaml
# sealed-db-credentials.yaml(Gitにコミット可能)
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
  name: db-credentials
  namespace: production
spec:
  encryptedData:
    username: AgB1k2j3l4m5n6o7p8...
    password: AgC9d0e1f2g3h4i5j6...
  template:
    metadata:
      name: db-credentials
      namespace: production
    type: Opaque

9.2 External Secrets Operator

# SecretStore: Vault接続
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: vault-backend
  namespace: production
spec:
  provider:
    vault:
      server: https://vault.example.com
      path: secret
      version: v2
      auth:
        kubernetes:
          mountPath: kubernetes
          role: production-app
          serviceAccountRef:
            name: vault-auth
---
# ExternalSecret: Vaultからシークレットを同期
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: db-credentials
  namespace: production
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: vault-backend
    kind: SecretStore
  target:
    name: db-credentials
    creationPolicy: Owner
    template:
      type: Opaque
      data:
        DATABASE_URL: "postgresql://{{ .username }}:{{ .password }}@db.production:5432/myapp"
  data:
    - secretKey: username
      remoteRef:
        key: production/database
        property: username
    - secretKey: password
      remoteRef:
        key: production/database
        property: password
---
# ClusterSecretStore: クラスター全体で使用
apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
  name: aws-secrets-manager
spec:
  provider:
    aws:
      service: SecretsManager
      region: ap-northeast-2
      auth:
        jwt:
          serviceAccountRef:
            name: external-secrets-sa
            namespace: external-secrets

9.3 SOPS(Secrets OPerationS)

# .sops.yaml設定ファイル
creation_rules:
  - path_regex: ".*\\.enc\\.yaml$"
    encrypted_regex: "^(data|stringData)$"
    age: age1ql3z7hjy54pw3hyww5ay...
  - path_regex: "environments/production/.*"
    kms: "arn:aws:kms:ap-northeast-2:123456789012:key/abc-123"
  - path_regex: "environments/staging/.*"
    pgp: "FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4"
# SOPSで暗号化
sops --encrypt --in-place secrets.enc.yaml

# FluxでSOPS使用のためのKustomization
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: api-server
  namespace: flux-system
spec:
  interval: 5m
  sourceRef:
    kind: GitRepository
    name: platform-repo
  path: ./deploy/apps/api-server
  prune: true
  decryption:
    provider: sops
    secretRef:
      name: sops-age-key

10. Progressive Delivery

10.1 Argo Rollouts - カナリアデプロイ

apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: api-server
  namespace: production
spec:
  replicas: 10
  revisionHistoryLimit: 3
  selector:
    matchLabels:
      app: api-server
  template:
    metadata:
      labels:
        app: api-server
    spec:
      containers:
        - name: api-server
          image: myregistry.com/api-server:v2.0.0
          ports:
            - containerPort: 8080
          resources:
            requests:
              cpu: 500m
              memory: 512Mi
            limits:
              cpu: "1"
              memory: 1Gi
  strategy:
    canary:
      canaryService: api-server-canary
      stableService: api-server-stable
      trafficRouting:
        istio:
          virtualServices:
            - name: api-server-vsvc
              routes:
                - primary
      steps:
        - setWeight: 5
        - pause:
            duration: 5m
        - setWeight: 20
        - pause:
            duration: 10m
        - setWeight: 50
        - pause:
            duration: 10m
        - setWeight: 80
        - pause:
            duration: 5m
      analysis:
        templates:
          - templateName: success-rate
          - templateName: latency
        startingStep: 1
        args:
          - name: service-name
            value: api-server-canary
---
# AnalysisTemplate: 成功率検証
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
  name: success-rate
spec:
  args:
    - name: service-name
  metrics:
    - name: success-rate
      interval: 60s
      successCondition: result[0] >= 0.99
      failureLimit: 3
      provider:
        prometheus:
          address: http://prometheus.monitoring:9090
          query: |
            sum(rate(http_requests_total{service="{{args.service-name}}",status=~"2.."}[5m]))
            /
            sum(rate(http_requests_total{service="{{args.service-name}}"}[5m]))
---
# AnalysisTemplate: レイテンシー検証
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
  name: latency
spec:
  args:
    - name: service-name
  metrics:
    - name: p99-latency
      interval: 60s
      successCondition: result[0] < 500
      failureLimit: 3
      provider:
        prometheus:
          address: http://prometheus.monitoring:9090
          query: |
            histogram_quantile(0.99,
              sum(rate(http_request_duration_seconds_bucket{service="{{args.service-name}}"}[5m]))
              by (le)
            ) * 1000

10.2 Argo Rollouts - ブルーグリーンデプロイ

apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: web-frontend
  namespace: production
spec:
  replicas: 5
  selector:
    matchLabels:
      app: web-frontend
  template:
    metadata:
      labels:
        app: web-frontend
    spec:
      containers:
        - name: web
          image: myregistry.com/web-frontend:v3.0.0
          ports:
            - containerPort: 3000
  strategy:
    blueGreen:
      activeService: web-frontend-active
      previewService: web-frontend-preview
      autoPromotionEnabled: false
      scaleDownDelaySeconds: 300
      prePromotionAnalysis:
        templates:
          - templateName: smoke-tests
        args:
          - name: preview-url
            value: "http://web-frontend-preview.production.svc:3000"
      postPromotionAnalysis:
        templates:
          - templateName: success-rate
        args:
          - name: service-name
            value: web-frontend-active

11. マルチクラスターGitOps

11.1 Hub-Spokeパターン

management-cluster/          (Hub)
  argocd/
    applicationsets/
      monitoring.yaml         -> すべてのクラスターにモニタリングデプロイ
      logging.yaml            -> すべてのクラスターにロギングデプロイ
      apps.yaml               -> 環境別アプリデプロイ
    projects/
      platform.yaml
      production.yaml
      staging.yaml
    clusters/
      prod-us-east.yaml
      prod-ap-northeast.yaml
      staging-us-east.yaml

11.2 リポジトリ構造(こうぞう): Monorepo vs Polyrepo

Monorepo構造:

k8s-manifests/
  apps/
    api-server/
      base/
      overlays/
        development/
        staging/
        production/
    web-frontend/
      base/
      overlays/
        development/
        staging/
        production/
  platform/
    monitoring/
    logging/
    ingress/
  clusters/
    production/
    staging/

Polyrepo構造:

# repo: api-server-deploy
deploy/
  base/
  overlays/
    development/
    staging/
    production/

# repo: web-frontend-deploy
deploy/
  base/
  overlays/
    development/
    staging/
    production/

# repo: platform-config
monitoring/
logging/
ingress/
項目MonorepoPolyrepo
可視性(かしせい)システム全体(ぜんたい)を一望サービスごとに独立(どくりつ)
アクセス制御ディレクトリベース(CODEOWNERS)リポジトリごと
CI/CD変更検知が複雑シンプルなトリガー
依存性管理容易(ようい)バージョン管理が必要
規模(きぼ)中小規模に適合大規模組織に適合

11.3 App-of-Appsパターン

# ルートApplication: 他のApplicationを管理
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: root-app
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/myorg/k8s-manifests.git
    targetRevision: main
    path: clusters/production
  destination:
    server: https://kubernetes.default.svc
    namespace: argocd
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

12. Drift検知(けんち)とReconciliation

12.1 ArgoCD Drift検知

ArgoCDはリアルタイムでGitの望ましい状態とクラスターの実際の状態を比較する。

# 特定フィールドをDrift検知から除外
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: api-server
spec:
  ignoreDifferences:
    # HPAが管理するreplicasを無視
    - group: apps
      kind: Deployment
      jsonPointers:
        - /spec/replicas
    # 自動生成されるアノテーションを無視
    - group: ""
      kind: Service
      jqPathExpressions:
        - .metadata.annotations."service.beta.kubernetes.io/aws-load-balancer-*"
    # MutatingWebhookが注入するサイドカーを無視
    - group: apps
      kind: Deployment
      managedFieldsManagers:
        - istio-sidecar-injector

12.2 Flux Reconciliation

# 強制Reconciliation
# flux reconcile kustomization api-server --with-source

# Reconciliation状態確認
# flux get kustomizations
# flux get helmreleases -A

# 特定リソースの一時停止
# flux suspend kustomization api-server
# flux resume kustomization api-server

13. 実践(じっせん)クイズ

Q1: GitOpsのPullベースモデルがPushベース(従来のCI/CD)よりセキュリティ面で優れている理由は?

正解(せいかい): Pullベースモデルでは、クラスター内部のエージェント(ArgoCD/Flux)がGitをポーリングして変更を適用する。CIパイプラインにクラスターアクセス資格情報を公開する必要がない。

  • Pushモデル: CIサーバーがkubectl/helmで直接デプロイ -> クラスター資格情報がCIに必要
  • Pullモデル: クラスター内部エージェントがGitを監視 -> クラスター資格情報が外部に公開されない
  • 攻撃対象面(Attack Surface)の縮小: CIシステムが侵害されてもクラスターに直接アクセスできない
Q2: ApplicationSetのMatrix Generatorはどのような状況で有用か?

正解: Matrix Generatorは2つのジェネレーターを組み合わせてデカルト積(すべての組み合わせ)を作成する。複数のクラスターに複数のアプリケーションをデプロイする場合に有用。

  • 例: 3クラスター(prod-us、prod-eu、prod-ap)x 5アプリ = 15個のApplication自動生成
  • Cluster Generator + List Generatorの組み合わせが最も一般的
  • 重複設定なしですべての組み合わせを自動管理
Q3: ArgoCD Image Updaterのwrite-back-methodがgitの場合とargocdの場合の違いは?

正解: git方式はイメージタグの変更をGitリポジトリにコミットする。argocd方式はArgoCD Applicationのパラメーターを直接変更する。

  • git: Gitが単一の真実の源として維持される。監査証跡あり。速度は遅い
  • argocd: Gitを変更しない。高速だがGitと実際の状態が不一致になる可能性がある
  • 本番環境ではgit方式を推奨
Q4: Sealed SecretsとExternal Secrets Operatorの主な違いは?

正解: Sealed Secretsはシークレットを暗号化してGitに保存し、External Secrets Operatorは外部シークレットストア(Vault、AWS SM等)からランタイムに同期する。

  • Sealed Secrets: 暗号化されたシークレットをGitに保存。オフラインでも動作。キーローテーションが複雑
  • External Secrets Operator: 外部ストアからリアルタイム同期。一元管理。外部サービス依存
  • 小規模チーム: Sealed Secretsがよりシンプル
  • 大規模組織: External Secrets + Vaultの組み合わせを推奨
Q5: Argo Rolloutsのカナリアデプロイ中にAnalysisTemplateが失敗するとどうなるか?

正解: AnalysisTemplateのメトリクスがsuccessConditionを満たさずfailureLimitに達すると、Rolloutは自動的にロールバックされる。

  • カナリアトラフィックが0%に戻る
  • stableバージョンがすべてのトラフィックを受ける
  • Rollout状態がDegradedに変更
  • 手動リトライ: kubectl argo rollouts retry rollout api-server
  • 分析結果確認: kubectl argo rollouts get rollout api-server

14. 参考資料(さんこうしりょう)

  1. ArgoCD公式ドキュメント
  2. Flux公式ドキュメント
  3. ApplicationSetドキュメント
  4. ArgoCD Image Updater
  5. Argo Rollouts
  6. Sealed Secrets
  7. External Secrets Operator
  8. SOPS
  9. Flagger
  10. GitOps Principles - OpenGitOps
  11. Kustomize
  12. Helm
  13. Weave GitOps