Dockerから始めるECS完全自動化：ECR→ECS FargateをTerraformとGitHub Actionsで構築した話

## はじめに
TerraformとGitHub Actionsを使って、Dockerで作ったWordPressアプリケーションをECS（Fargate）に完全自動デプロイする構成を構築しました。
VPCやALB、CloudFront、Route53までTerraformでコード化し、GitHub ActionsのpushイベントをトリガーにDockerビルド＆ECRプッシュ→Terraform applyまでCI/CD化しています。

## 構成
![構成図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3812406/29e1d3d9-6835-4484-b784-972d6573d1ba.png)

#### ディレクトリ
```
├── .github/
│   └── workflows/
│       └── main.yml
│
├── terraform/
│   ├── main.tf
│   └── variables.tf                          
│
├── modules/                    
│   ├── vpc/                    
│   │   ├── main.tf             
│   │   ├── variables.tf        
│   │   └── outputs.tf          
│   ├── ecs/
│   │   ├── main.tf             
│   │   ├── variables.tf        
│   ├── rds/
│   │   ├── main.tf             
│   │   ├── variables.tf        
│   │   └── outputs.tf
│   ├── alb/
│   │   ├── main.tf             
│   │   ├── variables.tf        
│   │   └── outputs.tf
│   ├── cloudfront/
│   │   ├── main.tf             
│   │   ├── variables.tf        
│   │   └── outputs.tf
│   ├── route53/
│   │   ├── main.tf             
│   │   ├── variables.tf        
│   │   └── outputs.tf          
│
├── Dockerfile             
├── .gitignore               
└── README.md 
```

## 前提
- AWS CLIのインストール、クレデンシャル設定済み（ワークフローでECR URIを取得するため）
- Terraform.tfstateのためのS3バケット（バージョニング有効）を作成
- ECRリポジトリの作成
- GitHub secretsに下の認証情報を保存
①AWS_ACCESS_KEY_ID
②AWS_SECRET_ACCESS_KEY
③AWS_REGION
④DOCKERHUB_USERNAME
⑤DOCKERHUB_PASSWORD

## 各テンプレート
- terraform/main.tfのプロバイダは明示しておらず、Github ActionsワークフローではGitHubのsecretsから環境変数を渡しています
- ローカル実行する場合はAWS CLIで設定している認証情報が自動で使用されます
- 各テンプレートは以下を参照ください

https://github.com/ryt117/fargate-wp-terraform/tree/main

## ワークフロー
#### 要約
- GitHub上の変更検知 or 手動実行
- ソースコード取得
- AWS/ECR/DockerHub認証
- Dockerイメージビルド＆ECRプッシュ
- Terraform plan && apply

```
name: Deploy Terraform Configuration

 on: 
  push:
   workflow_dispatch:  # 手動トリガーを追加

jobs:
  deploy-terraform:
    name: Apply Terraform Configuration to AWS
    runs-on: ubuntu-latest

    env:
      AWS_REGION: ap-northeast-1
      ECR_REPO: wordpress  # ECRリポジトリ名 
      IMAGE_TAG: ${{ github.sha }}  # DockerイメージタグにlatestではなくコミットSHAを使う
    
    steps:

    - name: Checkout
      uses: actions/checkout@v4

    - name: Setup Terraform
      uses: hashicorp/setup-terraform@v2
      with:
        terraform_version: 1.4.0  # 使用するTerraformのバージョンを明示指定

    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v4
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}  # GitHub Secrets に設定されたAWS認証情報
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}  # GitHub Secrets に設定されたAWS認証情報
        aws-region: ${{ env.AWS_REGION }}  # 上で定義したリージョン

     # DockerHubにログイン
    - name: Login to DockerHub
      run: echo "${{ secrets.DOCKERHUB_PASSWORD }}" | docker login -u ${{ secrets.DOCKERHUB_USERNAME }} --password-stdin  

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

    # カレントディレクトリにあるDockerfileからイメージをビルド
    - name: Build Docker image
      run: |
        docker build -t $ECR_REPO:$IMAGE_TAG .

    - name: Tag & Push to ECR
      run: |
        ECR_URI=$(aws ecr describe-repositories --repository-names $ECR_REPO --query 'repositories[0].repositoryUri' --output text)
        docker tag $ECR_REPO:$IMAGE_TAG $ECR_URI:$IMAGE_TAG
        docker push $ECR_URI:$IMAGE_TAG

    - name: Initialize Terraform
      run: cd terraform && terraform init

    - name: Terraform Plan
      run: cd terraform && terraform plan -var="image_tag=${{ github.sha }}"

    - name: Terraform Apply
      run: cd terraform && terraform apply -auto-approve -var="image_tag=${{ github.sha }}"
```
#### ワークフローの結果 
![ワークフロー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3812406/d6630ea6-7175-43b6-8359-0c59f7d9feeb.png)

## テスト
####  ECS タスクの起動確認
- 今回はタスクを1つで起動しているので正常 
![タスク正常稼働.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3812406/8ee8efd7-e6dc-44be-89ba-3aef5ec64b64.png)

#### LBターゲット確認
![タスクのプライベートIP.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3812406/9bb3de2d-00e6-498d-bc64-113e2edf3ab8.png)

#### レスポンス確認
![20250405_000000.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3812406/12f8033e-cf42-4a16-b9ea-891200505939.jpeg)

| ヘッダ | 解説 |
|:-:|:-:|
| HTTP/1.1 302 Found  | リダイレクト  |
| Location:ttps://hogehoge.click/wp-admin/install.php  | リダイレクト先  |
|  Server: Apache/2.4.62 (Debian) |  Webサーバ情報 |
|  X-Powered-By: PHP/8.1.32 | PHPバージョン  |
|  Cache-Control: no-cache, must-revalidate, max-age=0 | CloudFrontはCathing Disabledのため正常  |
| X-Redirect-By: WordPress  | 	リダイレクトの実行元  |
|  X-Cache: Miss from cloudfront | キャッシュされておらず、オリジンサーバー（ALB→ECS）から取り出している  |
|  Via: 1.1 ...cloudfront.net (CloudFront) | CloudFront経由である  |

#### WordPress
![wpインストール画面.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3812406/a200b29c-dda5-47bf-b675-488033227375.png)