目次(もくじ)
1. はじめに:GitOpsとは何(なに)か
GitOpsは、Gitリポジトリをインフラとアプリケーションの単一(たんいつ)の真実(しんじつ)の源(みなもと)(Single Source of Truth)として使用する運用方法論(うんようほうほうろん)だ。宣言的(せんげんてき)な設定(せってい)ファイルをGitに保存(ほぞん)し、自動化(じどうか)されたプロセスがクラスターの実際(じっさい)の状態(じょうたい)をGitの望(のぞ)ましい状態と同期(どうき)する。
GitOpsの4つの原則(げんそく):
- 宣言的(Declarative) - システムの望ましい状態を宣言的に記述(きじゅつ)
- バージョン管理(Versioned) - 望ましい状態がGitに保存され、完全(かんぜん)な履歴(りれき)追跡(ついせき)が可能
- 自動適用(Pulled) - エージェントが望ましい状態の変更(へんこう)を自動的に検知(けんち)して適用(Pull型)
- 継続的調整(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比較(ひかく)
| 項目(こうもく) | ArgoCD | Flux |
|---|---|---|
| UI | 豊富(ほうふ)なWeb UI内蔵 | 別途UI必要(Weave GitOps等) |
| アーキテクチャ | 中央集権型(ちゅうおうしゅうけんがた) | 分散型、ネームスペース単位 |
| CRD数 | 少数(Application、AppProject) | 多数(GitRepo、Kustomization、HelmRelease等) |
| Helmサポート | テンプレートレンダリング後適用 | ネイティブHelmRelease CRD |
| Kustomize | 内蔵サポート | 内蔵サポート |
| マルチテナンシー | AppProjectで隔離(かくり) | ネームスペースベースの隔離 |
| Image自動更新 | ArgoCD Image Updater | Flux Image Automation |
| SSO | OIDC/SAML/LDAPサポート | UIツールに依存 |
| RBAC | 細粒度(さいりゅうど)のロールベースアクセス制御 | Kubernetes RBAC活用 |
| CLI | argocd CLI | flux CLI |
| 通知 | Notification設定 | Alert/Provider CRD |
| Health Check | カスタムLuaスクリプト | Kustomizationヘルスチェック |
| Drift検知 | リアルタイムUI表示 | 定期的Reconciliation |
| Progressive Delivery | Argo 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/
| 項目 | Monorepo | Polyrepo |
|---|---|---|
| 可視性(かしせい) | システム全体(ぜんたい)を一望 | サービスごとに独立(どくりつ) |
| アクセス制御 | ディレクトリベース(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/1102)
GitOpsは、Gitリポジトリをインフラとアプリケーションの単一(たんいつ)の真実(しんじつ)の源(みなもと)(Single Source of Truth)として使用する運用方法論(うんようほうほ...