Rails APIのFargateデプロイメント：GitHub Actionsによる自動CI/CDパイプライン構築 [ 備忘録 ] 

# 前回のQiita
https://qiita.com/oyne2561/items/b4190d00346f4e27430b

# リポジトリ
https://github.com/oyne2561/aws_deploy_rails7.2.2/tree/main

# 解説
こちらにも記載しています。
https://github.com/oyne2561/aws_deploy_rails7.2.2/blob/main/READ_GITHUB_ACTIONS.md

## Github ActionsのSercetsを作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4077353/f6fa441c-f6a9-4a6f-bf3a-c248fbaa46b6.png)

1. `AWS_ACCESS_KEY_ID`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4077353/8134b687-3849-4e87-926e-9a01f16de3f7.png)

2. `AWS_SECRET_ACCESS_KEY`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4077353/d890bdb2-997a-4b5f-a6c4-fa47b36a0c76.png)


## 実際の`.github/workflows/cicd.yml`
```yml
# ワークフローの名前を設定
name: CI/CD Pipeline

# このワークフローがいつ実行されるかを定義
on: 
  push:
    branches: [ main, develop ]  # mainまたはdevelopブランチにプッシュされた時

# 環境変数の定義（全てのジョブで使用可能）
env:
  AWS_REGION: ap-northeast-1                 # AWSリージョン（東京）
  ECR_REPOSITORY: todo-app-api               # ECR（Docker画像保存場所）のリポジトリ名
  ECS_SERVICE: todo-app-api-service          # ECS（コンテナ実行環境）のサービス名
  ECS_CLUSTER: todo-app-api-cluster          # ECSクラスター名
  ECS_TASK_DEFINITION: todo-app-api-task     # ECSタスク定義名

# ジョブの定義（並列または順次実行される処理の単位）
jobs:
  # 1つ目のジョブ：テスト実行
  test:
    runs-on: ubuntu-latest  # Ubuntu最新版の仮想マシンで実行

    # ステップの定義（順次実行される処理）
    steps:
    # Step 1: ソースコードをチェックアウト（取得）
    - name: Checkout code
      uses: actions/checkout@v4

    # Step 2: Ruby環境のセットアップ
    - name: Set up Ruby
      uses: ruby/setup-ruby@v1
      with:
        ruby-version: 3.2.0    # Ruby 3.2.0を使用
        bundler-cache: true   # キャッシュを無効化して手動でbundle install

    # Step 3: コード品質チェック（RuboCop）
    - name: Run RuboCop (Lint)
      run: bundle exec rubocop

  # 2つ目のジョブ：デプロイ実行
  deploy:
    needs: test  # testジョブが成功した場合のみ実行
    runs-on: ubuntu-latest
    # mainブランチへのプッシュの場合のみ実行
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'

    steps:
    # Step 1: ソースコードをチェックアウト
    - name: Checkout code
      uses: actions/checkout@v4

    # Step 2: AWS認証情報の設定
    - 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: ${{ env.AWS_REGION }}

    # Step 3: Amazon ECRにログイン
    - name: Login to Amazon ECR
      id: login-ecr
      uses: aws-actions/amazon-ecr-login@v2

    # Step 4: Docker ImageをビルドしてECRにプッシュ
    - name: Build, tag, and push image to Amazon ECR
      id: build-image
      env:
        ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
        IMAGE_TAG: ${{ github.sha }}  # GitコミットのSHAをタグとして使用
      run: |
        # Docker画像をビルドしてECRにプッシュ
        docker build -f Dockerfile.prod -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
        docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
        # latestタグも付けてプッシュ
        docker tag $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG $ECR_REGISTRY/$ECR_REPOSITORY:latest
        docker push $ECR_REGISTRY/$ECR_REPOSITORY:latest
        # 次のステップで使用するため、画像URIを出力
        echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT

    # Step 5: 現在のECSタスク定義をダウンロード
    - name: Download task definition
      run: |
        aws ecs describe-task-definition --task-definition $ECS_TASK_DEFINITION \
          --query taskDefinition > task-definition.json
    
    # Step 5.5: タスク定義のアーキテクチャをX86_64に修正
    - name: Fix task definition architecture
      run: |
        # JSONファイルでcpuArchitectureをX86_64に変更
        jq '.runtimePlatform.cpuArchitecture = "X86_64"' task-definition.json > task-definition-fixed.json
        # 修正されたファイルに置き換え
        mv task-definition-fixed.json task-definition.json
        
        # 確認のため修正内容を表示
        echo "=== 修正されたruntimePlatform ==="
        jq '.runtimePlatform' task-definition.json

    # Step 6: タスク定義に新しいDocker Imageを設定
    - name: Fill in the new image ID in the Amazon ECS task definition
      id: task-def
      uses: aws-actions/amazon-ecs-render-task-definition@v1
      with:
        task-definition: task-definition.json
        container-name: todo-app-api
        image: ${{ steps.build-image.outputs.image }}

    # Step 7: ECSサービスに新しいタスク定義をデプロイ
    - name: Deploy Amazon ECS task definition
      uses: aws-actions/amazon-ecs-deploy-task-definition@v1
      with:
        task-definition: ${{ steps.task-def.outputs.task-definition }}
        service: ${{ env.ECS_SERVICE }}
        cluster: ${{ env.ECS_CLUSTER }}
        wait-for-service-stability: true  # サービスが安定するまで待機
```

# GitHub Actionsを実装時に遭遇したエラーの解決

## 問題の概要

GitHub Actions での Docker デプロイ時に、ローカル開発環境（Apple Silicon/ARM64）とデプロイ先（Intel/AMD x86_64）のアーキテクチャが異なることで発生するエラーについて解説します。

## エラーの原因

GitHub Actions のランナーや ECS などのクラウド環境は通常 x86_64 アーキテクチャを使用しますが、ローカルの Apple Silicon Mac では ARM64（aarch64）アーキテクチャを使用します。この不整合により、以下のような問題が発生します：

- Bundle install 時のプラットフォーム不整合
- Docker イメージの実行時エラー
- ECS タスクの起動失敗

## 解決手順

### 1. Gemfile.lock のプラットフォーム追加

まず、ローカル環境で `Gemfile.lock` に x86_64-linux プラットフォームを追加します。

```bash
bundle lock --add-platform x86_64-linux
```

**実行後の `Gemfile.lock`**
```
PLATFORMS
  aarch64-linux   # ARM64（既存）
  x86_64-linux    # Intel/AMD 64bit（新規追加）
```

### 2. Dockerfile の修正

Docker ビルド時の bundle コマンドの順序が重要です。deployment モードを有効にする前にプラットフォームを追加する必要があります。

```dockerfile
# ❌ 間違った順序（deploymentモードが先だとlockファイル変更不可）
RUN bundle config set --local deployment 'true' && \
    bundle lock --add-platform x86_64-linux  # エラー！

# ✅ 正しい順序
RUN bundle config set --local without 'development test' && \
    bundle lock --add-platform x86_64-linux && \  # 先にプラットフォーム追加
    bundle config set --local deployment 'true' && \  # 後でdeploymentモード
    bundle install --jobs 4
```

**重要なポイント：**
- `deployment` モードではロックファイルの変更が禁止される
- プラットフォーム追加は `deployment` モードを有効にする前に実行する
- `without` オプションで不要な gem グループを除外してビルド時間を短縮

### 3. ECS タスク定義のアーキテクチャ修正

GitHub Actions の CI/CD パイプラインで、ECS タスク定義のアーキテクチャを動的に修正します。

```yaml
# cicd.yml の一部
- name: Fix task definition architecture
  run: |
    # JSONファイルでcpuArchitectureをX86_64に変更
    jq '.runtimePlatform.cpuArchitecture = "X86_64"' task-definition.json > task-definition-fixed.json
    # 修正されたファイルに置き換え
    mv task-definition-fixed.json task-definition.json
    
    # 確認のため修正内容を表示
    echo "=== 修正されたruntimePlatform ==="
    jq '.runtimePlatform' task-definition.json
```
## 注意点

- ローカルでの `bundle lock --add-platform` 実行後は、必ず `Gemfile.lock` をコミットする
- ECS 以外のデプロイ先でも同様のアーキテクチャ指定が必要な場合がある
- Docker のマルチプラットフォームビルドを使用する場合は、別途 `docker buildx` の設定が必要

## まとめ

Apple Silicon Mac での開発が一般的になった現在、アーキテクチャ不整合は頻繁に発生する問題です。適切な順序でのプラットフォーム設定と、CI/CD パイプラインでの動的な修正により、スムーズなデプロイが実現できます。


# 証跡

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4077353/aec50e69-065f-45b1-bf72-a503ef0773c7.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4077353/b76a324a-8dcf-470a-bf06-cb07e8e76c0f.png)