CloudShellでDockerコンテナをECSにデプロイ ハンズオン

はじめに

昨年CloudShellでDockerコマンドが利用できるようになりました(2023年12月27日 アップデート)。
だから何ができるのか分からなかったので、とりあえず自分の中で思いつた『こういった構成できるんじゃないか?』を試してみました。

構築イメージ

やってみた目次

No 内容
1 コンテナ構築 / 起動
2 ECRへイメージをプッシュ
3 ECSでコンテナ起動

1.コンテナ構築 / 起動

1.1.CloudShellでFlaskアプリ作成

CloudShellにて静的ページを返すアプリを構築

# 最終的なフォルダ構成
.
├── Dockerfile
├── app.py
├── requirements.txt
└── templates
          └── index.html

1.2.Flaskインストール

requirements.txtFlaskを記載

vi requirements.txt
cat requirements.txt
Flask

# Flaskインストール
pip install -r requirements.txt

1.3.『app.py』作成

vi app.py
cat app.py
from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
def index():
    return render_template('index.html')

if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0', port=8080)

1.4.『templates/index.html』作成

vi index.html
cat index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <title>Flask</title>
</head>
<body>
    <p>Hello World!!</p>
</body>
</html>

1.5.『DockerFile』作成

vi Dockerfile
cat Dockerfile
# Python 3.11の公式イメージをベースとする
FROM python:3.11

# 作業ディレクトリを/appに設定
WORKDIR /app

# 現在のディレクトリの内容をコンテナの/appに追加
ADD . /app

# pipをアップグレードし、requirements.txtにリストされた依存関係をインストール
RUN pip install --upgrade pip && \
    pip install -r requirements.txt

# コンテナの8080ポートを公開
EXPOSE 8080

# コンテナ起動時にapp.pyを実行
CMD ["python", "app.py"]

1.6.コンテナ起動

# Dockerイメージのビルド
docker build -t flask-app .

# Dockerコンテナの実行
docker run -d -p 8080:8080 --name flask-app flask-app:latest 

1.7.コンテナ起動確認

# Curlコマンドによる確認
curl http://localhost:8080
<!DOCTYPE html>
<html lang="en">
        <head>
                    <title>Flask</title>
        </head>
        <body>
                    <p>Hello World!!</p>
        </body>
</html>

# Docker コンテナ状況確認
docker ps
CONTAINER ID   IMAGE              COMMAND           CREATED         STATUS         PORTS                                       NAMES
203c24eebd5c   flask-app:latest   "python app.py"   6 minutes ago   Up 5 minutes   0.0.0.0:8080->8080/tcp, :::8080->8080/tcp   flask-app

# Docker images確認
docker images
REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
flask-app    latest    533d5b063fc0   7 minutes ago   1.03GB

コンテナを停止する際のコマンド

コンテナを停止する際は下記コマンドを利用

# コンテナ停止する場合
docker stop ${CONTAINER ID ※上記の場合、203c24eebd5c}

# コンテナの削除
docker rm ${CONTAINER ID ※上記の場合、203c24eebd5c}

#イメージの削除
docker rmi ${IMAGE ID ※上記の場合、533d5b063fc0}

2.ECRへイメージをプッシュ

リージョンは東京リージョン(ap-northeast-1)を使用
各自のAWSアカウントIDを利用

2.1.ECRリポジトリ作成

# 環境変数
export AWS_ACCOUNTID=123456789012
export AWS_REGION=ap-northeast-1
export AWS_REPOSITORY_NAME=my-flask-app

# リポジトリ作成
aws ecr create-repository --repository-name ${AWS_REPOSITORY_NAME} --region ${AWS_REGION}

2.2.ECRへイメージをプッシュ

# ECRにDockerクライアントをログイン
aws ecr get-login-password --region ${AWS_REGION} | docker login --username AWS --password-stdin ${AWS_ACCOUNTID}.dkr.ecr.ap-northeast-1.amazonaws.com

# Docker イメージ名確認
# REPOSITORY == イメージ名
# TAG == タグ
docker images
REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
flask-app    latest    533d5b063fc0   7 minutes ago   1.03GB

# リポジトリにイメージをプッシュできるようタグを付与
docker tag ${イメージ名}:${タグ} ${ACCOUNTID}.dkr.ecr.ap-northeast-1.amazonaws.com/${AWS_REPOSITORY_NAME}:latest

# タグがついたか確認
docker images
REPOSITORY                                                       TAG       IMAGE ID       CREATED         SIZE
${ACCOUNTID}.dkr.ecr.ap-northeast-1.amazonaws.com/my-flask-app   latest    533d5b063fc0   8 minutes ago   1.03GB
flask-app    latest    533d5b063fc0   7 minutes ago   1.03GB

# ECRにプッシュ
docker push ${ACCOUNTID}.dkr.ecr.ap-northeast-1.amazonaws.com/${AWS_REPOSITORY_NAME}:latest

# ECRのリポジトリ確認
aws ecr list-images --repository-name ${AWS_REPOSITORY_NAME} --region ${AWS_REGION}

3.ESCでコンテナ起動

3.1.タスク定義の作成

アカウントIDリージョン部分はそれぞれ修正する

vi task-definition.json
cat task-definition.json
{
  "family": "my-flask-app-task",
  "taskRoleArn": "arn:aws:iam::アカウントID:role/ecsTaskExecutionRole",
  "executionRoleArn": "arn:aws:iam::アカウントID:role/ecsTaskExecutionRole",
  "networkMode": "awsvpc",
  "containerDefinitions": [
    {
      "name": "flask-app",
      "image": "アカウントID.dkr.ecr.リージョン.amazonaws.com/my-flask-app:latest",
      "cpu": 256, 
      "memory": 512,
      "essential": true,
      "portMappings": [
        {
          "containerPort": 8080,
          "hostPort": 8080
        }
      ]
    }
  ],
  "requiresCompatibilities": [
    "FARGATE"
  ],
  "cpu": "256",
  "memory": "512"
}

# 上記設定したタスク定義を作成
# file://{task-definition.json}のパスを記載
aws ecs register-task-definition --cli-input-json file://path/to/task-definition.json

3.2.ECS SGの作成

# 環境変数
export VPC_ID=vpc-XXXXX
export ECS_SG_NAME=my-flask-app-SG
export MY_IP=YYY.YYY.YYY.YYY

#ECSのSG作成 
export SG_ID=$(aws ec2 create-security-group \
--group-name ${ECS_SG_NAME} \
--description ${ECS_SG_NAME} \
--vpc-id ${VPC_ID} \
--query 'GroupId' \
--output text)

# ECSのSGに下記ルール追加
aws ec2 authorize-security-group-ingress \
             --group-id $SG_ID \
             --protocol tcp \
             --port 8080 \
             --cidr ${MY_IP}/32

3.2.ECS の作成

ECS Fargateで構築

# 環境変数
export SUBNET_PUB_A=subnet-XXXXX
export SUBNET_PUB_C=subnet-YYYYY
export CLUSTER_NAME=my-flask-app-cluster
export SERVICE_NAME=my-flask-app-service
export TASK_NAME=my-flask-app-task

# ECS クラスター作成
aws ecs create-cluster --cluster-name ${CLUSTER_NAME}

# ECS サービスの作成
aws ecs create-service --cluster ${CLUSTER_NAME} --service-name ${SERVICE_NAME} \
--task-definition ${TASK_NAME} --desired-count 1 \
--launch-type FARGATE --network-configuration "awsvpcConfiguration={subnets=[${SUBNET_PUB_A},${SUBNET_PUB_C}],securityGroups=[${SG_ID}],assignPublicIp=\"ENABLED\"}"

挙動の確認

ECS > クラスター > 「my-flask-app-cluster」 > サービス > タスク 押下

赤枠部分を押下するとhttp://13.115.249.61/でブラウザが開くが、今回コンテナは8080ポートでリッスンしているため、http://13.115.249.61:8080/という形でURLを変更してアクセス
※既に、こちらのタスク削除済み

ブラウザで画面が表示される

おわりに

思いついたことが検証できてなによりでした。

実際本番環境で CloudShellをつかう選択肢は限られてると思いますが(スペック的な面で)、簡単な構築であればサクッとECSを作れちゃうのは魅力かと思いました。

Last modified: 2024-02-24

Author