Skip to content
Published on

GitOps + Gitワークフロー完全ガイド — ブランチ戦略・PRルール・デプロイパイプライン

Authors
  • Name
    Twitter

はじめに

「コードがすなわちインフラである。」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年に提案したクラシックモデル。maindevelopfeature/*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 FlowGitHub FlowTrunk-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 レビューチェックリスト

レビュアーが確認すべき主要項目:

  1. 機能の正確性 — 変更が意図通りに動作するか?
  2. エッジケース — 空値、大量データ、同時リクエストなどの例外状況を処理しているか?
  3. セキュリティ — SQLインジェクション、XSS、シークレット露出のリスクはないか?
  4. パフォーマンス — N+1クエリ、不要な計算、メモリリークはないか?
  5. テストカバレッジ — 変更されたロジックに対するテストがあるか?
  6. 命名規則 — 変数、関数、クラス名が意味を適切に伝えているか?

3.3 マージ戦略

戦略コミット履歴適した状況
Squash Mergefeatureの全コミットを1つに統合mainの履歴をきれいに保ちたい場合
Rebase Mergefeatureコミットを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にアップグレード
ciCI設定変更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.pruneGitから削除されたリソースを自動削除プロダクション: 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)いいえ
stagingQA、パフォーマンステスト、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有効化[ ]
ArgoCDApplicationリソースのGit管理[ ]
ArgoCDSync通知連携(Slack/Teams)[ ]
ArgoCDRBAC設定(チーム別プロジェクト権限)[ ]
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ワークフローを導入する際に、チームがよく犯す間違いの一覧。

  1. アプリコードとマニフェストを同じリポジトリで管理 — アプリのCIが実行されるたびにArgoCDの同期がトリガーされる。マニフェストは必ず別のリポジトリに分離すること。

  2. プロダクションでAuto Sync + Auto Pruneを設定 — 誤ってマージされた削除が即座にプロダクションに反映される。プロダクションではManual Syncを使用するか、最低限selfHealのみを有効にすること。

  3. プロダクションでlatestタグを使用 — どのバージョンがデプロイされたか追跡が不可能になる。必ずイミュータブルタグ(SHA、セマンティックバージョン)を使用すること。

  4. PRレビューなしで自動マージ — マニフェスト変更PRも必ずレビューを経る必要がある。自動マージはdev環境にのみ許可すること。

  5. RBACなしでArgoCDを運用 — すべてのチームメンバーがすべてのアプリケーションをSync/Deleteできると事故が発生する。Project + RBACで権限を最小化すること。

  6. 意味のないコミットメッセージを書くfixupdatetestのようなコミットメッセージは3ヶ月後には誰も理解できない。Conventional Commitsを強制すること。

  7. featureブランチを長期間維持 — 3日以上生存するfeatureブランチはマージコンフリクトの温床になる。小さなPRで頻繁にマージすること。

  8. シークレットをGitにコミット — 一度でもGitにpushされたシークレットは履歴に永遠に残る。Sealed Secrets、External Secrets Operator、またはVaultを使用すること。

  9. 環境別の差異をハードコーディング — dev/staging/prodの差異を個別のYAMLファイルで作成せず、Kustomize overlaysまたはHelm valuesで管理すること。

  10. ロールバック手順を文書化しない — 障害時に「git revert後にArgoCD sync」という手順をすべてのチームメンバーが知っている必要がある。Runbookを作成すること。

10. まとめ

領域重要ポイント
GitOps原則宣言的、バージョン管理、自動適用、自己修復 — 4つすべてを満たして初めて真のGitOps
ブランチ戦略GitOpsにはGitHub FlowまたはTrunk-Basedが最適。チーム規模とリリースサイクルに合わせて選択
PRルールテンプレート + チェックリスト + 最低1人レビュー + CIゲート = 品質の最終防衛線
コミットコンベンションConventional Commits + commitlintで機械的に強制。人に依存しない
ArgoCDApplication YAML、syncPolicy、ディレクトリ構造を標準化しGitで管理
CI/CDApp Repo → CI(ビルド/テスト/プッシュ) → Manifest Repo PR → ArgoCD Syncのフローを自動化
環境分離Kustomize overlaysまたはHelm values-env.yamlで環境別の差異を管理
運用チェックリストをチームオンボーディングドキュメントに含め、定期的に点検

GitOpsはツールではなく文化だ。ArgoCDをインストールすることが終わりではなく、チーム全体がGit中心のワークフローを体得し、ルールを守ることが核心だ。この記事で取り上げた内容を一つずつ適用しながら、チームに合ったワークフローを構築していってほしい。