FastAPIアプリをECSへデプロイ!DockerfileをAWS上で動かす手順を解説

前回の記事では、FastAPIで作成したアプリケーションをDocker化し、ローカルでの実行までを行いました。今回はいよいよそのアプリケーションをAWS ECS(Elastic Container Service)へデプロイする手順をご紹介します。

DockerイメージをAWS上で動かすためには、ECR(Elastic Container Registry)への登録や、IAMユーザーの作成AWS CLIとの連携など、いくつかの準備が必要です。この記事ではその一連の流れをステップごとに丁寧に解説していきます。

1. AWSのユーザー作成

まず最初のステップではAWSのコンソール画面上でのIAMユーザーの作成です。

もちろん既存の管理者ユーザーを使っても構いませんが、操作専用のIAMユーザーを作成することで、必要最小限の権限に絞り、セキュリティリスクを軽減できます。

特に開発・テスト用の環境では推奨されるやり方です。

作成手順

  1. AWSマネジメントコンソールにログイン
  2. 「IAM」サービスを選択
  3. 左メニューから「ユーザー」→「ユーザーを追加」
  4. ユーザー名を設定し、プログラムによるアクセス(AWS CLI用)を有効に
  5. 適切なポリシー(例:AmazonEC2ContainerRegistryFullAccess など)をアタッチ

2,ローカルPCとAWSアカウントの紐づけ

作成したIAMユーザーの認証情報を使って、ローカル環境とAWSを接続します。これにより、ターミナルからAWSの各種サービスにアクセスできるようになります。

ターミナル(Mac)またはコマンドプロンプト(Windows)で以下のコマンドを実行してください。

aws configure

クレデンシャル情報の入力では次の4つの情報の入力が求められます。

AWS Access Key ID     : 確認方法は後ほど解説します
AWS Secret Access Key : 確認方法は後ほど解説します
Default region name   : ap-northeast-1(利用するリージョンを入力)
Default output format(例: json、未入力でもOK)

AWS Access Key IDとAWS Secret Access Keyの作成・確認方法

クレデンシャル情報の入力時に求められるKey IDとAccess Keyは以下の手順で作成します。

まず、作成したユーザーでAWSマネージドコンソールにサインインを行い、「IAM」→「ユーザー」のページを開きます。そして、作成したユーザー(今回使用するIAMユーザー)を選択してください。

アクセスキーを作成を選択して、キーを作成しましょう。アクセスキーの作成が完了すると、クレデンシャル情報の入力時に必要な下記2つの情報が表示されます。

  • AWS Access Key ID 
  • AWS Secret Access Key 

この2つの情報をコピー、もしくは.csvファイルをダウンロードしてください。

キー情報は、先ほどIAMユーザーを作成した際に取得したものを使います。

もしSecret Access Keyをコピーし忘れた場合は、再生成が必要になるので注意しましょう。

クレデンシャル情報の入力に必要な情報が揃ったらコマンドプロンプト(もしくはターミナル)に下記コマンドを入力します。

aws configure

そして、以下の4つの情報を1つずつ入力します。

ここまで入力が完了すれば、ステップ2「ローカルPCとAWSアカウントの紐づけ」が完了です。

3. Docker イメージをAWS ECR へプッシュ

AWSとの接続が完了したら、FastAPIのDockerイメージをAWSのコンテナレジストリ「ECR」にアップロードしていきます。

① ECR リポジトリの作成

まずはDockerイメージを格納するためのリポジトリを作成します。

aws ecr create-repository --repository-name ait-api-age-app

実際にリソースが作成されているかAWSマネジメントコンソールで確認しましょう。

② ECR にログイン

ECRにDocker経由でアクセスできるようにログインします。以下のコマンドを実行してください。

aws ecr get-login-password --region ap-northeast-1 | \
docker login --username AWS --password-stdin <AWSアカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com
  • <AWSアカウントID>は12桁のアカウント番号を入力してください。

Login Succeeded と表示されれば、ログインは成功しています。

③ Docker イメージにタグを付ける

アップロードするイメージにECR用のタグを付けます。

docker tag ait-api-age-app:latest <AWSアカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com/ait-api-age-app:latest

④ ECR にプッシュ

DockerイメージをAWS ECRにプッシュします。

docker push <AWSアカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com/ait-api-age-app:latest

これでECSデプロイ時に利用するECRイメージの準備が整いました。

ECSデプロイに必要なAWSリソースを作成

DockerイメージをECRにプッシュできたら、ECS上でアプリケーションを動かすためのインフラを整えていきます。

AWS CLIを使って以下のリソースを一括で構築していきます。

それぞれのサービスの概要や役割については、AWS公式ドキュメントより確認してください。

作成するAWSリソース一覧

  • VPC(任意のアドレス範囲)
  • インターネットゲートウェイ(IGW)
  • パブリックサブネット(2個作成、異なるAZ)
  • ルートテーブル(0.0.0.0/0へIGW)
  • セキュリティグループ(ポート80許可)
  • ターゲットグループ
  • ALB(Application Load Balancer)
  • リスナー
  • CloudWatchロググループ

Step 1:VPCの作成

まずはアプリをデプロイするためのネットワーク(VPC)を作成します。

VPC_ID=$(aws ec2 create-vpc \
  --cidr-block 10.0.0.0/16 \
  --query 'Vpc.VpcId' \
  --output text)
aws ec2 create-tags \
  --resources $VPC_ID \
  --tags Key=Name,Value=ait-api-age-app-vpc
aws ec2 modify-vpc-attribute --vpc-id $VPC_ID --enable-dns-support "{\"Value\":true}"
aws ec2 modify-vpc-attribute --vpc-id $VPC_ID --enable-dns-hostnames "{\"Value\":true}"

Step 2:インターネットゲートウェイ(IGW)作成

VPC内のリソースをインターネットに接続するにはIGWが必要となるため、以下コマンドでIGWを作成します。

IGW_ID=$(aws ec2 create-internet-gateway \
  --query 'InternetGateway.InternetGatewayId' \
  --output text)

aws ec2 attach-internet-gateway \
  --internet-gateway-id $IGW_ID \
  --vpc-id $VPC_ID
aws ec2 create-tags \
  --resources $IGW_ID \
  --tags Key=Name,Value=ait-api-age-app-igw

Step 3:2つのパブリックサブネットを作成

続いて、2つのサブネットを作成します。

ECSは高可用性を保つために、複数のアベイラビリティゾーン(AZ)にサブネットを分散配置するのが基本です。

下記コマンドで別々のAZに1つずつサブネットを配置できます。

SUBNET1_ID=$(aws ec2 create-subnet \
  --vpc-id $VPC_ID \
  --cidr-block 10.0.1.0/24 \
  --availability-zone ap-northeast-1a \
  --query 'Subnet.SubnetId' \
  --output text)

SUBNET2_ID=$(aws ec2 create-subnet \
  --vpc-id $VPC_ID \
  --cidr-block 10.0.2.0/24 \
  --availability-zone ap-northeast-1c \
  --query 'Subnet.SubnetId' \
  --output text)
aws ec2 create-tags \
  --resources $SUBNET1_ID \
  --tags Key=Name,Value=ait-api-age-app-subnet-1a

aws ec2 create-tags \
  --resources $SUBNET2_ID \
  --tags Key=Name,Value=ait-api-age-app-subnet-1c

Step 4:ルートテーブル作成 & IGWルート設定

作成したサブネットからインターネットに出るには、ルートテーブルにIGWへのルートを設定する必要があるため設定します。

ROUTE_TABLE_ID=$(aws ec2 create-route-table \
  --vpc-id $VPC_ID \
  --query 'RouteTable.RouteTableId' \
  --output text)

aws ec2 create-route \
  --route-table-id $ROUTE_TABLE_ID \
  --destination-cidr-block 0.0.0.0/0 \
  --gateway-id $IGW_ID

aws ec2 create-tags \
  --resources $ROUTE_TABLE_ID \
  --tags Key=Name,Value=ait-api-age-app-public-rt

# サブネットにルートテーブルを関連付け
aws ec2 associate-route-table --subnet-id $SUBNET1_ID --route-table-id $ROUTE_TABLE_ID
aws ec2 associate-route-table --subnet-id $SUBNET2_ID --route-table-id $ROUTE_TABLE_ID

Step 5:セキュリティグループ作成

インターネットからのHTTPリクエストを受け取るため、ポート80番を開放したセキュリティグループを作成します。

SG_ID=$(aws ec2 create-security-group \
  --group-name ecs-sg \
  --description "ECS access" \
  --vpc-id $VPC_ID \
  --query 'GroupId' \
  --output text)

aws ec2 authorize-security-group-ingress \
  --group-id $SG_ID \
  --protocol tcp \
  --port 80 \
  --cidr 0.0.0.0/0
aws ec2 create-tags \
  --resources $SG_ID \
  --tags Key=Name,Value=ait-api-age-app-sg

Step 6:ターゲットグループ作成(Fargate 用)

ALBがリクエストを転送するFargateタスクの宛先を登録するため、ターゲットグループを作成します。

TG_ARN=$(aws elbv2 create-target-group \
  --name ait-api-age-app-targets \
  --protocol HTTP \
  --port 80 \
  --vpc-id $VPC_ID \
  --target-type ip \
  --query 'TargetGroups[0].TargetGroupArn' \
  --output text)

Step 7:ALB(Application Load Balancer)の作成

ユーザーからのリクエストを受け取るインターネット公開型ALBを作成します。

ALBをパブリックサブネットに配置し、作成したセキュリティグループを適用します。

ALB_ARN=$(aws elbv2 create-load-balancer \
  --name ait-api-age-app-alb \
  --subnets $SUBNET1_ID $SUBNET2_ID \
  --security-groups $SG_ID \
  --scheme internet-facing \
  --type application \
  --query 'LoadBalancers[0].LoadBalancerArn' \
  --output text)

Step 8:リスナーを作成(ポート80 → ターゲットグループ)

ALBがリクエストを受けたときに、どのターゲットグループへ転送するかを定義するリスナーを設定します。

これで、ALB経由のHTTPリクエストがECSタスクに転送されるようになります。

aws elbv2 create-listener \
  --load-balancer-arn $ALB_ARN \
  --protocol HTTP \
  --port 80 \
  --default-actions Type=forward,TargetGroupArn=$TG_ARN

Step 9:CloudWatchロググループの作成

ECSタスクのログ出力先として使う、CloudWatch Logsのロググループを作成します。

aws logs create-log-group \
  --log-group-name /ecs/ait-api-age-app-task \
  --region ap-northeast-1

これにより、FastAPIアプリの標準出力ログやエラーログをCloudWatch上で確認できるようになります。

ECS(Fargate)クラスターとタスク定義の作成

前回までで、AWS上に必要なネットワークリソース(VPCやALBなど)を構築し、FastAPIアプリのDockerイメージをECRにプッシュするところまで完了しました。
ここからは、いよいよECS(Elastic Container Service)上にFargateを使ってFastAPIアプリをデプロイするフェーズに入ります。

ECSデプロイの全体の流れ

  1. ECSクラスターの作成
  2. タスク定義ファイルの作成
  3. タスク実行ロールの作成
  4. ECSサービスロールの作成
  5. ECSサービスの作成(ALBとの連携)

1.クラスターの作成

まず最初に、アプリケーションをデプロイするECSクラスターを作成します。クラスターはFargateタスクを実行するための「箱」のような存在です。

aws ecs create-cluster --cluster-name ait-api-age-app-cluster

このコマンドを実行すると、クラスター名やARNがJSON形式で返されます。

作成に成功していれば、下記画像のような出力になります。

AWSマネジメントコンソールのECS画面でも確認できます。

2. タスク定義ファイルの作成(task-def.json

タスク定義は、Fargateで動かすコンテナの設定情報をまとめたものです。CPUやメモリの割り当て、ログ設定、ポート番号などを記述します。

まずは以下のコマンドでファイルを作成します。

touch task-def.json

task-def.jsonファイルに以下を記述します。

{
    "family": "ait-api-age-app-task",
    "networkMode": "awsvpc",
    "requiresCompatibilities": ["FARGATE"],
    "cpu": "256",
    "memory": "512",
    "executionRoleArn": "arn:aws:iam::<AWSアカウントID>:role/ecsTaskExecutionRole",
    "containerDefinitions": [
      {
        "name": "ait-api-age-app",
        "image": "<AWSアカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com/ait-api-age-app:latest",
        "essential": true,
        "portMappings": [
          {
            "containerPort": 80,
            "protocol": "tcp"
          }
        ],
        "logConfiguration": {
          "logDriver": "awslogs",
          "options": {
            "awslogs-group": "/ecs/ait-api-age-app-task",
            "awslogs-region": "ap-northeast-1",
            "awslogs-stream-prefix": "ecs"
          }
        }
      }
    ]
  }

ポイント

“awslogs-group”の値は、Step 9:CloudWatchロググループの作成で作成したロググループ名を入力する

<AWS_ACCOUNT_ID>は環境に合わせてアカウントIDを入力してください。

3. 「ecsTaskExecutionRole」を作成

ECSがタスクを実行する際に使用するIAMロール(実行ロール)を作成します。

aws iam create-role \
  --role-name ecsTaskExecutionRole \
  --assume-role-policy-document '{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Effect": "Allow",
        "Principal": {
          "Service": "ecs-tasks.amazonaws.com"
        },
        "Action": "sts:AssumeRole"
      }
    ]
  }'

ロールが作成できたら、必要なポリシーをアタッチします。

aws iam attach-role-policy \
  --role-name ecsTaskExecutionRole \
  --policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy

このロールにより、ECSタスクはECRからイメージを取得したり、CloudWatchにログを送信することができます。

4. タスク定義を登録

先ほど作成したJSONファイルをもとに、ECSへタスク定義を登録します。

aws ecs register-task-definition --cli-input-json file://task-def.json

成功すると、タスク定義のARNやバージョン情報が表示されます。

5. ECS サービスロールを作成

ECSサービスを作成する際、必要な権限を持つサービスリンクロールが存在していない場合は以下のコマンドで作成します。

aws iam create-service-linked-role \
  --aws-service-name ecs.amazonaws.com

これにより、ECSがALBなどと連携してタスクを起動できるようになります。

6. サービス作成(ECS タスクを ALB 経由で起動)

最後に、ECSクラスターにサービスを作成します。

これはFargateタスクを指定数だけ常時稼働させるための設定です。

aws ecs create-service \
  --cluster ait-api-age-app-cluster \  
  --service-name ait-api-age-app-service \
  --task-definition ait-api-age-app-task \
  --desired-count 1 \
  --launch-type FARGATE \
  --network-configuration "awsvpcConfiguration={subnets=[subnet-xxxxxx,subnet-yyyyyy],securityGroups=[sg-zzzzzz],assignPublicIp=ENABLED}" \
  --load-balancers "targetGroupArn=arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:targetgroup/<target-group-id>,containerName=ait-api-age-app,containerPort=80"

設定ミスを防ぐために、必要な値はAWSコンソールかCLIで事前に確認しておきましょう。

下記それぞれコマンドもしくはAWSマネージメントコンソールで確認して入力してください

subnet-xxxxxx 1つ目のサブネットIDを入力
subnet-yyyyyy 2つ目のサブネットIDを入力
sg-zzzzzz セキュリティグループIDを入力
123456789012  AWSアカウントIDを入力
target-group-id. ロードバランサーで使用するターゲットグループID

ALBのDNS名を確認し、アプリをブラウザで表示

サービス作成が成功すると、ALBのURLを通じてFastAPIアプリにアクセスできるようになります。

aws elbv2 describe-load-balancers \
  --names ait-api-age-app-alb \
  --query "LoadBalancers[0].DNSName" \
  --output text

出力されたURL(例:ait-api-age-app-alb-xxxx.elb.amazonaws.com)に /docs を付けてブラウザでアクセスしてみましょう。

例:

FastAPI - Swagger UI
Screenshot

さらに以下のようにクエリをつければ、アプリの動作確認もできます。

URLの末尾に「/age?birth_year=2000」の用に入力すると画像のように年齢が表示されます。

例:
http://ait-api-age-app-alb-408369693.ap-northeast-1.elb.amazonaws.com/age?birth_year=2000

まとめ

ここまでで、FastAPIアプリケーションをECS Fargate上にデプロイし、ALBを通じてインターネットからアクセス可能な状態にする手順を一通り完了しました。

本記事で行ったことをおさらいします。

  • ECSクラスターの作成
  • Fargate用のタスク定義と実行ロールの設定
  • ECSサービスの作成とALBとの連携
  • アプリURLの確認と動作検証

これらの手順は、FastAPIに限らず、FlaskやNext.js、Node.jsなど他のWebアプリケーションにも応用可能です。

プロフィール
tanat

「株式会社あいてぃ」に所属。
主にAWSとAzureなどのクラウドインフラを管理するクラウドエンジニアとして活動中。
スマホアプリやフロント部分の構築も可能なフルスタックエンジニアとして活躍できるように日々勉強中です。

tanatをフォローする
awsプログラミング
tanatをフォローする

コメント

タイトルとURLをコピーしました
function cocoon_custom_code_copy_script() { ?>   } add_action('wp_footer', 'cocoon_custom_code_copy_script');