コンテンツにスキップ
LinkedInX

デプロイとCI/CD

約10分

対象読者: アプリケーションのコンテナ化とCI/CD構築を学びたいエンジニア、自動デプロイを整備したい方
前提知識: クラウドアーキテクチャ入門の全体構成を把握していると理解が深まります

デプロイ(Deploy)とは、開発したアプリケーションを本番環境で動作させるプロセスです。「自分のMacでは動くのに本番で動かない」という問題はコンテナで解決できます。CI/CD(Continuous Integration / Continuous Deployment)とは、コードの変更を自動でテスト・ビルド・デプロイするパイプラインです。CI/CDを整備すると、手作業によるデプロイミスを排除し、1日何度でも安全にリリースできるようになります。

なぜデプロイプロセスを整備するのか

Section titled “なぜデプロイプロセスを整備するのか”

手作業によるデプロイには次の問題があります。

  • 開発環境と本番環境の差異(OS・ライブラリバージョン)による動作不一致
  • 手順書のミスや手順の属人化
  • デプロイのたびにサービス停止が発生する
  • どのバージョンが本番で動いているか追跡できない

コンテナとCI/CDはこれらをまとめて解決します。

コンテナとは、アプリケーションとその依存関係(ライブラリ、ランタイム、設定ファイル)をひとまとめにした隔離された実行環境です。コンテナは「どのサーバーでも同じように動く」を保証します。

Docker はコンテナを作成・実行する最も広く使われるツールです。

比較項目仮想マシン(VM)コンテナ
起動時間数分数秒
サイズ数GB(OS込み)数十〜数百MB
リソース効率低(OS全体を実行)高(ホストOSのカーネルを共有)
隔離レベル強(完全なOS分離)中(プロセスレベルの分離)
移植性低(VMイメージは重い)高(コンテナイメージはどこでも動く)

Dockerfile とは、コンテナイメージの作成手順を記述するテキストファイルです。

# Python FastAPI アプリの Dockerfile 例
FROM python:3.12-slim          # ベースイメージ(Python 3.12 の軽量版)

WORKDIR /app                   # コンテナ内の作業ディレクトリ

COPY requirements.txt .        # 依存関係ファイルをコピー
RUN pip install --no-cache-dir -r requirements.txt  # ライブラリをインストール

COPY . .                       # アプリのソースコードをコピー

EXPOSE 8000                    # コンテナが公開するポート番号

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
# イメージをビルド
docker build -t my-ai-app:latest .

# コンテナを起動(ローカル確認用)
docker run -p 8000:8000 --env-file .env my-ai-app:latest

コンテナレジストリとは、Dockerイメージを保存・配布するサービスです。GitHubがコードを管理するように、レジストリはコンテナイメージを管理します。

レジストリ特徴
Docker Hub最も有名。パブリックイメージが豊富
Amazon ECRAWSとの統合が容易。ECS/EKSとセットで使う
GitHub Container RegistryGitHub Actionsとの連携が簡単
Google Artifact RegistryGCP環境に統合

CI(継続的インテグレーション)とCD(継続的デプロイ)

Section titled “CI(継続的インテグレーション)とCD(継続的デプロイ)”
  • CI(Continuous Integration): コードをプッシュするたびに自動でリント・テスト・ビルドを実行
  • CD(Continuous Deployment): テストが通ったコードを自動でステージング・本番環境にデプロイ

GitHub Actions によるCI/CDパイプライン

Section titled “GitHub Actions によるCI/CDパイプライン”
graph LR
    Push["開発者がプッシュ\n(feature → main)"] --> CI["GitHub Actions\nCI ジョブ"]

    CI --> Lint["① Lint チェック\n(flake8 / eslint)"]
    Lint --> Test["② テスト実行\n(pytest / jest)"]
    Test --> Build["③ Docker イメージ\nビルド"]
    Build --> Push2["④ レジストリに\nイメージプッシュ"]

    Push2 --> DeployStaging["⑤ ステージング環境\nへデプロイ"]
    DeployStaging --> SmokeTest["⑥ スモークテスト\n(基本動作確認)"]

    SmokeTest -->|合格| DeployProd["⑦ 本番環境\nへデプロイ"]
    SmokeTest -->|不合格| Rollback["自動ロールバック"]

    DeployProd --> Notify["⑧ Slack通知\n(デプロイ完了)"]
# .github/workflows/deploy.yml
name: CI/CD Pipeline

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.12"

      - name: Install dependencies
        run: pip install -r requirements.txt

      - name: Run linter
        run: flake8 src/

      - name: Run tests
        run: pytest tests/ -v
        env:
          DATABASE_URL: ${{ secrets.TEST_DATABASE_URL }}

  build-and-deploy:
    needs: test                  # testジョブが成功した場合のみ実行
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'  # mainブランチへのpushのみ

    steps:
      - uses: actions/checkout@v4

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ap-northeast-1

      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v2

      - name: Build and push Docker image
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          IMAGE_TAG: ${{ github.sha }}
        run: |
          docker build -t $ECR_REGISTRY/my-ai-app:$IMAGE_TAG .
          docker push $ECR_REGISTRY/my-ai-app:$IMAGE_TAG

      - name: Deploy to ECS
        run: |
          aws ecs update-service \
            --cluster production \
            --service ai-chat-service \
            --force-new-deployment

本番AIサービスは通常3つの環境を用意します。

development(開発環境)
  ↓ プルリクエスト作成
staging(ステージング環境)
  ↓ mainへのマージ後、動作確認
production(本番環境)
環境目的インフラ規模
development開発者のローカルまたは個人の開発サーバー最小
staging本番に近い構成での動作確認本番の縮小版
production実際のユーザーが使う環境フルスケール

サービス停止を最小化しながらデプロイするための戦略が3つあります。

戦略仕組み停止時間ロールバック速度複雑さ
ローリングアップデート旧インスタンスを1台ずつ新バージョンに入れ替えなし
ブルーグリーン旧環境(Blue)を残したまま新環境(Green)を用意し、トラフィックを一括切り替えなし速い(即切り戻し可)
カナリアリリーストラフィックの一部(例: 5%)だけ新バージョンに流し、問題なければ段階的に拡大なし速い

ブルーグリーンデプロイのイメージ

Section titled “ブルーグリーンデプロイのイメージ”
デプロイ前:
  トラフィック 100% → [Blue環境: v1.2]

デプロイ中:
  [Green環境: v1.3] を起動してテスト
  トラフィック 100% → [Blue環境: v1.2]

切り替え後:
  トラフィック 100% → [Green環境: v1.3]
  [Blue環境: v1.2] は一定期間保持(ロールバック用)

コンテナオーケストレーション: Kubernetes

Section titled “コンテナオーケストレーション: Kubernetes”

Kubernetes(K8s)とは、大量のコンテナを自動で管理・スケールするオーケストレーションシステムです。「コンテナが落ちたら自動で再起動」「負荷が増えたら自動でスケールアウト」といった運用を自動化します。

Kubernetesの主要概念:

概念役割
Pod1つ以上のコンテナをまとめた最小デプロイ単位
DeploymentPodの望ましい状態(レプリカ数等)を定義
ServicePodへのネットワークアクセスを提供するロードバランサー
Ingress外部からのHTTPSトラフィックをServiceにルーティング
ConfigMap / Secret設定値・シークレットを管理

Kubernetesは強力ですが複雑です。小〜中規模のAIサービスではまずマネージドサービス(Cloud Run、Fargate、Railway)から始めることを推奨します。

サービス特徴向くケース
AWS ECS / Fargateサーバーレスコンテナ実行。Kubernetesより簡単AWSで本番運用
Google Cloud RunHTTPリクエストに応じて自動スケールGCPでステートレスAPI
RailwayDockerfileがあれば数分でデプロイ可能個人・スタートアップ
RenderGitHubと連携した簡単デプロイ個人・スタートアップ
Heroku最も手軽なPaaS小規模 / プロトタイプ
Kubernetes(EKS/GKE)大規模・複雑な要件大規模本番

APIキーやDBパスワードをソースコードに直接書いてはいけません。

# ローカル開発: .env ファイルに記述し、.gitignore に追加
ANTHROPIC_API_KEY=sk-ant-xxxxx
DATABASE_URL=postgresql://user:pass@localhost:5432/mydb
REDIS_URL=redis://localhost:6379

# .gitignore
.env

本番環境では、.env ファイルをサーバーに置く代わりに専用のシークレット管理サービスを使います。

サービス説明
AWS Secrets Managerシークレットをローテーション含めて管理
AWS SSM Parameter Storeシンプルなキーバリュー形式のシークレット
Google Secret ManagerGCPのシークレット管理
GitHub Actions SecretsCIパイプライン内でのみ使うシークレット
Vault (HashiCorp)クラウド非依存のOSSシークレット管理
  • コンテナ(Docker)は「どの環境でも同じように動く」を保証し、「自分のMacでは動く」問題を解決する
  • Dockerfile でアプリと依存関係をパッケージ化し、コンテナレジストリで配布する
  • GitHub ActionsでCI/CDを構築し、テスト→ビルド→デプロイを自動化する
  • ローリング・ブルーグリーン・カナリアの3戦略でダウンタイムなしのデプロイが可能
  • Kubernetesは本格的なコンテナオーケストレーションだが、まずCloud Run / Fargate / Railwayで始めるのが現実的
  • シークレットはソースコードに入れず、必ずシークレット管理サービスを使う

Q: Kubernetesは必要ですか?

A: 多くの場合、最初は不要です。AWS ECS/Fargate、Google Cloud Run、Railway などのマネージドサービスで自動スケーリングや自動再起動は実現できます。Kubernetesが必要になるのは、カスタムスケーリングロジック、複雑なネットワーク設定、大規模な本番運用が必要になったときです。

Q: デプロイが失敗したときにロールバックするには?

A: ブルーグリーンデプロイなら旧環境へのトラフィックを即時切り戻すだけです。ローリングアップデートの場合は、前のコンテナイメージタグを指定して再デプロイします。GitHub Actionsワークフローに「前のSHAに戻す」ジョブを用意しておくか、ECS/Cloud Runのコンソールから前のリビジョンに切り戻せます。

Q: ステージング環境は必ず必要ですか?

A: 本番ユーザーがいるサービスでは強く推奨します。ステージングなしで本番に直接デプロイすると、動作確認の機会がなく障害発生時の影響範囲が大きくなります。費用を抑えるなら、ステージングはより小さなインスタンスサイズにして最低限の構成にすることで十分です。

Q: GitHub Actions 以外のCI/CDツールはありますか?

A: GitLab CI/CD(GitLab利用者向け)、CircleCI(歴史あるクラウドCI)、Jenkins(OSS・オンプレでの細かい制御)などがあります。GitHubを使っているなら GitHub Actions が最も統合が簡単で、パブリックリポジトリなら無料枠があります。

このページの外部仕様・背景情報は、参考文献を参照してください。[1][2][3][4][5]

  1. Docker Documentation
  2. GitHub Actions Documentation
  3. Kubernetes Documentation
  4. AWS ECS Developer Guide
  5. Google Cloud Run Documentation
クイズ