GitHub Actionsを用いたCI/CDの基礎

# はじめに：CI/CDの必要性
「ローカルでは動いたけど、本番で動かない」
そんな状況に陥ったことはありませんか？

CI/CD（継続的インテグレーション／継続的デリバリー）は、開発・テスト・デプロイの自動化を通じて、開発品質の向上とチームの効率化を実現する仕組みです。

今回紹介するのは、GitHub Actions × Docker Compose を使った最小構成のCI/CDフローです。

# 今回のCI/CDフロー（図解）
今回は試しに以下のようなフローを構築しようと思います。

```txt
① ローカル開発（別ブランチ）
     ↓
② 開発ブランチにpush（例: feature/login）
     ↓
③ CI（自動）：
   - Docker Composeでbuildできるか
   - pytestでテストが通るか
   - flake8でコード品質チェック
     ↓
④ Pull Request作成
     ↓
⑤ mainブランチにマージされたら自動でCD（デプロイ）
   - docker-compose.prod.yml を使って
   - 本番環境用イメージをBuild & 起動
```

この構成の特徴は、開発ブランチでCI、mainブランチでCDという明確な責任分離です。


# 実際に使用したコード
### 構成について
```txt
.
├── 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
```yml:.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
```
### 詳細解説
```yml
name: CI
```
- このワークフローの名前。GitHub Actions UIに表示される。

```yml
on:
  push:
    branches: ["*", "!main"]
  pull_request:
    branches: [main]
```
- push: どのブランチでもpush時にCIを実行（ただし main は除外）
- pull_request: main ブランチに対するPR作成時にもCIを実行

#### lintジョブ

```yml
jobs:
  lint:
    runs-on: ubuntu-latest
```
- GitHubリポジトリのソースコードをチェックアウト

```yml
      - run: pip install flake8
```
- 静的解析ツール flake8 をインストール

```yml
      - run: flake8 app tests
```
- app/とtests/に対してPythonコードスタイルのチェックを実行
    - 例：関数定義前に2行空いていない、未使用importがあるなどのエラーを検出

#### testジョブ（例）

```yml
  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
```yml: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
```
### 詳細解説

```yml
name: Deploy to Production
```
- デプロイ用ワークフローのタイトル。GitHub UIに表示される。


```yml
on:
  push:
    branches: [main]
```
- main ブランチに push されたときに発火
- → PRをマージした後、自動で実行される

### デプロイジョブの定義
```yml
jobs:
  deploy:
    runs-on: ubuntu-latest
```
- deploy というジョブ名で、Ubuntu上で動作


```yml
    steps:
      - uses: actions/checkout@v3
```
- ソースコード（mainブランチ）の最新状態を取得

```yml
      - 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通知や本番環境との連携まで広げることもできます。