- Authors
- Name
- はじめに
- 1. GitOpsコア原則
- 2. ブランチ戦略の比較
- 3. PR(Pull Request)ルール
- 4. コミットコンベンション
- 5. ArgoCDを活用したGitOpsパイプライン
- 6. CI/CDパイプライン設計
- 7. 環境別デプロイ戦略
- 8. 運用チェックリスト
- 9. よくある間違い
- 10. まとめ
はじめに
「コードがすなわちインフラである。」GitOpsは、この一文を運用の現実にしたパラダイムだ。Gitリポジトリをシングルソースオブトゥルース(SSOT)とし、宣言的な設定ファイルへの変更履歴の一つ一つが、インフラとアプリケーションの状態そのものとなる。もはや手動でkubectl applyを実行したり、Slackで「デプロイお願いします」と依頼する必要はない。
しかし、GitOpsを導入したからといって、すべてが自動的に整理されるわけではない。ブランチ戦略が乱雑であればデプロイパイプラインも乱雑になり、PRルールがなければコード品質は保証されず、コミットメッセージがバラバラであれば変更履歴の追跡は不可能になる。GitOpsの真の力は、Gitワークフロー全体が体系的に設計されて初めて発揮される。
この記事では、GitOpsのコア原則からブランチ戦略の比較、PRルール設計、コミットコンベンション、ArgoCDパイプライン構成、環境別デプロイ戦略、そして運用チェックリストまで、チームですぐに適用できるレベルで徹底解説する。
1. GitOpsコア原則
GitOpsは2017年にWeaveworksが提唱した概念で、以下の4つの原則に基づいている。
1.1 宣言的(Declarative)
すべてのシステム状態を宣言的に定義する。「このコマンドを実行せよ」ではなく、**「この状態であるべきだ」**を記述する。
# 宣言的な状態定義の例 — Kubernetes Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-server
namespace: production
spec:
replicas: 3
selector:
matchLabels:
app: api-server
template:
metadata:
labels:
app: api-server
spec:
containers:
- name: api-server
image: ghcr.io/myorg/api-server:v2.4.1
ports:
- containerPort: 8080
1.2 バージョン管理(Versioned and Immutable)
すべての変更はGitにコミットとして記録される。誰が、いつ、なぜ、何を変更したか — 完全な監査ログ(Audit Log)が自動的に残る。ロールバックはgit revertで完了する。
1.3 自動適用(Pulled Automatically)
承認された変更は自動的にシステムに適用される。Push方式(CIが直接デプロイ)ではなく、**Pull方式(エージェントがGitを監視し、変更を検知して適用する)**を使用する。
1.4 自己修復(Continuously Reconciled)
実際のシステム状態がGitに定義された状態と異なる場合、自動的に修正する。誰かがkubectlで直接変更しても、GitOpsエージェントが元の状態に戻す。
| 原則 | 核心的な問い | 失敗時の症状 |
|---|---|---|
| 宣言的 | 望ましい状態がコードで定義されているか? | 設定ドリフト、再現不可 |
| バージョン管理 | すべての変更がGitに記録されているか? | 変更追跡不可、責任所在の不明確さ |
| 自動適用 | 変更が手動介入なしに適用されるか? | デプロイ遅延、ヒューマンエラー |
| 自己修復 | ドリフトが自動的に修正されるか? | 環境の不整合、障害の拡散 |
2. ブランチ戦略の比較
ブランチ戦略は、チームの規模、リリースサイクル、プロダクトの特性に応じて選択すべきだ。3つの主要な戦略を比較する。
2.1 Git Flow
Vincent Driessenが2010年に提案したクラシックモデル。main、develop、feature/*、release/*、hotfix/*の5種類のブランチタイプを使用する。
main ────●─────────────●──────●── (プロダクション)
\ / \ /
release \ ●────● \ /
\ / \/
develop ─────●──●──●──●───●──●── (統合)
\ \ /
feature ●──●────●
適したケース: 定期リリースサイクルのあるチーム、モバイルアプリ、パッケージライブラリ
2.2 GitHub Flow
mainブランチとfeatureブランチのみを使用するシンプルなモデル。featureブランチで作業し、PRを通じてmainにマージする。
main ────●──●──●──●──●── (常にデプロイ可能)
\ / \ /
feature ●──● ●──●
適したケース: 継続的デプロイ(CD)を実践するSaaSチーム、小規模チーム
2.3 トランクベース開発(Trunk-Based Development)
すべての開発者がtrunk(main)に直接コミットするか、非常に短命なブランチ(1日以内)を使用する。
main ────●──●──●──●──●──●──●── (すべてのコミットがデプロイ候補)
\/ \/ \/
short ● ● ● (1日以内にマージ)
適したケース: 大規模エンジニアリング組織(Google、Meta)、フィーチャーフラグベースのリリース
比較表
| 項目 | Git Flow | GitHub Flow | Trunk-Based |
|---|---|---|---|
| ブランチ数 | 5種以上 | 2種 | 1〜2種 |
| マージ頻度 | 週1〜2回 | 日1〜3回 | 日5〜10回以上 |
| リリースサイクル | 隔週/月次 | 随時 | 随時 |
| 複雑さ | 高い | 低い | 低い |
| フィーチャーフラグ必要 | いいえ | 任意 | 必須 |
| CI要求レベル | 中程度 | 高い | 非常に高い |
| ロールバック容易性 | releaseタグ | コミット単位 | コミット/フラグ |
| GitOps適合性 | 普通 | 良い | 非常に良い |
推奨: GitOps環境ではGitHub Flowまたはトランクベース開発を推奨する。Git Flowのreleaseブランチは、GitOpsの自動同期パターンと摩擦が大きい。
3. PR(Pull Request)ルール
PRは単なるコードレビューツールではない。変更の意図を文書化し、品質ゲートを通過させ、チームの知識を共有する中核プロセスだ。
3.1 PRテンプレート
.github/PULL_REQUEST_TEMPLATE.mdファイルをリポジトリルートに追加する。
## 変更内容
<!-- 何をなぜ変更したか簡潔に説明 -->
## 変更タイプ
- [ ] 機能追加 (feature)
- [ ] バグ修正 (bugfix)
- [ ] リファクタリング (refactor)
- [ ] インフラ/設定変更 (infra)
- [ ] ドキュメント更新 (docs)
## テスト
- [ ] ユニットテスト追加/修正
- [ ] 統合テスト通過
- [ ] ローカル環境で手動検証
## チェックリスト
- [ ] コミットメッセージがConventional Commits形式に従っているか?
- [ ] 不要なファイル(ログ、一時ファイル)が含まれていないか?
- [ ] 既存APIと下位互換性があるか?
- [ ] セキュリティ上の機密情報(シークレット、キー)が含まれていないか?
## 関連Issue
Closes #
## スクリーンショット(UI変更時)
3.2 レビューチェックリスト
レビュアーが確認すべき主要項目:
- 機能の正確性 — 変更が意図通りに動作するか?
- エッジケース — 空値、大量データ、同時リクエストなどの例外状況を処理しているか?
- セキュリティ — SQLインジェクション、XSS、シークレット露出のリスクはないか?
- パフォーマンス — N+1クエリ、不要な計算、メモリリークはないか?
- テストカバレッジ — 変更されたロジックに対するテストがあるか?
- 命名規則 — 変数、関数、クラス名が意味を適切に伝えているか?
3.3 マージ戦略
| 戦略 | コミット履歴 | 適した状況 |
|---|---|---|
| Squash Merge | featureの全コミットを1つに統合 | mainの履歴をきれいに保ちたい場合 |
| Rebase Merge | featureコミットをmain上に再配置 | コミット単位が論理的に適切に分割されている場合 |
| Merge Commit | マージコミットを生成 | featureブランチのコンテキストを保持したい場合 |
チーム推奨設定: Squash Mergeをデフォルトに設定し、
mainブランチへの直接pushを禁止する。GitHub Settings > Branches > Branch protection rulesで「Require a pull request before merging」を有効にする。
4. コミットコンベンション
4.1 Conventional Commitsフォーマット
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
typeの一覧:
| type | 説明 | 例 |
|---|---|---|
feat | 新機能 | feat(auth): ソーシャルログイン追加 |
fix | バグ修正 | fix(api): ページネーションオフセットエラー修正 |
docs | ドキュメント変更 | docs: API仕様更新 |
style | フォーマッティング、セミコロンなど | style: ESLintルール適用 |
refactor | リファクタリング | refactor(db): クエリ最適化 |
test | テスト追加/修正 | test: 会員登録E2Eテスト追加 |
chore | ビルド、ツール設定 | chore: Node.js 20にアップグレード |
ci | CI設定変更 | ci: GitHub Actionsキャッシュ最適化 |
perf | パフォーマンス改善 | perf(api): レスポンスキャッシュ適用 |
4.2 commitlintセットアップ
# インストール
npm install --save-dev @commitlint/cli @commitlint/config-conventional
# commitlint.config.js
echo "module.exports = { extends: ['@commitlint/config-conventional'] };" > commitlint.config.js
# huskyでGitフック連携
npx husky init
echo "npx --no -- commitlint --edit \$1" > .husky/commit-msg
commitlint.config.jsでチームルールをカスタマイズできる。
// commitlint.config.js
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [
2,
'always',
['feat', 'fix', 'docs', 'style', 'refactor', 'test', 'chore', 'ci', 'perf', 'revert'],
],
'subject-max-length': [2, 'always', 72],
'body-max-line-length': [2, 'always', 100],
},
}
5. ArgoCDを活用したGitOpsパイプライン
5.1 ArgoCDアーキテクチャ
ArgoCDはKubernetesネイティブなGitOpsデプロイツールで、以下のコンポーネントで構成される。
- API Server — Web UI、CLI、gRPC/REST APIを提供
- Repository Server — Gitリポジトリからマニフェストをレンダリング
- Application Controller — 実際の状態と目標状態を継続的に比較し同期
5.2 Application定義
# argocd/applications/api-server.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: api-server
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
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
syncPolicy:
automated:
prune: true # Gitから削除されたリソースを自動削除
selfHeal: true # ドリフトの自動修正
syncOptions:
- CreateNamespace=true
- PruneLast=true
retry:
limit: 3
backoff:
duration: 5s
factor: 2
maxDuration: 3m
5.3 Syncポリシーオプション
| オプション | 説明 | 推奨 |
|---|---|---|
automated.prune | Gitから削除されたリソースを自動削除 | プロダクション: true |
automated.selfHeal | 手動変更時の自動修正 | プロダクション: true |
syncOptions.CreateNamespace | ネームスペースの自動作成 | 開発: true、プロダクション: false |
retry.limit | 同期失敗時のリトライ回数 | 3〜5 |
5.4 Gitリポジトリのディレクトリ構造
k8s-manifests/
├── apps/
│ ├── api-server/
│ │ ├── base/
│ │ │ ├── deployment.yaml
│ │ │ ├── service.yaml
│ │ │ ├── hpa.yaml
│ │ │ └── kustomization.yaml
│ │ └── overlays/
│ │ ├── dev/
│ │ │ ├── kustomization.yaml
│ │ │ └── patches/
│ │ │ └── replicas.yaml
│ │ ├── staging/
│ │ │ ├── kustomization.yaml
│ │ │ └── patches/
│ │ │ └── replicas.yaml
│ │ └── production/
│ │ ├── kustomization.yaml
│ │ └── patches/
│ │ ├── replicas.yaml
│ │ └── resources.yaml
│ └── web-frontend/
│ ├── base/
│ └── overlays/
├── argocd/
│ ├── applications/
│ │ ├── api-server.yaml
│ │ └── web-frontend.yaml
│ └── projects/
│ └── default.yaml
└── README.md
6. CI/CDパイプライン設計
6.1 全体フロー
graph LR
A[開発者Push] --> B[GitHub Actions CI]
B --> C{テスト通過?}
C -->|Yes| D[コンテナイメージビルド]
C -->|No| E[失敗通知]
D --> F[イメージPush - GHCR]
F --> G[マニフェストリポジトリPR作成]
G --> H[自動マージ / 手動承認]
H --> I[ArgoCD検知]
I --> J[Kubernetes同期]
J --> K{ヘルスチェック}
K -->|Healthy| L[デプロイ完了]
K -->|Degraded| M[自動ロールバック]
6.2 GitHub Actionsワークフロー
# .github/workflows/ci-cd.yaml
name: CI/CD Pipeline
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Lint
run: npm run lint
- name: Unit tests
run: npm run test -- --coverage
- name: Upload coverage
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
build-and-push:
needs: test
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
outputs:
image-tag: ${{ steps.meta.outputs.version }}
steps:
- uses: actions/checkout@v4
- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=sha,prefix=
type=ref,event=branch
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
cache-from: type=gha
cache-to: type=gha,mode=max
update-manifest:
needs: build-and-push
runs-on: ubuntu-latest
steps:
- name: Checkout manifest repo
uses: actions/checkout@v4
with:
repository: myorg/k8s-manifests
token: ${{ secrets.MANIFEST_REPO_TOKEN }}
- name: Update image tag
run: |
cd apps/api-server/base
kustomize edit set image \
ghcr.io/myorg/api-server=ghcr.io/myorg/api-server:${{ needs.build-and-push.outputs.image-tag }}
- name: Create PR
uses: peter-evans/create-pull-request@v6
with:
token: ${{ secrets.MANIFEST_REPO_TOKEN }}
commit-message: 'chore: update api-server image to ${{ needs.build-and-push.outputs.image-tag }}'
title: 'deploy: api-server ${{ needs.build-and-push.outputs.image-tag }}'
body: |
自動生成されたデプロイPRです。
- ソースコミット: ${{ github.sha }}
- イメージタグ: ${{ needs.build-and-push.outputs.image-tag }}
branch: deploy/api-server-${{ needs.build-and-push.outputs.image-tag }}
base: main
7. 環境別デプロイ戦略
7.1 環境分離の原則
| 環境 | 目的 | 同期方式 | 承認必要 |
|---|---|---|---|
| dev | 機能開発、統合テスト | 自動 (Auto Sync) | いいえ |
| staging | QA、パフォーマンステスト、UAT | 自動 (Auto Sync) | いいえ |
| production | 本番サービス | 手動 (Manual Sync) | はい |
7.2 Kustomize Overlay構成
base/kustomization.yaml:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
- hpa.yaml
commonLabels:
app: api-server
overlays/dev/kustomization.yaml:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
namePrefix: dev-
namespace: dev
patches:
- path: patches/replicas.yaml
images:
- name: ghcr.io/myorg/api-server
newTag: latest
overlays/dev/patches/replicas.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-server
spec:
replicas: 1
template:
spec:
containers:
- name: api-server
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 200m
memory: 256Mi
overlays/production/kustomization.yaml:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
namespace: production
patches:
- path: patches/replicas.yaml
- path: patches/resources.yaml
images:
- name: ghcr.io/myorg/api-server
newTag: v2.4.1 # プロダクションは明示的タグを使用
overlays/production/patches/replicas.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-server
spec:
replicas: 5
template:
spec:
containers:
- name: api-server
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: '1'
memory: 1Gi
7.3 環境別Helm Values
Helmを使用する場合、環境別のvaluesファイルで分離する。
helm-charts/
├── api-server/
│ ├── Chart.yaml
│ ├── templates/
│ ├── values.yaml # デフォルト値
│ ├── values-dev.yaml # dev環境
│ ├── values-staging.yaml # staging環境
│ └── values-prod.yaml # production環境
# values-prod.yaml
replicaCount: 5
image:
repository: ghcr.io/myorg/api-server
tag: v2.4.1
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: '1'
memory: 1Gi
autoscaling:
enabled: true
minReplicas: 5
maxReplicas: 20
targetCPUUtilization: 70
ingress:
enabled: true
hosts:
- host: api.myservice.com
paths:
- path: /
pathType: Prefix
8. 運用チェックリスト
チームでGitOpsワークフローを運用する際に必ず確認すべき項目。
| カテゴリ | 項目 | 状態 |
|---|---|---|
| ブランチ保護 | mainブランチへの直接push禁止 | [ ] |
| ブランチ保護 | PRなしのマージ禁止 | [ ] |
| ブランチ保護 | Force push禁止 | [ ] |
| コードレビュー | 最低1人のレビュー承認必須 | [ ] |
| コードレビュー | CODEOWNERSファイル設定 | [ ] |
| コードレビュー | Stale review自動解除設定 | [ ] |
| CIゲート | PRにCI通過必須設定(required status checks) | [ ] |
| CIゲート | テストカバレッジ閾値設定 | [ ] |
| CIゲート | リント/フォーマット検査通過必須 | [ ] |
| コミット | commitlint + husky設定 | [ ] |
| コミット | Conventional Commitsガイドのドキュメント化 | [ ] |
| セキュリティ | シークレットスキャン有効化(GitHub Secret Scanning) | [ ] |
| セキュリティ | .gitignoreに機密ファイルパターン追加 | [ ] |
| セキュリティ | Dependabot / Renovate有効化 | [ ] |
| ArgoCD | ApplicationリソースのGit管理 | [ ] |
| ArgoCD | Sync通知連携(Slack/Teams) | [ ] |
| ArgoCD | RBAC設定(チーム別プロジェクト権限) | [ ] |
| ArgoCD | プロダクション手動Sync設定 | [ ] |
CODEOWNERSファイルの例:
# .github/CODEOWNERS
# デフォルトオーナー
* @myorg/platform-team
# インフラマニフェスト
/apps/ @myorg/sre-team
/argocd/ @myorg/sre-team
# フロントエンド
/apps/web-frontend/ @myorg/frontend-team
# バックエンドAPI
/apps/api-server/ @myorg/backend-team
9. よくある間違い
GitOpsとGitワークフローを導入する際に、チームがよく犯す間違いの一覧。
アプリコードとマニフェストを同じリポジトリで管理 — アプリのCIが実行されるたびにArgoCDの同期がトリガーされる。マニフェストは必ず別のリポジトリに分離すること。
プロダクションでAuto Sync + Auto Pruneを設定 — 誤ってマージされた削除が即座にプロダクションに反映される。プロダクションではManual Syncを使用するか、最低限
selfHealのみを有効にすること。プロダクションでlatestタグを使用 — どのバージョンがデプロイされたか追跡が不可能になる。必ずイミュータブルタグ(SHA、セマンティックバージョン)を使用すること。
PRレビューなしで自動マージ — マニフェスト変更PRも必ずレビューを経る必要がある。自動マージはdev環境にのみ許可すること。
RBACなしでArgoCDを運用 — すべてのチームメンバーがすべてのアプリケーションをSync/Deleteできると事故が発生する。Project + RBACで権限を最小化すること。
意味のないコミットメッセージを書く —
fix、update、testのようなコミットメッセージは3ヶ月後には誰も理解できない。Conventional Commitsを強制すること。featureブランチを長期間維持 — 3日以上生存するfeatureブランチはマージコンフリクトの温床になる。小さなPRで頻繁にマージすること。
シークレットをGitにコミット — 一度でもGitにpushされたシークレットは履歴に永遠に残る。Sealed Secrets、External Secrets Operator、またはVaultを使用すること。
環境別の差異をハードコーディング — dev/staging/prodの差異を個別のYAMLファイルで作成せず、Kustomize overlaysまたはHelm valuesで管理すること。
ロールバック手順を文書化しない — 障害時に「git revert後にArgoCD sync」という手順をすべてのチームメンバーが知っている必要がある。Runbookを作成すること。
10. まとめ
| 領域 | 重要ポイント |
|---|---|
| GitOps原則 | 宣言的、バージョン管理、自動適用、自己修復 — 4つすべてを満たして初めて真のGitOps |
| ブランチ戦略 | GitOpsにはGitHub FlowまたはTrunk-Basedが最適。チーム規模とリリースサイクルに合わせて選択 |
| PRルール | テンプレート + チェックリスト + 最低1人レビュー + CIゲート = 品質の最終防衛線 |
| コミットコンベンション | Conventional Commits + commitlintで機械的に強制。人に依存しない |
| ArgoCD | Application YAML、syncPolicy、ディレクトリ構造を標準化しGitで管理 |
| CI/CD | App Repo → CI(ビルド/テスト/プッシュ) → Manifest Repo PR → ArgoCD Syncのフローを自動化 |
| 環境分離 | Kustomize overlaysまたはHelm values-env.yamlで環境別の差異を管理 |
| 運用 | チェックリストをチームオンボーディングドキュメントに含め、定期的に点検 |
GitOpsはツールではなく文化だ。ArgoCDをインストールすることが終わりではなく、チーム全体がGit中心のワークフローを体得し、ルールを守ることが核心だ。この記事で取り上げた内容を一つずつ適用しながら、チームに合ったワークフローを構築していってほしい。