🙌

小規模プロジェクトにおける効率的デプロイ戦略:クラウドサービスへの自動デプロイ

2025/03/07に公開

小規模プロジェクトにおける効率的デプロイ戦略:クラウドサービスへの自動デプロイ

はじめに

小規模開発プロジェクトでは、開発リソースが限られているからこそ、効率的な開発フローの構築が重要です。特に継続的デプロイ(CD)の自動化は、開発サイクルを加速させる重要な要素です。

私は最近、隙間時間に役立つタスク提案アプリ「rest_app」の開発において、GitHub ActionsとVercelの連携について学びがあったので共有します。この記事は特に「これから小規模開発のプロジェクトの技術検証や本番開発に携わりたい方」に向けた内容です。

大前提、vercelにデプロイ済みのアプリが「rest_app」であり、今回github actionsを用いて自動的にブランチの変更を読み取りデプロイできないかを試しているということです。

プロジェクト概要と技術スタック

「rest_app」は、ユーザーの隙間時間に最適な活動を提案するWebアプリケーションです。

使用技術スタック:

  • フロントエンド: Next.js
  • バックエンド: FastAPI
  • データベース: Supabase
  • 認証: Supabaseの認証機能
  • デプロイ先: Vercel

GitHub Actionsによる自動デプロイ設定:私の奮闘記

最初の目標は、コードの変更をGitHubにプッシュしたらVercelに自動デプロイされる環境を構築することでした。GitHub Actionsを使って自動化しようと考えました。

1. vercel.jsonの作成

まず、プロジェクトのルートディレクトリにvercel.jsonファイルを作成しました:

{
  "version": 2,
  "buildCommand": "next build",
  "outputDirectory": ".next",
  "framework": "nextjs",
  "cleanUrls": true
}

この設定ファイルはVercelにプロジェクトの構成を伝えるもので、ビルドコマンドや出力ディレクトリなどを指定します。

2. GitHub Actionsのワークフロー設定

次に、GitHub Actionsのワークフローを設定するために、.github/workflows/deploy.ymlファイルを作成しようとしました。ここで最初の壁にぶつかりました。

問題点1: ディレクトリ構造の作成

GitHubのWeb UIを使ってファイルを直接作成しようとしましたが、ネストされたディレクトリ構造(.github/workflows/)を一度に作成するのが難しいことに気づきました。

解決策:
最終的には、次のステップで対応しました:

  1. リポジトリのルートに.githubディレクトリを作成
  2. その中にworkflowsディレクトリを作成
  3. 最後にdeploy.ymlファイルを追加

deploy.ymlの内容と詳細解説:

name: Deploy to Vercel

on:
  push:
    branches:
      - rest_app  # デフォルトブランチへのプッシュ時に実行
  pull_request:
    branches:
      - rest_app  # プルリクエスト時にもプレビューデプロイを実行

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Install Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Install Vercel CLI
        run: npm install -g vercel
        
      - name: Deploy to Vercel
        if: github.event_name == 'push'
        run: vercel deploy --prod --token=${{ secrets.VERCEL_TOKEN }} --yes
        
      - name: Preview Deploy to Vercel
        if: github.event_name == 'pull_request'
        run: vercel deploy --token=${{ secrets.VERCEL_TOKEN }} --yes

ワークフローの各セクション解説:

  • name: Deploy to Vercel: このワークフローの名前を定義。GitHubのActionsタブに表示される名前です。

  • on: ワークフローのトリガー条件を定義します:

    • push: rest_appブランチにコードがプッシュされた時にワークフローを実行
    • pull_request: rest_appブランチへのプルリクエスト時にもワークフローを実行
  • jobs: 実行するジョブを定義します:

    • deploy: ジョブの名前
    • runs-on: ubuntu-latest: 最新のUbuntu環境でジョブを実行
  • steps: ジョブ内の具体的な実行手順:

    • uses: actions/checkout@v3: リポジトリのコードをチェックアウト
    • uses: actions/setup-node@v3: Node.js環境をセットアップ(バージョン18を指定)
    • run: npm ci: パッケージの依存関係をインストール(npm installよりも速いらしい…)
    • run: npm install -g vercel: Vercel CLIをグローバルにインストール
    • 条件分岐:
      • プッシュイベントの場合: 本番環境へデプロイ(--prodフラグ付き)
      • プルリクエストの場合: プレビュー環境へデプロイ(--prodフラグなし)
    • --token=${{ secrets.VERCEL_TOKEN }}: GitHubシークレットからVercelトークンを使用
    • --yes: 全てのプロンプトに自動的に「yes」と応答

3. Vercelトークンの取得と設定

GitHub Actionsからデプロイするには、Vercelのアクセストークンが必要です。

  1. Vercelダッシュボードにログイン
  2. 設定 > Tokens から新しいトークンを作成
    自分のvercelアカウントの右上のAccount settingの上部のsettingタブのTokenを選択

GitHub acitonというな名前で権限を与える

出てきたトークンをコピー

  1. GitHubリポジトリの「Settings」>「Secrets and variables」>「Actions」で新しいシークレットを作成
    • 名前: VERCEL_TOKEN
    • 値: 先ほど取得したトークン

GitHubに戻りデプロイを行いプロジェクトの上部のsettingタブのsercret and variablesを選択してトークンをペースト

意外な発見:Vercelの自動デプロイ機能

苦労してGitHub Actionsの設定を完了し、プッシュしたところ、予想外の出来事が起きました。Vercelのダッシュボードを確認すると、GitHub Actionsがトリガーされる前に、すでにデプロイが完了していたのです!

発見: Vercelには標準で自動デプロイ機能が組み込まれていました。GitHub Actionsによる複雑な設定をしなくても、GitHubのリポジトリと連携するだけで自動デプロイが可能だったのです。

これはエンジニアとしての教訓的な瞬間でした。時々、私たちは複雑な解決策を追求しすぎて、すでに存在するシンプルな機能を見落としてしまうことがあります。

デプロイの確認方法

しかし、GitHub Actionsとの二重デプロイが発生したため、どちらのデプロイが成功したのか判断するのに混乱しました。

Vercelのデプロイ確認

Vercelダッシュボードの「Deployments」タブで、デプロイの「Source」列を確認:

  • 「Git」と表示 → Vercelの自動デプロイ

  • 「CLI」と表示 → GitHub Actionsからのデプロイ

GitHub Actionsの確認

GitHubリポジトリの「Actions」タブで各ワークフローの実行結果を確認できます。緑のチェックマークが表示されていれば成功です。

最終的に両方のデプロイが成功していましたが、タイムスタンプを見るとVercelの自動デプロイの方が先に完了していました。

Vercelの自動デプロイのトリガー条件と制限

Vercelの自動デプロイには以下のような特徴があります:

トリガー条件

  • デフォルトブランチ(main/rest_appなど)へのプッシュで自動デプロイ
  • プルリクエスト作成時に自動的にプレビューデプロイが生成
  • GitHub、GitLab、Bitbucketとの連携でWebhookを介して通知

制限事項

  • 無料プランではデプロイ数に制限(100デプロイ/日)
  • ビルド時間制限(通常45分)
  • 環境変数はVercelダッシュボードで別途設定が必要
  • カスタムビルドステップは限定的
  • 自動ロールバック機能は標準では提供されていない
  • 承認ワークフローや段階的なデプロイなどの高度な機能は制限あり

GitHub Actionsの応用と発展:様々なクラウドサービスへのデプロイ

GitHub Actionsの大きな魅力は、様々なクラウドプロバイダーに対応できる汎用性です。ここでは主要なクラウドサービスへのコンテナベースデプロイの例を紹介します。

Azure: GitHub → Azure Container Registry → Azure Web Apps

name: Deploy to Azure Web App via ACR

on:
  push:
    branches:
      - main

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Azure Login
      uses: azure/login@v1
      with:
        creds: ${{ secrets.AZURE_CREDENTIALS }}
    
    - name: Build and push Docker image to ACR
      uses: azure/docker-login@v1
      with:
        login-server: ${{ secrets.ACR_LOGIN_SERVER }}
        username: ${{ secrets.ACR_USERNAME }}
        password: ${{ secrets.ACR_PASSWORD }}
    
    - run: |
        docker build -t ${{ secrets.ACR_LOGIN_SERVER }}/myapp:${{ github.sha }} .
        docker push ${{ secrets.ACR_LOGIN_SERVER }}/myapp:${{ github.sha }}
    
    - name: Deploy to Azure Web App
      uses: azure/webapps-deploy@v2
      with:
        app-name: 'your-app-name'
        images: '${{ secrets.ACR_LOGIN_SERVER }}/myapp:${{ github.sha }}'

AWS: GitHub → Amazon ECR → AWS App Runner

name: Deploy to AWS App Runner via ECR

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v2
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        aws-region: us-east-1
    
    - name: Login to Amazon ECR
      id: login-ecr
      uses: aws-actions/amazon-ecr-login@v1
    
    - name: Build and push Docker image
      env:
        ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
        ECR_REPOSITORY: my-app-repo
        IMAGE_TAG: ${{ github.sha }}
      run: |
        docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
        docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
    
    - name: Deploy to AWS App Runner
      run: |
        aws apprunner create-service --cli-input-json '{
          "ServiceName": "my-app-service",
          "SourceConfiguration": {
            "AuthenticationConfiguration": {
              "AccessRoleArn": "${{ secrets.AWS_APP_RUNNER_ROLE_ARN }}"
            },
            "AutoDeploymentsEnabled": true,
            "ImageRepository": {
              "ImageIdentifier": "${{ steps.login-ecr.outputs.registry }}/my-app-repo:${{ github.sha }}",
              "ImageRepositoryType": "ECR",
              "ImageConfiguration": {
                "Port": "8080"
              }
            }
          },
          "InstanceConfiguration": {
            "Cpu": "1 vCPU",
            "Memory": "2 GB"
          }
        }'

GCP: GitHub → Google Container Registry → Cloud Run

name: Deploy to Google Cloud Run via GCR

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Setup Google Cloud SDK
      uses: google-github-actions/setup-gcloud@v1
      with:
        project_id: ${{ secrets.GCP_PROJECT_ID }}
        service_account_key: ${{ secrets.GCP_SA_KEY }}
        export_default_credentials: true
    
    - name: Authorize Docker push to Container Registry
      run: gcloud auth configure-docker
    
    - name: Build and push Docker image
      env:
        GCR_HOSTNAME: gcr.io
        PROJECT_ID: ${{ secrets.GCP_PROJECT_ID }}
        IMAGE_NAME: my-app
        IMAGE_TAG: ${{ github.sha }}
      run: |
        docker build -t $GCR_HOSTNAME/$PROJECT_ID/$IMAGE_NAME:$IMAGE_TAG .
        docker push $GCR_HOSTNAME/$PROJECT_ID/$IMAGE_NAME:$IMAGE_TAG
    
    - name: Deploy to Cloud Run
      run: |
        gcloud run deploy my-app \
          --image gcr.io/${{ secrets.GCP_PROJECT_ID }}/my-app:${{ github.sha }} \
          --platform managed \
          --region us-central1 \
          --allow-unauthenticated

コンテナベースデプロイと直接デプロイの比較

クラウドサービスへのデプロイには、大きく分けて2つのアプローチがあります:

直接デプロイ(コードベース)のメリット

  • シンプルなセットアップ: コンテナ化の知識が不要
  • 迅速な開始: 少ない設定で短時間でパイプラインを構築可能
  • 低い学習曲線: Dockerの知識がなくても実装可能
  • デプロイが高速: 通常、コンテナのビルドより直接コードデプロイの方が速い
  • リソース消費が少ない: コンテナレジストリの維持が不要でコスト削減

コンテナ経由デプロイのメリット

  • 環境の一貫性: 開発環境と本番環境の差異を最小化
  • 移植性の高さ: 様々なクラウドプラットフォームへの移行が容易
  • 依存関係の封じ込め: バージョン競合のリスクが低減
  • ロールバックが容易: 以前のイメージに即座に戻せる
  • マイクロサービスとの親和性: 複数コンテナの組み合わせに適している
  • CI/CDパイプラインの一貫性: 様々な環境へのデプロイプロセスが統一

どちらの方法を選ぶべきか

小規模開発プロジェクト、特にITコンサルタントとして短期間で成果を出す必要がある場合は、サービス固有の自動デプロイ機能(Vercelなど)で十分なケースが多いです。

しかし、以下のような場合はGitHub Actionsを使ったカスタムデプロイが有用です:

  • テスト実行やその他のチェックを含む複雑なCIパイプラインが必要
  • クラウドサービスの自動デプロイ機能が要件を満たさない
  • 複数のクラウドプロバイダーを使用している場合(マルチクラウド戦略)
  • デプロイ前後に特別な処理が必要
  • 複数の環境へのデプロイフローを統一的に管理したい

特にコンテナベースのアプローチは、アプリケーションの規模が大きくなる、または将来的にクラウドプロバイダーを変更する可能性がある場合に、長期的な柔軟性を提供します。

まとめ

小規模プロジェクトでのデプロイ自動化において、私はGitHub Actionsという「大砲」で「小さな鳥」を狙っていたことに気づきました。Vercelのような特化型プラットフォームでは、標準機能だけで十分だった場面で複雑な設定に時間を費やしてしまいました。

とはいえ、この経験は無駄ではありませんでした。GitHub Actionsの設定方法を学ぶことで、将来的により複雑なプロジェクトや、他のクラウドサービスを利用する際にも応用できる知識を得ることができました。

小規模開発に携わる方々へのアドバイスとしては:

  1. まずはプラットフォームの標準機能を理解する
  2. 本当に追加の自動化が必要かを見極める
  3. 将来の拡張性を考慮し、適切なデプロイ戦略を選択する

時には「シンプルさ」こそが最良の解決策である一方、将来の成長に備えた拡張性も重要です。プロジェクトの要件とチームのスキルセットに合わせて、最適なアプローチを選択しましょう。

Discussion