はじめに:CI/CDの必要性
「ローカルでは動いたけど、本番で動かない」
そんな状況に陥ったことはありませんか?
CI/CD(継続的インテグレーション/継続的デリバリー)は、開発・テスト・デプロイの自動化を通じて、開発品質の向上とチームの効率化を実現する仕組みです。
今回紹介するのは、GitHub Actions × Docker Compose を使った最小構成のCI/CDフローです。
今回のCI/CDフロー(図解)
今回は試しに以下のようなフローを構築しようと思います。
① ローカル開発(別ブランチ)
↓
② 開発ブランチにpush(例: feature/login)
↓
③ CI(自動):
- Docker Composeでbuildできるか
- pytestでテストが通るか
- flake8でコード品質チェック
↓
④ Pull Request作成
↓
⑤ mainブランチにマージされたら自動でCD(デプロイ)
- docker-compose.prod.yml を使って
- 本番環境用イメージをBuild & 起動
この構成の特徴は、開発ブランチでCI、mainブランチでCDという明確な責任分離です。
実際に使用したコード
構成について
.
├── app/
│ └── main.py # 本体アプリケーションのロジック(例:add関数)
├── tests/
│ └── test_main.py # pytest によるユニットテスト
├── docker-compose.yml # CI用の開発環境構成(ビルド&テスト実行)
├── docker-compose.prod.yml # 本番用デプロイ構成(ポート公開・永続化など)
├── Dockerfile # アプリのビルド定義(pytest + flake8用)
├── requirements.txt # Python依存ライブラリ定義
├── pytest.ini # カバレッジ表示用の設定ファイル
└── .github/
└── workflows/
├── ci.yml # CI処理:lint, testを自動化
└── deploy.yml # CD処理:mainマージ時に本番デプロイ
.github/workflows/ci.yml
name: CI
on:
push:
branches: ["*", "!main"]
pull_request:
branches: [main]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: pip install flake8
- run: flake8 app tests
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: docker compose up --abort-on-container-exit
詳細解説
name: CI
- このワークフローの名前。GitHub Actions UIに表示される。
on:
push:
branches: ["*", "!main"]
pull_request:
branches: [main]
- push: どのブランチでもpush時にCIを実行(ただし main は除外)
- pull_request: main ブランチに対するPR作成時にもCIを実行
lintジョブ
jobs:
lint:
runs-on: ubuntu-latest
- GitHubリポジトリのソースコードをチェックアウト
- run: pip install flake8
- 静的解析ツール flake8 をインストール
- run: flake8 app tests
- app/とtests/に対してPythonコードスタイルのチェックを実行
- 例:関数定義前に2行空いていない、未使用importがあるなどのエラーを検出
testジョブ(例)
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: docker compose up --abort-on-container-exit
- docker compose で docker-compose.yml に基づくテスト環境(app + db)を起動
- pytest を実行し、テスト失敗時は exit code != 0 でワークフロー全体が失敗となる
- --abort-on-container-exit によって、app コンテナが終了すると全体が止まる
.github/workflows/deploy.yml
name: Deploy to Production
on:-
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: docker compose -f docker-compose.prod.yml up -d --build
詳細解説
name: Deploy to Production
- デプロイ用ワークフローのタイトル。GitHub UIに表示される。
on:
push:
branches: [main]
- main ブランチに push されたときに発火
- → PRをマージした後、自動で実行される
デプロイジョブの定義
jobs:
deploy:
runs-on: ubuntu-latest
- deploy というジョブ名で、Ubuntu上で動作
steps:
- uses: actions/checkout@v3
- ソースコード(mainブランチ)の最新状態を取得
- run: docker compose -f docker-compose.prod.yml up -d --build
- docker-compose.prod.yml に基づき 本番用イメージをビルド&バックグラウンド起動
- -d:バックグラウンドで起動
- --build:常に最新ソースコードからイメージを再構築
補足:なぜ ci.yml と deploy.yml を分けるのか?
| 理由 | 説明 |
|---|---|
| 責務の分離 | CIはテスト品質保証、CDは本番反映。混ぜると保守性が下がる |
| 再利用性 | CIだけ複数ブランチで使えるが、CDはmainだけで良い |
| 安全性 | CDが条件付きで動作するようにすることで誤デプロイを防げる |
個人的にハマったポイント
Docker Compose のバージョン違い
- docker-compose(ハイフンあり)は GitHub Actionsでは非推奨
- 正しくは docker compose(スペースあり)を使用
他のよくあるフロー構成パターン
GitHub Actions では、用途に応じてCI/CDの構成を柔軟に変えることができます。以下に、実務でよく使われる3つのパターンと、それに対応する設定・想定シナリオを紹介します。
パターン①:Staging環境を挟むCD構成
構成イメージ
feature/xxx ブランチ
↓ push
CI:lint / test(自動)
↓ PR → main
main ブランチにマージ
↓
CD:Stagingに自動デプロイ
↓ 手動承認
Productionブランチにマージ
↓
CD:本番に自動デプロイ
こんなときに使う
- 本番に即反映させたくない(社内レビューや検証が必要)
- ステージング環境でのQAやUATを挟みたいとき
- 複数人チームで、リリース責任を明確にしたいとき
deploy.yml(Staging用)
on:
push:
branches: [main] # mainマージでStagingにCD
# VPSやCloud RunなどにStagingデプロイ処理を書く
deploy-prod.yml(Production用)
on:
push:
branches: [release]
# 本番用環境に対してデプロイ処理を記述
パターン②:タグ付きリリースで本番CD(セマンティックバージョン)
構成イメージ
mainブランチが安定したタイミングで:
$ git tag v1.0.0
$ git push origin v1.0.0
↓
GitHub Actionsがタグ検知 → 本番デプロイ
こんなときに使う
- リリースのたびに 手動で明示的に本番反映したい
- バージョンを意識してデプロイ管理したい
- OSS開発などで「タグ = リリース」の明示が必要なとき
deploy.yml(タグトリガー)
on:
push:
tags:
- 'v*.*.*' # セマンティックバージョン(例: v1.0.0)
# docker build & deploy 処理を書く
パターン③:Smokeテスト付きの自動ロールバック
構成イメージ
main push → 自動デプロイ → 自動テスト(Smoke Test)
成功 → 完了
失敗 → ロールバック処理を実行
こんなときに使う
- リリース後すぐに 致命的なバグ検知を防ぎたい
- 「壊れた本番環境」を最短で回避したい
deploy.yml(Smoke Test + ロールバック)
jobs:
deploy:
steps:
- run: docker compose -f prod.yml up -d --build
smoke-test:
needs: deploy
steps:
- run: curl --fail http://localhost:8000/health || exit 1
rollback:
if: failure()
needs: smoke-test
steps:
- run: docker compose -f prod.yml down && docker compose -f prev.yml up -d
終わりに
CI/CDは一見ハードルが高く見えますが、GitHub ActionsとDocker Composeだけでもここまでできるという実感を得ました。
今回紹介したようなシンプルな構成から始めて、将来的にはSlack通知や本番環境との連携まで広げることもできます。