CI/CD入門 - 継続的インテグレーション・デリバリーの仕組みとパイプライン構築
CI/CD(Continuous Integration / Continuous Delivery・Deployment)の基本概念、パイプラインの構成要素、主要ツール比較、GitHub Actionsによる実装例をまとめる。
CIとCDの違い
| 用語 | 正式名称 | 概要 |
|---|---|---|
| CI | Continuous Integration(継続的インテグレーション) | コード変更を頻繁にメインブランチへ統合し、自動でビルド・テストを実行する |
| CD(デリバリー) | Continuous Delivery(継続的デリバリー) | CIに加え、リリース可能な状態まで自動化する。本番デプロイは手動承認 |
| CD(デプロイ) | Continuous Deployment(継続的デプロイ) | テスト通過後、本番環境への反映まですべて自動化する |
flowchart LR
A["コード変更"] --> B["ビルド"]
B --> C["テスト"]
C --> D["ステージング<br/>デプロイ"]
D --> E{"手動承認"}
E -->|"Continuous Delivery"| F["本番デプロイ"]
E -->|"Continuous Deployment<br/>(承認不要)"| F
style A fill:#e3f2fd
style B fill:#e8f5e9
style C fill:#e8f5e9
style D fill:#fff3e0
style F fill:#fce4ec
subgraph CI ["CI(継続的インテグレーション)"]
B
C
end
CI(継続的インテグレーション)
- 開発者がコードをプッシュするたびに、自動でビルドとテストが実行される
- 統合の問題を早期に検出し、修正コストを低減する
- 「動くコード」が常にメインブランチに存在する状態を維持する
CD(継続的デリバリー / 継続的デプロイ)
- Continuous Delivery: リリース候補を自動で作成するが、本番へのデプロイは人間が判断する
- Continuous Deployment: テストをすべて通過すれば自動的に本番へデプロイされる
- 小規模チームやSaaSではContinuous Deployment、規制産業やエンタープライズではContinuous Deliveryが採用されることが多い
パイプラインの構成要素
典型的なCI/CDパイプラインは以下のステージで構成される。
flowchart TD
A["1. ソースコード取得<br/>(checkout)"] --> B["2. 依存関係の<br/>インストール"]
B --> C["3. 静的解析<br/>(lint / format check)"]
C --> D["4. ユニットテスト"]
D --> E["5. ビルド"]
E --> F["6. 統合テスト /<br/>E2Eテスト"]
F --> G["7. アーティファクト<br/>の保存"]
G --> H["8. デプロイ"]
style A fill:#e3f2fd
style B fill:#e3f2fd
style C fill:#e8f5e9
style D fill:#e8f5e9
style E fill:#fff3e0
style F fill:#fff3e0
style G fill:#f3e5f5
style H fill:#fce4ec
| ステージ | 目的 | ツール例 |
|---|---|---|
| ソースコード取得 | リポジトリからコードをチェックアウト | actions/checkout |
| 依存関係インストール | パッケージの復元・キャッシュ | npm, pip, Maven, Gradle |
| 静的解析 | コードスタイル・品質チェック | ESLint, Prettier, Flake8, RuboCop |
| ユニットテスト | 個別の関数・モジュールの検証 | Jest, pytest, JUnit |
| ビルド | 実行可能なアーティファクトの生成 | Vite, webpack, Gradle, Docker |
| 統合 / E2Eテスト | 複数コンポーネントの結合動作確認 | Playwright, Cypress, Selenium |
| アーティファクト保存 | ビルド成果物の保管 | S3, GitHub Artifacts, ECR |
| デプロイ | 環境への反映 | AWS CLI, kubectl, Terraform |
主要CI/CDツール比較
クラウドホスティング型
| ツール | 特徴 | 料金体系 | 適したケース |
|---|---|---|---|
| GitHub Actions | GitHubに統合、YAML定義、豊富なMarketplace | パブリックリポジトリ無料、プライベートは月2,000分無料 | GitHub利用プロジェクト全般 |
| GitLab CI/CD | GitLabに統合、Auto DevOps機能 | 月400分無料(SaaS版) | GitLab利用プロジェクト |
| CircleCI | 高速実行、Docker対応が強力 | 月6,000クレジット無料 | Docker中心の開発 |
| AWS CodePipeline | AWSサービスとのネイティブ統合 | パイプラインごとの従量課金 | AWSインフラ中心の開発 |
セルフホスティング型
| ツール | 特徴 | 適したケース |
|---|---|---|
| Jenkins | プラグインエコシステムが巨大、高い拡張性 | オンプレ環境、複雑なパイプライン |
| Drone CI | Docker中心のシンプルな設計 | コンテナベースのワークフロー |
| Gitea Actions | GitHub Actions互換の構文 | Giteaセルフホスト環境 |
ツール選定の指針
- ソースコード管理と同一プラットフォームを選ぶとセットアップが最小限になる
- セルフホスティングランナーのサポートがあれば、クラウド型でもオンプレ実行が可能
- エコシステム(Marketplace・プラグイン) の充実度が運用の効率に直結する
GitHub Actionsのワークフロー
基本構造
GitHub Actionsのワークフローは .github/workflows/ ディレクトリにYAMLファイルとして配置する。
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
# ワークフロー全体で使用する環境変数
env:
NODE_VERSION: "20"
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: "npm"
- run: npm ci
- run: npm run lint
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: "npm"
- run: npm ci
- run: npm test
build:
runs-on: ubuntu-latest
needs: [lint, test] # lint, testの両方が成功してから実行
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: "npm"
- run: npm ci
- run: npm run build
- uses: actions/upload-artifact@v4
with:
name: build-output
path: dist/
主要な概念
| 概念 | 説明 |
|---|---|
| ワークフロー | .ymlファイル1つに対応する自動化プロセス全体 |
イベント(on) | ワークフローを起動するトリガー(push, pull_request, scheduleなど) |
ジョブ(jobs) | 同一ランナー上で実行されるステップのまとまり。デフォルトで並列実行 |
ステップ(steps) | ジョブ内の個別の処理。usesでアクション、runでシェルコマンドを実行 |
needs | ジョブ間の依存関係を定義する。指定したジョブの完了後に実行される |
アクション(uses) | 再利用可能な処理単位。Marketplaceで公開されている |
| マトリクス戦略 | 複数の環境(Node.js 18/20/22 など)で同一ジョブを並列実行する |
マトリクス戦略の例
複数のNode.jsバージョンとOSの組み合わせでテストを実行する場合:
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
node-version: [18, 20, 22]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm test
デプロイジョブの例(S3 + CloudFront)
deploy:
runs-on: ubuntu-latest
needs: build
if: github.ref == 'refs/heads/main' # mainブランチのみ
permissions:
id-token: write # OIDC認証に必要
contents: read
steps:
- uses: actions/download-artifact@v4
with:
name: build-output
path: dist/
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsDeployRole
aws-region: ap-northeast-1
- name: Deploy to S3
run: aws s3 sync dist/ s3://my-bucket/ --delete
- name: Invalidate CloudFront cache
run: |
aws cloudfront create-invalidation \
--distribution-id ${{ vars.CLOUDFRONT_DISTRIBUTION_ID }} \
--paths "/*"
典型的なパイプライン構成
フロントエンドプロジェクト
flowchart LR
A["Push /<br/>Pull Request"] --> B["lint<br/>(ESLint, Prettier)"]
B --> C["test<br/>(Jest / Vitest)"]
C --> D["build<br/>(Vite / webpack)"]
D --> E["deploy<br/>(S3 / Vercel /<br/>Cloudflare Pages)"]
style A fill:#e3f2fd
style B fill:#e8f5e9
style C fill:#e8f5e9
style D fill:#fff3e0
style E fill:#fce4ec
バックエンドプロジェクト(コンテナ)
flowchart LR
A["Push /<br/>Pull Request"] --> B["lint"]
B --> C["unit test"]
C --> D["Docker<br/>build"]
D --> E["integration<br/>test"]
E --> F["ECR<br/>push"]
F --> G["ECS /<br/>EKS deploy"]
style A fill:#e3f2fd
style B fill:#e8f5e9
style C fill:#e8f5e9
style D fill:#fff3e0
style E fill:#fff3e0
style F fill:#f3e5f5
style G fill:#fce4ec
モノレポ構成
モノレポでは変更されたパッケージのみパイプラインを実行することで効率化を図る。
on:
push:
paths:
- "packages/frontend/**"
jobs:
frontend:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npx turbo run build --filter=frontend
主要なモノレポツールとCI連携:
| ツール | 特徴 |
|---|---|
| Turborepo | タスクのキャッシュ・並列実行、--filterで対象パッケージを絞り込み |
| Nx | 依存グラフに基づく影響範囲の検出(nx affected) |
| Lerna | npmパッケージ管理に特化、changesets連携 |
ブランチ戦略との連携
CI/CDパイプラインはブランチ戦略と密接に関連する。
Trunk-Based Development
gitGraph
commit id: "init"
branch feature-a
commit id: "feat-a"
checkout main
merge feature-a id: "merge-a"
commit id: "release-1"
branch feature-b
commit id: "feat-b"
checkout main
merge feature-b id: "merge-b"
| 特性 | 内容 |
|---|---|
| メインブランチ | mainのみ。常にデプロイ可能 |
| フィーチャーブランチ | 短命(1〜2日以内にマージ) |
| CI実行タイミング | すべてのpushとPR |
| CD | mainマージ後に自動デプロイ |
| 適したチーム | 小〜中規模、デプロイ頻度が高い |
Git Flow
| 特性 | 内容 |
|---|---|
| メインブランチ | main(本番)+ develop(開発) |
| フィーチャーブランチ | developから分岐、developへマージ |
| リリースブランチ | release/*で最終調整後mainへマージ |
| CI実行タイミング | developとrelease/*へのpush |
| CD | mainマージ時に本番デプロイ |
| 適したチーム | 大規模、リリースサイクルが固定 |
GitHub Flow
| 特性 | 内容 |
|---|---|
| メインブランチ | mainのみ |
| フィーチャーブランチ | PRベースでレビュー・マージ |
| CI実行タイミング | PRの作成・更新時 |
| CD | mainマージ後に自動デプロイ |
| 適したチーム | 中規模、WebアプリケーションやSaaS |
セキュリティ
シークレット管理
CI/CDパイプラインではAPIキー、認証情報、トークンなどの機密情報を安全に扱う必要がある。
GitHub Actionsのシークレット:
steps:
- name: Deploy
env:
API_KEY: ${{ secrets.API_KEY }} # リポジトリシークレット
DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
run: ./deploy.sh
| レベル | スコープ | 設定場所 |
|---|---|---|
| リポジトリシークレット | 単一リポジトリ | Settings → Secrets and variables → Actions |
| 環境シークレット | 特定の環境(production等) | Settings → Environments → 環境名 → Secrets |
| Organizationシークレット | 組織内の複数リポジトリ | Organization Settings → Secrets |
シークレット管理のベストプラクティス:
- ワークフローのログにシークレットが出力されないよう
::add-mask::を活用する - シークレットは定期的にローテーションする
- 最小権限の原則に基づき、必要なシークレットのみ各ジョブに渡す
.envファイルをリポジトリにコミットしない(.gitignoreに追加)
OIDC認証(OpenID Connect)
従来の長期的なアクセスキーの代わりに、OIDCを使用して一時的な認証情報を取得する方式。GitHub ActionsからAWSなどのクラウドプロバイダーへの認証で推奨される。
flowchart LR
A["GitHub Actions<br/>ワークフロー"] -->|"1. OIDCトークン<br/>を取得"| B["GitHub<br/>OIDCプロバイダー"]
B -->|"2. トークンを<br/>提示"| C["AWS STS<br/>(AssumeRoleWithWebIdentity)"]
C -->|"3. 一時認証情報<br/>を返却"| A
A -->|"4. 一時認証情報で<br/>AWSリソースにアクセス"| D["AWS<br/>リソース"]
style A fill:#e3f2fd
style B fill:#e8f5e9
style C fill:#fff3e0
style D fill:#fce4ec
OIDCのメリット:
| 観点 | 長期アクセスキー | OIDC |
|---|---|---|
| 有効期間 | 手動ローテーションまで有効 | ワークフロー実行時のみ有効(数分〜1時間) |
| 漏洩リスク | シークレットの漏洩リスクあり | 一時的な認証情報のため影響が限定的 |
| 管理コスト | 定期的なローテーションが必要 | 自動的に管理される |
| 設定 | アクセスキーをシークレットに保存 | IAMロールの信頼ポリシーを設定 |
AWS側のIAM信頼ポリシー例:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
},
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:my-org/my-repo:ref:refs/heads/main"
}
}
}
]
}
環境保護ルール
GitHub Actionsの Environments 機能を使用すると、デプロイ前に承認を要求したり、特定のブランチからのみデプロイを許可したりできる。
jobs:
deploy-production:
runs-on: ubuntu-latest
environment:
name: production # 保護ルールが適用される
url: https://example.com
steps:
- run: ./deploy.sh
| 保護ルール | 説明 |
|---|---|
| 必須レビュアー | 指定したユーザーの承認がないとデプロイされない |
| 待機タイマー | 指定した時間だけデプロイを遅延させる |
| ブランチ制限 | 特定のブランチからのみデプロイを許可 |
| デプロイメントブランチ | mainやrelease/*のみにデプロイを制限 |
キャッシュとパフォーマンス最適化
CI/CDパイプラインの実行時間を短縮するための手法。
依存関係のキャッシュ
- uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm" # node_modulesのキャッシュを自動管理
手動でキャッシュを管理する場合:
- uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-npm-
ジョブの並列実行
依存関係のないジョブは並列で実行し、needsで依存関係のあるジョブのみ直列にする。
jobs:
lint: # ┐
... # ├─ 並列実行
test: # ┘
...
build:
needs: [lint, test] # lint, testの完了後に実行
その他の最適化手法
| 手法 | 効果 |
|---|---|
| ジョブの並列化 | 独立したジョブを同時に実行して全体時間を短縮 |
pathsフィルタ | 変更のあったファイルに関連するジョブのみ実行 |
| Dockerレイヤーキャッシュ | Dockerビルド時にレイヤーを再利用して高速化 |
| セルフホストランナー | 専用マシンで実行し、キャッシュの永続性を確保 |
concurrency設定 | 同一ブランチでの重複実行をキャンセル |
concurrencyの設定例:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true # 同じグループの古い実行をキャンセル
参考リンク
- GitHub Actions ドキュメント - GitHub公式
- GitHub Actions のセキュリティ強化 - GitHub公式
- GitLab CI/CD ドキュメント - GitLab公式
- Jenkins ドキュメント - Jenkins公式
- CircleCI ドキュメント - CircleCI公式
- AWS CodePipeline ユーザーガイド - AWS公式
- GitHub Actions で OpenID Connect を使用する - GitHub公式