40分でAWS ECS on Fargateチュートリアル

## はじめに
AWSのFargateを使ってみたいけど、何から始めていいかわからない...という方へ。
一緒に超シンプルな構成を試して「これがFargateか😃」を体感してみましょう。

## 書こうと思ったきっかけ
この記事は、インフラ・AWS初学者がFargateを理解して使えるようになるための学びの記録です。
初回は下記の記事です。ECRとECS、Fargateの概要説明とCloudShellを使って10分でECRにDockerイメージをプッシュする方法をまとめました。(多分３分で読めます）

https://qiita.com/yuzu_ponz007/items/b3cb75da2a1ee6a7ca0c


今回はちょっとステップアップして、GitHubActionsを使ってECRにイメージをプッシュする方法も併せてご紹介します。


:::note info
この記事一本で、シンプルなAWS ECS on Fargateを体験できます
:::


## ECS on Fargate とは？
**Fargateっていうリソース環境（エンジンやメモリがある場所）でコンテナ管理するECSだよ**、というやつです。
コンテナとは「アプリケーションを動かすために必要なものの集まり」と思ってください。
リソース環境がEC2になると「ECS on EC2」と呼びます。
Fargate と EC2の違いは「人間がコンテナを動かすためのサーバーを管理しないといけないのか」という点です。
Fargateは、AWSが自動でサーバーを用意してくれるので管理不要です。

ECSは、例えると「牧場」です。
広い草原がある環境（Fargate）で牛（コンテナ）を管理しています。
ということで次は ECS の構成を理解していきます。

## ECSの構成要素
ECSは、クラスター、サービス、タスク定義・タスクで構成されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3999989/f63f8e8a-6b43-4835-8e8a-7ca05188e579.png)
引用画像：https://zenn.dev/issy/articles/zenn-ecs-overview

### ① クラスター (Cluster)
サービスやタスクを管理する 「まとめ役」です。

### ② サービス (Service)
タスクを管理する　「実行役」です。

### ③④ タスクとタスク定義 (Task Definition)
タスク定義は「設計図」です。
タスク定義をもとに「起動させるコンテナの集まり」をタスクといいます。


あるルールに従う構成員、その上に実行役、さらに上にまとめ役がいる...
まるで何かの組織図のようですね。


:::note info
ECSの構成　→　組織図
クラスター　→ 親玉
サービス　  → 実行役（構成員をまとめるリーダー）
タスク定義　→ 構成員のためのルール・仕組み
タスク　   → 構成員たち
:::


# やってみよう
今回は簡単に、パブリックサブネットにECSを構築して１つのタスクを実行します。
タスクに割り振られたIPアドレスでアクセスするまでがゴールです。

下記の手順で進めます。
1. GitHubActionsを使って、ECRにDockerイメージをプッシュ
2. CloudFormation（コードでインフラ構築できるツール）を使ってECSを構築
3. タスクに割り振られたIPアドレスでアクセスし「これがFargateか😃」を体感する
4. GUIでクラスターを作ってみる
5. もう一回タスクに割り振られたIPアドレスでアクセスし「これがFargateか😃」を体感する

前提：AWSとGitHubにログインしている

それではチュートリアルスタートです。

## GitHubActionsを使って、ECRにDockerイメージをプッシュ
### 1-1.ECS → ECR → リポジトリを作成

![スクリーンショット 2025-03-28 23.20.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3999989/f82cc55c-1f50-4976-9828-95c3065af98b.png)

![スクリーンショット 2025-03-28 23.22.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3999989/1a2d2bd8-5019-427a-aa4d-1d99f519d3cf.png)


### 1-2. GitHubActionsを使う準備
リポジトリを作成して、下記のDockerfileとhtmlディレクトリ、index.htmlを保存します

``` ruby:Dockerfile
FROM nginx:latest
COPY ./html /usr/share/nginx/html
```
``` ruby:html/index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My Web App</title>
</head>
<body>
    <h1>初めてのFargate</h1>
</body>
</html>
```

GitHubActions用のymlファイルを作成します

![スクリーンショット 2025-03-28 23.32.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3999989/7749828b-7626-416d-8428-8c5883be8c50.png)

![スクリーンショット 2025-03-28 23.33.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3999989/c1ad08dd-972f-4520-b4a3-d125bc2d6a47.png)

![スクリーンショット 2025-03-28 23.36.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3999989/62076292-8b61-4a12-9478-dbec0de23552.png)

エディタ上に下記のコードを貼り付けてymlファイルを作成します

``` ruby: .github/workflows/ecr-push.yml
name: Push to ECR

# トリガー 「mainブランチにプッシュされたら」
on:
  push:
    branches:
      - main
  workflow_dispatch:

jobs:
# ジョブ
  build-and-push: 
    runs-on: ubuntu-latest # 実行環境（ランナー）を指定 ubuntu-latestを指定した場合、stepsでUNIX系のコマンドが使用できる
    
    # ステップ
    steps:
      # リポジトリのクローン
      - name: Checkout code
        uses: actions/checkout@v3

      # AWS CLI のセットアップ
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v3
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{ secrets.AWS_REGION }}

      # Dockerログイン
      - name: Log in to Amazon ECR
        run: |
          aws ecr get-login-password --region ${{ secrets.AWS_REGION }} \
            | docker login --username AWS --password-stdin \
              $(aws sts get-caller-identity --query 'Account' --output text).dkr.ecr.${{ secrets.AWS_REGION }}.amazonaws.com

      # Dockerイメージのビルド
      - name: Build Docker image
        run: |
          docker build -t ${{ secrets.ECR_REPOSITORY }}:${{ secrets.IMAGE_TAG }} -f Dockerfile .

      # Dockerイメージのタグ付け
      - name: Tag Docker image
        run: |
          docker tag ${{ secrets.ECR_REPOSITORY }}:${{ secrets.IMAGE_TAG }} \
          $(aws sts get-caller-identity --query 'Account' --output text).dkr.ecr.${{ secrets.AWS_REGION }}.amazonaws.com/${{ secrets.ECR_REPOSITORY }}:${{ secrets.IMAGE_TAG }}

      # Dockerイメージのプッシュ
      - name: Push Docker image to Amazon ECR
        run: |
          docker push \
          $(aws sts get-caller-identity --query 'Account' --output text).dkr.ecr.${{ secrets.AWS_REGION }}.amazonaws.com/${{ secrets.ECR_REPOSITORY }}:${{ secrets.IMAGE_TAG }}
```

### 1-3. GitHubActionsを使ってリポジトリにイメージをプッシュする

リポジトリのSettingsに、アクセスキー、リージョン、シークレットキー、ECRリポジトリ名、イメージタグを保存します。
IAMユーザーのアクセスキーとシークレットキーは、
AWSコンソールの画面右上のアカウントをクリック → セキュリティ情報　→　アクセスキーの作成で作成できます。
新規の場合は、New repository secretをクリックして一つずつ保存してくださいね。

:::note info
AWS_ACCESS_KEY_ID:
IAMユーザーのアクセスキー。人に見せちゃダメ。

AWS_SECRET_ACCESS_KEY:
IAMユーザーのシークレットキー。人に見せちゃダメ。

AWS_REGION:
ECRのリージョン（例: 東京リージョンならap-northeast-1）。

ECR_REPOSITORY:
ECRリポジトリ名（例: my-docker-repo）。自分のに変更してね

IMAGE_TAG:
Dockerイメージのタグ（例: latest）。このまま使用OK
:::

![スクリーンショット 2025-03-28 23.28.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3999989/c6c7c497-11e5-4bd1-b258-edada71dbbb3.png)

[1-1. ]　で付けたリポジトリ名を保存

![スクリーンショット 2025-03-28 23.29.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3999989/4886432c-9691-460d-a5d1-0bf8c1ddb322.png)

それではGitHubActionsを実行します

![スクリーンショット 2025-03-28 23.39.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3999989/9c7757c2-c490-40b8-9d8a-ce8f48239e7e.png)

![スクリーンショット 2025-03-28 23.45.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3999989/5cdcaf3a-875a-4cf4-9d0b-7a1d1b7ed707.png)

![スクリーンショット 2025-03-28 23.45.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3999989/e421ced5-e35a-47a8-bfee-63449e77d98d.png)

ちょっと興奮しますね

![スクリーンショット 2025-03-28 23.46.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3999989/c8ea572a-8ce6-4a36-b576-609bcc82cbe7.png)


ECRの中に、イメージがプッシュできました

![スクリーンショット 2025-03-28 23.46.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3999989/16ab5652-27de-432f-b415-ffafac80b4e3.png)

## 2. CloudFormationを使ってECSを構築
まず、イメージのURLをコピーします

![スクリーンショット 2025-03-28 23.49.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3999989/0192ab6e-7ba5-4b20-adec-c35b119867d9.png)

CloudFormationにアップロードするファイルを、ローカルで作成します。
自動でECS構築する魔法のファイルです。
今回は必要最小限のリソースを構築します。

:::note warn
下記のコードを使用する場合は、
"xxx.dkr.ecr.ap-northeast-1.amazonaws.com/my-app-repo:latest"の部分に
イメージのURLを貼り付けてください

:::

``` ruby: test.yaml
AWSTemplateFormatVersion: "2010-09-09"
Resources:
  # VPCの作成
  VPC:
    Type: "AWS::EC2::VPC"
    Properties:
      CidrBlock: "10.0.0.0/16"
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
        - Key: "Name"
          Value: "honda-vpc"

  # サブネットの作成
  PublicSubnet1:
    Type: "AWS::EC2::Subnet"
    Properties:
      VpcId: !Ref VPC
      CidrBlock: "10.0.0.0/20"
      AvailabilityZone: "ap-northeast-1a"
      Tags:
        - Key: "Name"
          Value: "honda-subnet-public1-ap-northeast-1a"

  # インターネットゲートウェイの設定
  InternetGateway:
    Type: "AWS::EC2::InternetGateway"
    Properties:
      Tags:
        - Key: "Name"
          Value: "honda-igw"

  AttachInternetGateway:
    Type: "AWS::EC2::VPCGatewayAttachment"
    Properties:
      InternetGatewayId: !Ref InternetGateway
      VpcId: !Ref VPC

  # ルートテーブルの設定
  PublicRouteTable:
    Type: "AWS::EC2::RouteTable"
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: "Name"
          Value: "honda-rtb-public"

  PublicRoute:
    Type: "AWS::EC2::Route"
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: "0.0.0.0/0"
      GatewayId: !Ref InternetGateway

  SubnetRouteTableAssociation:
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties:
      SubnetId: !Ref PublicSubnet1
      RouteTableId: !Ref PublicRouteTable

  # ECS用セキュリティグループ
  SecurityGroup:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      GroupDescription: "Allow HTTP traffic"
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: "tcp"
          FromPort: 80
          ToPort: 80
          CidrIp: "0.0.0.0/0"

  # ECSクラスターの作成
  ECSCluster:
    Type: "AWS::ECS::Cluster"
    Properties:
      ClusterName: "my-ecs-cluster"

  # ECSタスク定義
  ECSTaskDefinition:
    Type: "AWS::ECS::TaskDefinition"
    Properties:
      Family: "my-app-task"
      RequiresCompatibilities:
        - FARGATE
      NetworkMode: "awsvpc"
      Cpu: "256"
      Memory: "512"
      ExecutionRoleArn: !GetAtt ECSTaskExecutionRole.Arn
      ContainerDefinitions:
        - Name: "my-app-container"
          Image: "xxx.dkr.ecr.ap-northeast-1.amazonaws.com/my-app-repo:latest" ← ご自身のイメージURLを貼り付けてください
          PortMappings:
            - ContainerPort: 80
              HostPort: 80
              Protocol: "tcp"

  # ECSタスク実行ロール
  ECSTaskExecutionRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Principal:
              Service: "ecs-tasks.amazonaws.com"
            Action: "sts:AssumeRole"
      Policies:
        - PolicyName: "ecsTaskExecutionPolicy"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: "Allow"
                Action:
                  - "ecr:GetAuthorizationToken"
                  - "ecr:GetDownloadUrlForLayer"
                  - "ecr:BatchGetImage"
                  - "logs:CreateLogStream"
                  - "logs:PutLogEvents"
                  - "s3:GetObject"
                Resource: "*"

  # ECSサービスの作成
  ECSService:
    Type: "AWS::ECS::Service"
    Properties:
      Cluster: !Ref ECSCluster
      TaskDefinition: !Ref ECSTaskDefinition
      DesiredCount: 1
      LaunchType: "FARGATE"
      NetworkConfiguration:
        AwsvpcConfiguration:
          Subnets:
            - !Ref PublicSubnet1
          SecurityGroups:
            - !Ref SecurityGroup
          AssignPublicIp: "ENABLED"

```
引用元：https://qiita.com/free-honda/items/c7a68a216eef2161c567

<br>
CloudFormation → スタックの作成

![スクリーンショット 2025-03-28 23.47.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3999989/4ce70bb2-c8c9-4d75-aa24-bec6a8c203bb.png)

先ほど作成したtest.yamlをアップロードします

![スクリーンショット 2025-03-28 23.51.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3999989/d7f2f822-8038-46d9-9449-6214aa24f877.png)

スタックに名前を付けます

![スクリーンショット 2025-03-28 23.53.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3999989/d4ea865b-8be7-4607-b995-aa47ad6ecd59.png)

承認チェックに✅します

![スクリーンショット 2025-03-28 23.55.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3999989/4d0345b5-8c44-48d0-ae44-6831d39a063a.png)

最後のページは特に何も変更せずに「送信」をクリックします

![スクリーンショット 2025-03-28 23.56.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3999989/0aab3b82-037a-4003-9f0c-dd67ebb8d46c.png)

test.yamlに書いたリソースがどんどん作られていきます。
更新ボタンを押して、スタック名に「CREATE_COMPLETE」ステータスが付いたら完了です。

![スクリーンショット 2025-03-28 23.59.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3999989/858f6b55-3043-45a3-8961-9e1993c19265.png)


## 3. タスクに割り振られたIPアドレスでアクセスし「これがFargateか😃」を体感する

ECS → クラスター → クラスターをクリック

![スクリーンショット 2025-03-29 0.00.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3999989/cde0dac2-ae1a-4e35-969e-b9a544e27558.png)

タスクをクリック

![スクリーンショット 2025-03-29 0.01.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3999989/98335934-ef66-4438-a88e-fe6fb2e6f074.png)

パブリックIPアドレスをクリック

![スクリーンショット 2025-03-29 0.03.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3999989/a1104894-c4df-4f7d-a54f-10f7c1ec2612.png)

Fargateにアクセスできました！

![スクリーンショット 2025-03-29 0.37.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3999989/dabae600-3c71-4d8c-97c0-7c88f95b1cbf.png)

## 4. GUIでクラスターを作ってみる
先ほどは自動でクラスターとサービスを作成しましたが、試しに作ってみましょう
まず先ほど作成したクラスターを削除します
タスクからタスクを停止し、クラスターからクラスターを削除

![スクリーンショット 2025-03-29 21.41.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3999989/b1064bc8-a1e9-44c9-be4c-762cacbdb176.png)

![スクリーンショット 2025-03-29 21.42.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3999989/7ca42a65-aea3-4984-bd91-f17472296e77.png)

クラスターの作成をクリック

![スクリーンショット 2025-03-29 21.47.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3999989/e501b5be-1d92-4e73-94c5-a10c7930f1fa.png)

クラスターに名前を付けます

![スクリーンショット 2025-03-29 0.11.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3999989/52f2f72d-34b1-4db6-a4ee-be1f290a931b.png)

クラスターができました

![スクリーンショット 2025-03-29 0.13.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3999989/6e8ae664-d722-40fe-824d-47bed75dbada.png)

サービスを作成します

![スクリーンショット 2025-03-29 21.51.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3999989/1fb03382-3985-497e-add7-758fdada9780.png)

![スクリーンショット 2025-03-29 0.19.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3999989/c62e7795-65f7-4575-9d47-184a74e02b23.png)

ネットワーキングのタブを開いて、VPCやサブネットをdefaultではなく、自分で作成したものに変更しましょう

![スクリーンショット 2025-03-29 0.16.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3999989/2f8f23af-d907-4681-8860-1bae9db07994.png)

![スクリーンショット 2025-03-29 0.18.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3999989/41d05d71-9290-4f58-b92c-daf533167e5c.png)

「正常にデプロイされました」の表示は出たら完了です

![スクリーンショット 2025-03-29 0.35.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3999989/054365c6-b8cc-4817-ad02-b1c789724440.png)

## 5. もう一回タスクに割り振られたIPアドレスでアクセスし「これがFargateか😃」を体感する

ECS → クラスター → クラスターをクリック

![スクリーンショット 2025-03-29 0.00.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3999989/cde0dac2-ae1a-4e35-969e-b9a544e27558.png)

タスクをクリック

![スクリーンショット 2025-03-29 0.01.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3999989/98335934-ef66-4438-a88e-fe6fb2e6f074.png)

パブリックIPアドレスをクリック

![スクリーンショット 2025-03-29 0.03.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3999989/a1104894-c4df-4f7d-a54f-10f7c1ec2612.png)

Fargateにアクセスできました！

![スクリーンショット 2025-03-29 0.37.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3999989/dabae600-3c71-4d8c-97c0-7c88f95b1cbf.png)

## まとめ

:::note
- 「ECS on Fargate」とは「Fargate載せECS」の意味で、ECSがメイン
- ECSはクラスター、サービス、タスク・タスク定義で構成される
:::

最後までお付き合いくださりありがとうございます。
今回はECRにDockerイメージをプッシュし、ECSの中のFargateで簡単なWebサイトをデプロイする方法を学び、Fargateを体感しました。
次回はハッカソンで開発したアプリをFargateでデプロイしてみます。


## 参考文献
https://qiita.com/K5K/items/0d8dbdb39fbb0375e2bd

https://www.ctc-g.co.jp/solutions/cloud/column/article/50.html

https://qiita.com/free-honda/items/813b0baf8d993b300cc3

https://zenn.dev/issy/articles/zenn-ecs-overview

https://qiita.com/free-honda/items/da83c5dd44650c883164

https://qiita.com/free-honda/items/c7a68a216eef2161c567