【AWS実践編】FlaskとAmazon Auroraの連携アプリをECSでデプロイする

コンピューティングサービス

この記事では、前回ご紹介した「FlaskとAmazon Aurora!データベース連携の方法を徹底解説」の続きとして、FlaskとAmazon Auroraを連携させた単語帳アプリをAWS ECS (Elastic Container Service) 上にデプロイする方法を詳しく解説します。

さらに、今回はデータベースであるAmazon Auroraをプライベートサブネットに配置することで、よりセキュアな環境を構築します。

AWSの各種リソースをCLI (Command Line Interface) で作成していくため、実践的な知識が身につきます。

この記事で行うこと

この記事では、前回作成したFlaskとAmazon Aurora連携アプリをAWS ECS上でデプロイする一連の手順を追っていきます。

特に、Auroraをプライベートサブネットに配置することでセキュリティを向上させる点がポイントです。

VScodeでDockerfile 作成

まず、アプリケーションのデプロイに必要なフォルダ構成を確認しましょう。

フォルダ構成

wordbook-app/
├── app.py
├── models.py
├── requirements.txt
└── Dockerfile(※ECSに載せるなら後で作成)

前回の記事で紹介した構成と同じです。まだ作成していない場合は、前回の記事を参考にwordbook-app/ディレクトリとその中身(app.pymodels.pyrequirements.txt)を作成してください。

Dockerfileのコードを記述

VScodeでwordbook-appディレクトリの直下にDockerfileという名前のファイルを作成し、以下の内容を記述します。

このDockerfileは、PythonアプリケーションをDockerコンテナとして実行するための手順を定義しています。

# ベースイメージ
FROM python:3.11-slim

# 作業ディレクトリ
WORKDIR /app

# 依存ファイルをコピー
COPY requirements.txt .

# パッケージインストール
RUN pip install --no-cache-dir -r requirements.txt

# アプリのソースコードをコピー
COPY . .

# ポート指定(Flask用)
EXPOSE 5200

# アプリ起動コマンド
CMD ["python", "app.py"]

ネットワークリソース作成

アプリケーションをAWS ECS上にデプロイするためには、まず適切なネットワーク環境を構築する必要があります。

ここでは、以下の基本的なネットワークリソースをCLIで作成します。

  • VPC
  • サブネット
  • インターネットゲートウェイ
  • ルートテーブル
  • セキュリティグループ

Step 1:VPCの作成

アプリをデプロイするためのプライベートなネットワーク空間であるVPC (Virtual Private Cloud)を作成します。

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=wordbook-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)を作成し、VPCにアタッチします。

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=wordbook-app-igw

Step 3:ECS用のパブリックサブネットを作成

次に、ECSタスクを配置するパブリックサブネットを2つ作成します。高可用性を確保するため、異なるアベイラビリティゾーン(AZ)に分散して配置するのが推奨されます。

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=wordbook-app-subnet-1a

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

Step 4:Aurora 用のプライベートサブネット作成

セキュリティを考慮し、Amazon Auroraデータベースを配置するためのプライベートサブネットを2つ作成します。これも異なるAZに配置します。

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

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

aws ec2 create-tags --resources $PRIVATE_SUBNET1_ID --tags Key=Name,Value=wordbook-app-private-subnet-1a
aws ec2 create-tags --resources $PRIVATE_SUBNET2_ID --tags Key=Name,Value=wordbook-app-private-subnet-1c

Step 5:ルートテーブル作成 & 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=wordbook-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 6:セキュリティグループ作成

ECSとAuroraそれぞれに、適切な通信を許可するためのセキュリティグループを作成します。

ECS用のセキュリティグループ

インターネットからのHTTPリクエスト(ポート80)と、Flaskアプリケーションのポート(5200)を受け入れるためのセキュリティグループを作成します。

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 authorize-security-group-ingress \
  --group-id $SG_ID \
  --protocol tcp \
  --port 5200 \
  --cidr 0.0.0.0/0

aws ec2 create-tags \
  --resources $SG_ID \
  --tags Key=Name,Value=wordbook-app-sg

Flaskアプリケーションで別のポートを使用している場合は、5200ではなく、そのポートを記述してください。

Aurora用のセキュリティグループ

Auroraデータベースへのアクセスを、ECSのセキュリティグループからのみ許可するセキュリティグループを作成します。

これにより、データベースへの直接的な外部からのアクセスを防ぎます。

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

aws ec2 authorize-security-group-ingress \
  --group-id $AURORA_SG_ID \
  --protocol tcp \
  --port 3306 \
  --source-group $SG_ID

aws ec2 create-tags \
  --resources $AURORA_SG_ID \
  --tags Key=Name,Value=wordbook-db-sg

Amazon Aurora作成

ネットワークリソースの準備が整ったら、次にAmazon Auroraデータベースを作成します。

Aurora 用 DB サブネットグループを作成

Auroraデータベースをプライベートサブネットに配置するため、専用のDBサブネットグループを作成します。

aws rds create-db-subnet-group \
  --db-subnet-group-name wordbook-db-subnet-group \
  --db-subnet-group-description "Private subnets for Aurora" \
  --subnet-ids $PRIVATE_SUBNET1_ID $PRIVATE_SUBNET2_ID

Amazon Aurora作成

いよいよ、Amazon Auroraデータベースのクラスターを作成します。

先ほど作成したセキュリティグループとDBサブネットグループを指定します。

aws rds create-db-cluster \
  --db-cluster-identifier wordbook-app-db \
  --engine aurora-mysql \
  --engine-mode provisioned \
  --db-subnet-group-name $DB_SUBNET_GROUP_NAME \
  --vpc-security-group-ids $AURORA_SG_ID \
  --region ap-northeast-1 \
  --master-username admin \
  --master-user-password '<任意のパスワード>'

<任意のパスワード>については、コマンド実行時に実際に使用するパスワードに書き換えてください。

Aurora用 DBインスタンスを作成する

実際にデータを処理するためのDBインスタンスをクラスター内に作成します。

aws rds create-db-instance \
  --db-instance-identifier wordbook-app-db-instance-1 \
  --db-cluster-identifier wordbook-app-db \
  --engine aurora-mysql \
  --db-instance-class db.t4g.medium \
  --region ap-northeast-1

CLI からデータベースを作成する

RDSクラスターとインスタンスが作成されただけでは、まだ中にMySQLデータベースが存在しません。

アプリケーションから接続できるように、実際にデータベースを作成しましょう。

今回は、EC2インスタンスなどからSSH接続してAuroraに接続する想定です。

RDSに接続するコマンド

まず、Auroraに接続するために以下のコマンドを使います。ターミナルで実行してください。

mysql -u <ユーザー名> -p -h <エンドポイント名>

※コマンド実行後に、パスワードが求められます。

🔐 エンドポイント名、ユーザー名、パスワードの確認方法については後述。

データベース作成

接続に成功したら、以下のSQLコマンドを実行してデータベースを作成します。

CREATE DATABASE `<作成したいデータベース名>`;

名前に -(ハイフン)が含まれている場合は、バッククォート ` で囲む必要があります。

作成したデータベースの確認

データベースが正しく作成されたか確認するために、以下のコマンドを実行します。

SHOW DATABASES;

作成したデータベース名がリストに表示されていれば、データベースの準備は完了です。

ALB(Application Load Balancer)の作成

次に、ユーザーからのリクエストをECSタスクに振り分けるためのApplication Load Balancer (ALB)を設定します。

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

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

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

ALBの作成

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

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

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

リスナーを作成(ポート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

CloudWatchロググループの作成

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

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

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

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

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

① ECR リポジトリの作成

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

aws ecr create-repository --repository-name wordbook-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/<リポジトリ名>:latest

④ ECR にプッシュ

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

docker push <AWSアカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com/<リポジトリ名>:latest

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

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

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

前回までで、AWS上に必要なネットワークリソース(VPCやALBなど)を構築し、FastAPIアプリのDockerイメージをECRにプッシュするところまで完了しました。

ここからは、いよいよECS(Elastic Container Service)上にFargateを使ってFastAPIアプリをデプロイするフェーズに入ります。

1.クラスターの作成

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

aws ecs create-cluster --cluster-name wordbook-app-cluster

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

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

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

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

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

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

touch task-def.json

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

{
    "family": "wordbook-app-task",
    "networkMode": "awsvpc",
    "requiresCompatibilities": ["FARGATE"],
    "cpu": "256",
    "memory": "512",
    "executionRoleArn": "arn:aws:iam::<AWSアカウントID>:role/ecsTaskExecutionRole",
    "containerDefinitions": [
      {
        "name": "wordbook-app",
        "image": "<AWSアカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com/wordbook-app:latest",
        "essential": true,
        "portMappings": [
          {
            "containerPort": 5200,
            "protocol": "tcp"
          }
        ],
        "logConfiguration": {
          "logDriver": "awslogs",
          "options": {
            "awslogs-group": "/ecs/wordbook-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 wordbook-app-cluster \
  --service-name wordbook-app-service \
  --task-definition wordbook-app-task \
  --desired-count 1 \
  --launch-type FARGATE \
  --network-configuration "awsvpcConfiguration={
    subnets=[$SUBNET1_ID,$SUBNET2_ID],
    securityGroups=[$SG_ID],
    assignPublicIp=ENABLED
  }" \
  --load-balancers "targetGroupArn=$TG_ARN,containerName=wordbook-app,containerPort=5200"

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

下記それぞれコマンドもしくはAWSマネージメントコンソールで確認して入力してください。
・$SUBNET1_ID:1つ目のパブリックサブネットIDを入力
・$SUBNET2_ID:2つ目のパブリックサブネットIDを入力
・$SG_ID. :ECS用のセキュリティグループIDを入力
・$TG_ARN :EC2→ロードバランシング→ターゲットグループを選択するとARNが表示

アプリをブラウザで表示

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

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

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

例:
http://wordbook-app-alb-408369693.ap-northeast-1.elb.amazonaws.com/docs

ALBのURLで単語帳アプリが作成できることを確認できました。

まとめ

この記事では、FlaskとAmazon Auroraを連携させた単語帳アプリを、AWS ECS (Elastic Container Service) 上にデプロイする詳細な手順を解説しました。特に、Amazon Auroraをプライベートサブネットに配置することで、データベースへのアクセスをよりセキュアにする構成を実現しました。

VPC、サブネット、インターネットゲートウェイといったネットワーク基盤の構築から始まり、ALBの設定、CloudWatch Logsとの連携、そしてDockerイメージのECRへのプッシュ、最終的なECSクラスターとタスク定義の作成まで、一連のプロセスをCLIコマンドを中心に実践しました。

これらの手順を一つひとつ実行することで、AWS上でのコンテナ化されたアプリケーションデプロイと、セキュアなデータベース連携のノウハウを習得できたはずです。この知識を活かし、さらに複雑なアプリケーションの構築や、より高度なAWSサービスの活用に挑戦してみてください。

プロフィール
TanaT

株式会社あいてぃ所属。
クラウドエンジニア(AWS・Azure)
取得資格:AWS SAP、AZ-104、AZ-305
フロントエンド、バックエンド開発もできるフルスタックエンジニアとして学習中。
「AIとクラウドについて学ぶ」サイトの編集長。

TanaTをフォローする
コンピューティングサービスデータベース
シェアする
タイトルとURLをコピーしました