20
Help us understand the problem. What are the problem?

posted at

updated at

本番稼働を意識したECS Fargateを用いたFastAPIの環境構築(ALB、SSM、BlueGreenデプロイ)

0. 背景

ECS Fargateは、2018年に東京リージョンでの提供街開始され、サーバーやクラスターを管理することなくコンテナを実行することができる、いわゆるサーバーレスの実行環境です。Fargateの特徴の詳細はこの記事では述べませんが、使ってみたい技術ではあるものの、コンテナにログインしてデバッグするなどができず、本番環境での利用は躊躇してきました。コンテナに入ることができないというのはセキュリティ面ではメリットであるものの、本番で再現したバグのデバッグなどは直接入ってデバッグしたいという思いは出てくるものです。きちんとテストした上でデプロイし、またログを適切に吐かせることでコンテナに入らなくてもデバッグできるというポリシーかもしれませんが、実際の現場では往々にして本番環境で直接作業するということをゼロにすることは難しいのではないかと思っています。
しかし、2021年の3月にaws ecs execute-commandコマンドを利用することでSSMすることができるようになったことが発表され1、これまでの懸念が払拭されたことから、ECS Fargateを本格的に利用してみようと思い、この記事ではECS Fargateを利用してDockerアプリをデプロイする一連の流れを自分の備忘録も兼ねて紹介していきたいと思います。勉強中のみですので、間違っている点、改善点あればぜひコメントをいただけたらと思います。

0-1. 本番で稼働するために確認しておくべきこと

本番で稼働させるための確認すべきことはプロジェクトによって様々だとは思いますが、個人的には以下の様なことを考えていけば良いのではないかと思っています。プライベートで構築する範囲ではおざなりで良い部分も仕事として行うときには抑えておかなければならないこともあり、先輩方と仕事をするたびにただ動かせるだけではエンジニアとは名乗れないというのを日々感じています。

1) ロードバランサー経由でアクセスできる
ALBを利用した負荷分散だけでなく、Route53との連携やGlobalAccerelatorの利用による外部との連携が可能になるので、ALBを利用したアクセスは必須です。
2) SSHしてサーバーに入って作業ができる
前述の様にコンテナ内に入って作業ができるということはセキュリティ面では良くないものの、実運用ではしばしば行われ、緊急時のデバッグには必要になります。今回、SSHではなく、SSMで入ることになりますが、実用上は代替できるでしょう。
3) デプロイの仕方が簡便で堅牢であること
ElasticBeanstalkでは、eb deployコマンドを利用すれば簡単にアプリケーションをデプロイすることができました。ECS Fargateでのデプロイの場合どうなるのか、いくつかの方法はclassmethod様の記事2で挙げられていますが、今回は素朴にaws cliを用いた方法で行っていきたいと思います。
4) ログ出力
 ログは不具合調査や監査等で重要な存在になってきます。そのログをどの様にして確認できるかということも把握しておく必要があります。

0-2. 全体の流れ

まず冒頭で本記事の全体の流れについて紹介させていただきます。
1. アプリケーションの構築
 簡単なアプリケーションをPythonのWebフレームワークであるFastAPIをDockerで構築します。アプリケーションの中身は今回の主眼ではないのでFastAPIのサンプルで紹介されているシンプルなものを利用します。
2. ECRへの登録
 ECRへ登録します。この辺りは、AWS上でDocker imageを利用されている方であれば特筆すべきことは何もないので、読み飛ばしていただいで良いでしょう。
3. ネットワーク関係(VPC, サブネット, ロードバランサー, IGW、ターゲットグループ)の作成
 ECR Fargateといっても、ALBを利用するために基本的なネットワーク構成の作成は必要になります。ここでは、VPC、サブネット、ロードバランサー、インターネットゲートウェイ(IGW)、ターゲットグループを作成していきます。昨今は、現場によってはterraformなどやCloud Formation等でインフラもコードベースで管理するケースが多いとおもいますが、本記事はterraformの説明が主眼ではありませんので、AWSコンソール上から作成しています。
4. ECS Execで利用するKMSキーの作成
 ECS ExecでSSMする場合、暗号化のためのKMSキーが必要になってきます。この辺りは、Amazon ECS Exec による AWS Fargate, Amazon EC2上のコンテナへのアクセスというAmazon Web Services ブログを参考に(一部ほぼ転用)していますので、本家をご覧になった方が良いかもしれません。
5. ECS Execのログ周りの設定
6. IAMロールの作成
7. ECS(クラスター、タスク定義、サービス)の設定
8. デプロイの設定(Blue/Green)
9. ログの確認

0-3. 全体の構成概要

今回のAWSでの構成は概ね以下の様になります。本来ならばこれらに加え、Route53でのドメイン管理およびKMSでの証明書管理やRDSなどが加わってくると思いますが、ドメイン周りはALBより先の設定でありECSとは切り離して考えれること、RDSに関してもセキュリティーグループおよび環境変数への設定となることがみえているため、本記事では割愛いたします。
image.png

1. アプリケーションの構築

FastAPIのサンプルではtiangolo/uvicorn-gunicorn-fastapi:python3.7というイメージをベースにDockerfileを構築していますが、AWS上でより汎用的に利用するために、amazonlinux:2をベースにしてDockerfileを作成しました。ただ、振り返ってみれば特にamazonlinux:2ベースにする必要はなかったかなと感じていますので、お好きな形で作成すると良いでしょう。

FROM amazonlinux:2

WORKDIR /app

SHELL ["/bin/bash", "-c"]
RUN yum update -y
RUN yum install -y gcc bzip2 bzip2-devel openssl openssl-devel readline readline-devel git libffi-devel wget gcc-c++ unixODBC-devel tar.x86_64

RUN echo 'export PYENV_ROOT="/usr/local/pyenv"' | tee -a /etc/profile.d/pyenv.sh
RUN echo 'export PATH="${PYENV_ROOT}/shims:${PYENV_ROOT}/bin:${PATH}"' | tee -a /etc/profile.d/pyenv.sh
RUN echo 'eval "$(pyenv init -)"' | tee -a /etc/profile.d/pyenv.sh

RUN git clone https://github.com/pyenv/pyenv.git /usr/local/pyenv
COPY app/ /app

ENV PYENV_ROOT "/usr/local/pyenv"
ENV PATH "${PYENV_ROOT}/shims:${PYENV_ROOT}/bin:${PATH}"
RUN echo 'eval "$(pyenv init -)"' >> ~/.bashrc
RUN pyenv install -v 3.7.6 && pyenv global 3.7.6 && pip install awscli && pip install -r requirements.txt

EXPOSE 80

CMD ["uvicorn", "main:app", "--reload", "--host", "0.0.0.0", "--port", "80"]

appディレクトリを作成し、appディレクトリないのmain.pyファイルを以下の様に記述します。

from typing import Optional
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def read_root():
    return {"Hello": "World"}

@app.get("/items/{item_id}")
def read_item(item_id: int, q: Optional[str] = None):
    return {"item_id": item_id, "q": q}

また、appディレクトリ内のrequirements.txtは以下の様な内容です。

asgiref==3.4.1
click==8.0.1
fastapi==0.68.0
h11==0.12.0
importlib-metadata==4.6.3
pydantic==1.8.2
starlette==0.14.2
typing-extensions==3.10.0.0
uvicorn==0.14.0
zipp==3.5.0

ここまでの作業でディレクトリとファイル構成は以下の様になります。シンプルです。

.
├── Dockerfile
└── app
    ├── main.py
    └── requirements.txt

2. ECRへの登録

Docker imageの作成 → ECRへのdocker login → docker imageのタグ付け → ECRヘpushの順に説明します。

まず、Docker imageを作成します。今回は、ecs-fargate-demoという名前で作成することにします。

docker build -t ecs-fargate-demo .

ECRのリポジトリを作成します。以下ではAWSコマンドで行っていますが、AWSコンソール上で画面で行っても全く問題ないでしょう。また、東京リージョン(ap-northeast-1)は色々とアプリケーションがあることから、ほぼ使われていないus-west-1で構築していますが、お好みのリージョンに読み替えてください。
また、以降のコマンドではaws cliを用いていますが、IAMの利用方法によっては、--profile xxxxxxxxxxのようにプロファイルを指定してあげる必要がある場合がありますので、必要に応じて付け加えてください。

AWS_REGION=us-west-1
aws ecr create-repository \
  --repository-name ecs-fargate-demo \
  --region ${AWS_REGION}

次に、ECRにログインします。お馴染みの以下のコマンドでログインしてください。

AWS_ACCOUNT_ID=xxxxxxxxxxxx # 数字12桁のもの
# ECRにログイン
aws ecr get-login-password --region ${AWS_REGION} | docker login --username AWS --password-stdin ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com

次に、Docker Imageですが、タグにlatestをつける運用もありますが、個人的にはきちんとバージョン管理をするためにも、デプロイごとに別のタグをつけていく方が良いと考えております。classmethodさんの記事2にもlatest運用はやめましょうとあり、latest運用についての考察は勝手ながらそちらにお任せさせていただきます。今回は素朴にまずは1.0.0とつけておき、デプロイでは、1.0.1としたものをデプロイしていこうと思います。

# docker imageのタグ付け
docker tag ecs-fargate-demo ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/ecs-fargate-demo:1.0.0

最後にDocker ImageをECRにpushします。

# Docker ImageをECRにpush
docker push ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/ecs-fargate-demo:1.0.0

3. ネットワーク関係(VPC, サブネット, ロードバランサー, IGW、ターゲットグループ)の作成

この章はネットワーク関係の設定ですので、すでにあるVPCやサブネットに構築する場合は、スキップして構いません。また、ECS Fargateに固有の設定ではありませんので、AWSの基本的なネットワーク構成に詳しい方はご自身で設定するのがいいかと思います。

3-1. VPCの作成

VPCの名前は、ecs-fargate-demo-vpcなどで適宜つければ良いでしょう。。CIDRブロックもデモなので、10.0.0.0/16で良いでしょう。
image.png

3-2. サブネットの作成

サブネットも10.0.0.0/24などとしておきます。この辺りはシステムによって設計が変わってくるところかと思います。
image.png

この時作成されたVPC IDを変数にセットしておくこととします。

VPC_ID=vpc-xxxxxxxxxxxxxxxxx
PUBLIC_SUBNET1=subnet-xxxxxxxxxxxxxxxxx
PUBLIC_SUBNET2=subnet-xxxxxxxxxxxxxxxxx

3-3 セキュリティーグループの作成

コンテナの 80 番ポートへのリクエストを許可するためのセキュリティグループを作成します。

ECS_FARGATE_DEMO_SG=$(aws ec2 create-security-group --group-name ecs-fargate-demo-SG --description "ECS fargate demo SG" --vpc-id $VPC_ID --region $AWS_REGION)
ECS_FARGATE_DEMO_SG_ID=$(echo $ECS_FARGATE_DEMO_SG | jq --raw-output .GroupId)
aws ec2 authorize-security-group-ingress --group-id $ECS_FARGATE_DEMO_SG_ID --protocol tcp --port 80 --cidr 0.0.0.0/0 --region $AWS_REGION

3-4. ターゲットグループの作成

 ロードバランサーを作成する前にターゲットグループを作成しておきます。BlueGreenデプロイを行うために、2つ作成しておきます。ターゲットタイプは、IP addresses、ターゲットグループ名はなんでも良いですが、tg-ecs-fargate-demo-1tg-ecs-fargate-demo-2としておきます。VPCは3-1で作成したVPCを設定しておきます。Register targetsは現時点では設定しないままにしておきます。(のちほどサービスの作成やCodeDeployにおいて設定していきます)
image.png
image.png
image.png

3-5. ロードバランサーの作成

 ALB(Application Load Balancer)で作成していきます。名前は ecs-fargate-demo-albとしておきます。リスナーはHTTPSを追加したい方は必要に応じて追加してください。ここでは80だけとしておきます。アベイラビリティーゾーンの箇所では、3-1で作成したVPCおよび、3-2で作成したサブネットを選択してください。
image.png

 「手順 2: セキュリティ設定の構成」は、今回はHTTPのみなので、そのまま次へ、「手順 3: セキュリティグループの設定」では、ALB用のセキュリティーグループを用意してもいいですが、ここでは雑ですが、3-3で作成したセキュリティーグループを選択することとします。
 「手順 4: ルーティングの設定」では、3-4で作成したターゲットグループを一つ選択してください。
image.png
 「手順 5: ターゲットの登録」では、あとでターゲットを登録するので、そのまま次の手順に進んでください。

3-6. インターネットゲートウェイのアタッチ

 VPCのインターネットゲートウェイを作成し、VPCにアタッチします。
image.png
 また、ルートテーブルを作成し、ルートの編集でターゲットにインターネットゲートウェイを追加します。また、サブネットの関連付けで3-2で作成したサブネットを関連づけておきます。
image.png

4. ECS Execで利用するKMSキーの作成

 ECS Execは、実行中のコンテナに乗り込んでコマンドを実行できる機能です。以前は、ECS Fargate上でエラーが起こった場合、ログを仕込んだりEC2上にデプロイしてからssmやsshをして、docker execを行ったりしていました。文字で見るからに面倒さが伝わってきます。2021年3月15日に投稿されたAWS記事で可能になったことが公表されています。
ECS Execを利用するために、データチャネルを暗号化するためのKMSキーを作成する必要があります。

KMS_KEY=$(aws kms create-key --region $AWS_REGION)
KMS_KEY_ARN=$(echo $KMS_KEY | jq --raw-output .KeyMetadata.Arn)

aws kms create-alias --alias-name alias/ecs-fargate-demo --target-key-id $KMS_KEY_ARN --region $AWS_REGION
echo "The KMS Key ARN is: "$KMS_KEY_ARN

5. ECS Execのログ周りの設定

5-1. CloudWatchロググループの作成

このロググループは2つのログストリームを有し、 1つはコンテナのstdoutのため、もう一つはECS Execのログを出力するためです。

aws logs create-log-group --log-group-name /aws/ecs/ecs-fargate-demo --region $AWS_REGION

5-2. ECS Exec のログを出力するための S3 バケットの作成

ECS_FARGATE_BUCKET_NAME=ecs-fargate-demo-xxxxxxxxxx # xxxxxxxxxxのところはバケット名が重複しない様に適当な数字を当ててください。
aws s3api create-bucket --bucket $ECS_FARGATE_BUCKET_NAME --region $AWS_REGION --create-bucket-configuration LocationConstraint=$AWS_REGION

6. IAMロールの作成

ECS タスクロールと ECS タスク実行ロールのための 2 つの IAM ロールを作成します。ecs-tasks-trust-policy.jsonという名前でファイルを作成し、以下の内容を記述してください。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": [
          "ecs-tasks.amazonaws.com"
        ]
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

作成したファイルを利用して、ロールを作成します。以下のコマンドを実行してください。

aws iam create-role --role-name ecs-fargate-demo-task-execution-role --assume-role-policy-document file://ecs-tasks-trust-policy.json --region $AWS_REGION
aws iam create-role --role-name ecs-fargate-demo-task-role --assume-role-policy-document file://ecs-tasks-trust-policy.json --region $AWS_REGION

この時点ではロールにポリシーはアタッチされていないので、アタッチしていきます。ecs-fargate-demo-task-execution-roleには、すでに AWS 管理ポリシーとして存在する AmazonECSTaskExecutionRolePolicy をアタッチします。

aws iam attach-role-policy \
    --role-name ecs-fargate-demo-task-execution-role \
    --policy-arn "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"

ecs-fargate-demo-task-roleには、コンテナがSSMを経由してセキュリティ保護済みのセッションをオープンし、 ECS Exec が CloudWatch もしくは S3 (それぞれ上記で作成されたログストリーム、バケット) にログを出力することを許可するポリシーを作成、アタッチします。(元記事のままの説明ですいません。)
まず、ecs-fargate-demo-task-role-policy.jsonという名前でファイルを作成し、以下の内容を記述します。
このとき、以下の項目に関しては、これまでの作業の中で変数に格納してきているので、その値を代入してください。

<AWS_REGION>
<AWS_ACCOUNT_ID>
<ECS_FARGATE_BUCKET_NAME>
<KMS_KEY_ARN>

それぞれの値が不明な場合echo $KMS_KEY_ARNなどして確認してください。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ssmmessages:CreateControlChannel",
        "ssmmessages:CreateDataChannel",
        "ssmmessages:OpenControlChannel",
        "ssmmessages:OpenDataChannel"
      ],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "logs:DescribeLogGroups"
      ],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogStream",
        "logs:DescribeLogStreams",
        "logs:PutLogEvents"
      ],
      "Resource": "arn:aws:logs:<AWS_REGION>:<AWS_ACCOUNT_ID>:log-group:/aws/ecs/ecs-fargate-demo:*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "s3:PutObject"
      ],
      "Resource": "arn:aws:s3:::<ECS_FARGATE_BUCKET_NAME>/*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetEncryptionConfiguration"
      ],
      "Resource": "arn:aws:s3:::<ECS_FARGATE_BUCKET_NAME>"
    },
    {
      "Effect": "Allow",
      "Action": [
        "kms:Decrypt"
      ],
      "Resource": "<KMS_KEY_ARN>"
    }
  ]
}

JSONファイルを作成したら、ポリシーをアタッチするために次のコマンドを実行します。

aws iam put-role-policy \
    --role-name ecs-fargate-demo-task-role \
    --policy-name ecs-fargate-demo-task-role-policy \
    --policy-document file://ecs-fargate-demo-task-role-policy.json

この時点でIAMロールを確認すると二つロールが確認され、クリックして詳細まで確認するとポリシーがアタッチされていることが確認できます。
image.png
image.png

ここまでの間で、ディレクトリ構成は以下の様になっているかと思います。IAMロールの設定に必要な ecs-fargate-demo-task-role-policy.jsonecs-tasks-trust-policy.jsonが追加された形ですね。

.
├── Dockerfile
├── app
│   ├── main.py
│   └── requirements.txt
├── ecs-fargate-demo-task-role-policy.json
└── ecs-tasks-trust-policy.json

以降の作業では、タスク定義のためのecs-fargate-demo-definition.json、サービス作成のためのecs-fargate-demo-service.jsonを追加していきます。

7. ECS関連の設定

 ここでは、ECS関連の設定を行います。ECS クラスターの作成、タスク定義の作成、サービスの作成を行っていきます。ECSの各サービスの概念については、EC2のものでFargateのものではないですがAmazon EC2 Container Service(ECS)の概念整理3が分かりやすいと思います。

7-1. ECS クラスターの作成

 aws ecsコマンドを用いてクラスターを作成します。ECS Execのログ取得のために、executeCommandConfigurationのオプションが必要になります。(必ずしも必須というわけではありません)オプションの詳細は、AWS CLIのドキュメント4に委ねます。

aws ecs create-cluster \
    --cluster-name ecs-fargate-demo-cluster \
    --region $AWS_REGION \
    --configuration executeCommandConfiguration="{logging=OVERRIDE,\
                                                kmsKeyId=$KMS_KEY_ARN,\
                                                logConfiguration={cloudWatchLogGroupName="/aws/ecs/ecs-fargate-demo",\
                                                                   s3BucketName=$ECS_FARGATE_BUCKET_NAME,\
                                                                   s3KeyPrefix=exec-output}}"

7-2. タスク定義の作成

ECSのタスク定義を作成していきます。ecs-fargate-demo-definition.jsonというファイルを作成し、以下のJSONを記述してください。このとき、ここでも以下の項目に関しては、これまでの作業の中で変数に格納してきているので、その値を代入してください。

<AWS_REGION>
<AWS_ACCOUNT_ID>
{
  "family": "ecs-faragte-demo",
  "networkMode": "awsvpc",
  "executionRoleArn": "arn:aws:iam::<AWS_ACCOUNT_ID>:role/ecs-fargate-demo-task-execution-role",
  "taskRoleArn": "arn:aws:iam::<AWS_ACCOUNT_ID>:role/ecs-fargate-demo-task-role",
  "containerDefinitions": [
    {
      "name": "fargate-demo",
      "image": "<AWS_ACCOUNT_ID>.ecr.<AWS_REGION>.amazonaws.com/ecs-fargate-demo:1.0.0",
      "linuxParameters": {
        "initProcessEnabled": true
      },
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "/aws/ecs/ecs-fargate-demo",
          "awslogs-region": "<AWS_REGION>",
          "awslogs-stream-prefix": "container-stdout"
        }
      }
    }
  ],
  "requiresCompatibilities": [
    "FARGATE"
  ],
  "cpu": "256",
  "memory": "512"
}

JSONファイルを作成したら、aws ecs register-task-definition コマンドを実行し、タスク定義を作成します。

aws ecs register-task-definition \
    --cli-input-json file://ecs-fargate-demo-definition.json \
    --region $AWS_REGION

このコマンドを実行すると実行結果としてJSONが出力されます。また、AWSコンソール上でもタスク定義のところに追加がされます。

7-3. サービスの作成

ecs-fargate-demo-service.jsonファイルを作成し、以下のJSONを記述します。taskDefinitionはaws ecs register-task-definition コマンドの実行結果からも確認できますし、AWSコンソール上のタスク定義からも確認できます。
image.png
loadBalancerstargetGroupArnは3-4で作成したターゲットグループのArnを指定します。AWSコンソール上からでも確認することができます。
image.png

{
  "taskDefinition": "ecs-fargate-demo:1",
  "loadBalancers": [
    {
      "targetGroupArn": "arn:aws:elasticloadbalancing:us-west-1:<AWS_ACCOUNT_ID>:targetgroup/tg-ecs-fa-ecs-fargate-demo-1/7672b26fea125dfd",
      "containerName": "fargate-demo",
      "containerPort": 80
    }
  ],
  "deploymentController": {
    "type": "CODE_DEPLOY"
  },
  "desiredCount": 1
}

JSONファイルを作成したら、aws ecs create-serviceコマンドでサービスを作成します。コマンドのオプションの詳細はAWS CLI ドキュメントに委ねますが、--enable-execute-commandをつけることで、ECS Execが使える様になります。また、platform-versionはLATESTを指定していますが、ECS Execを利用するためには、1.4.0以上である必要があります。

aws ecs create-service \
    --cluster ecs-fargate-demo-cluster \
    --service-name ecs-fargate-demo \
    --enable-execute-command \
    --launch-type FARGATE \
    --platform-version LATEST \
    --region $AWS_REGION \
    --desired-count 1 \
    --network-configuration awsvpcConfiguration="{subnets=[$PUBLIC_SUBNET1, $PUBLIC_SUBNET2],securityGroups=[$ECS_FARGATE_DEMO_SG_ID],assignPublicIp=ENABLED}" \
    --tags key=environment,value=production \
    --cli-input-json file://ecs-fargate-demo-service.json

 クラスター > ecs-fargate-demo-cluster > サービス: ecs-fargate-demo のタスクタブで「前回のステータス」というところが「RUNNING」となるまで待ちます。
RUNNINGになれば、コンテナ内に入ることができます。そのためには、まず、タスクIDを確認しましょう。タスクIDは同じくタスクタブで確認できます。
image.png
 タスクIDが把握できたら、以下のコマンドでSSMすることができます。下記コマンドの一番最後のオプション--taskにタスクIDを指定してください。container名は、タスク定義の詳細のコンテナの定義で確認することができます。

aws ecs execute-command  \
    --region $AWS_REGION \
    --cluster ecs-fargate-demo-cluster \
    --container fargate-demo \
    --command "/bin/bash" \
    --interactive \
    --task 7816f2xxxxxxxxxxxxxxxxxxxxxxxxxx

ここまででECS Fargateでのアプリケーションを公開し、コンテナないにSSMすることができました。
では、最後にブラウザ上でも確認してみましょう。ALBを作成すると自動的にDNS名が設定されます。こちらをクリックすれば、ブラウザ上からのレスポンスを確認することができます。

image.png
うまく設定できていれば{"Hello": "World"}が確認できるはずです。
image.png
うまくいかない場合は、サービスのイベントやログの中身を確認してみてください。
image.png

8. デプロイの設定(Blue/Green)

 次にデプロイするための設定を行なっていきます。サービスは、AWSコンソールから作成する際、ローリングアップデートとBlue/Greenデプロイメント(AWS CodeDeploy を使用)を選ぶことができますが、今回、サービス作成時のJSONで、CODE_DEPLOYを指定してますので、Blue/Greenデプロイメントの方になります。

  "deploymentController": {
    "type": "CODE_DEPLOY"
  },

8-1. BlueGreenデプロイをするためのCodeDeployの設定

 CodeDeploy > アプリケーション から 「アプリケーションの作成」をクリックしアプリケーションを作成します。
 名前はなんでも良いですが、 AppECS-ecs-fargate-demo-cluster-ecs-fargate-demo とでも入れておけばいいかなと思います。AWSコンソール上からサービスを作成する場合は、このような命名規則で作成されるのでそれに合わせた形になります。コンピューティングプラットフォームは Amazon ECS にします。
image.png
CodeDeployでアプリケーションを作成したら、デプロイグループを作成します。
 デプロイグループ名は、DgpECS-ecs-fargate-demo-cluster-ecs-fargate-demo とでもしておきましょう(こちらもAWSコンソール上から自動で作成した場合の命名と同じ)。
 サービスロールはecsCodeDeployRoleを選択。
 環境設定でのECSクラスターとECSサービス名は、7-1と7-3で作成したものを選んでください。
 Load Balancerは、3-5で作成したものを選び、ターゲットグループ1と2は、3-4で作成した2つをそれぞれ選択してください。BlueGreenデプロイを行うので、片方のターゲットグループは現行のアプリケーション、もう片方の方に新しいアプリケーションをデプロイした上でターゲットグループをつけかえるので、2つとも設定が必要になります。
 デプロイ設定のところは、状況に応じて設定してください。今回は、「すぐにトラフィックを再ルーティング」のままで進めます。
image.png

8-2. デプロイの流れ

 ここまででデプロイするための準備が整いました。ここでは、aws cliを用いたデプロイの全体の流れについて順を追って説明していきます。
まず、app/main.py{"Hello": "World"}{"Hello": "World!!!"}とでも変えておき、これをデプロイすることにします。

 @app.get("/")
 def read_root():
-    return {"Hello": "World"}
+    return {"Hello": "World!!!"}

8-2-1. Docker imageの作成〜ECRへの登録

docker build -t ecs-fargate-demo .
docker tag ecs-fargate-demo ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/ecs-fargate-demo:1.0.1 # タグ付け。インクリメントするなど、versionがわかる様にしておく。
docker push ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/ecs-fargate-demo:1.0.1

8-2-2. タスク定義のJSON更新

 タスク定義を作成する際に利用したJSONファイル(ecs-fargate-demo-definition.json)のimageのタグ部分を8-2-1でつけたタグに変更します。

     {
       "name": "fargate-demo",
-      "image": "<AWS_ACCOUNT_ID>.dkr.ecr.us-west-1.amazonaws.com/ecs-fargate-demo:1.0.0",
+      "image": "<AWS_ACCOUNT_ID>.dkr.ecr.us-west-1.amazonaws.com/ecs-fargate-demo:1.0.1",
       "linuxParameters": {
         "initProcessEnabled": true
       }

8-2-3. タスク定義の登録

 aws ecs register-task-definitionコマンドでタスク定義を登録します。同じタスク定義名でregister-task-definitionを行うと、自動的にリビジョンが+1インクリメントされます。最初にタスク定義を作成するときと同じコマンドであることに気持ち悪さを感じつつも、進めていきます。

aws ecs register-task-definition \
    --cli-input-json file://ecs-fargate-demo.json \
    --region $AWS_REGION

この時、コマンド結果で出力されたJSONを見て、タスク定義のリビジョンを確認しておきます。ここで確認を忘れても、AWSコンソールのタスク定義(Task definitions)から確認することができますので慌てる必要はありません。
image.png

8-2-4. Codedeloyのデプロイメント作成

 以下のコマンドを実行することでデプロイすることができます。TaskDefinitionのところでecs-fargate-demo:2とありますが、タスク定義の登録の際に確認したリビジョンを指定してあげてください。
また、application-namedeployment-group-nameについては、CodeDeployで設定した値となります。--revisionのオプションの値のJSONが分かりにくいので、もう少しいい方法がないか検討中です。

aws deploy create-deployment \
  --application-name AppECS-ecs-fargate-demo-cluster-ecs-fargate-demo \
  --deployment-group-name DgpECS-ecs-fargate-demo-cluster-ecs-fargate-demo \
  --region $AWS_REGION \
  --revision '{"revisionType": "AppSpecContent", "appSpecContent": {"content": "{\"version\": 1, \"Resources\": [{\"TargetService\": {\"Type\": \"AWS::ECS::Service\", \"Properties\": {\"TaskDefinition\": \"arn:aws:ecs:$AWS_REGION:$AWS_ACCOUNT_ID:task-definition/ecs-fargate-demo:2\", \"LoadBalancerInfo\": {\"ContainerName\": \"fargate-demo\", \"ContainerPort\": 80}}}}]}"}}'

 コマンドを実行したら、AWSコンソールのCodeDeployからデプロイの進捗状況を確認することができます。
image.png
 7-2の最後に確認したALBのDNS名にアクセスして確認してみると{"Hello": "World!!!"}が表示されているのが家訓できるかと思います。
image.png

なお、サービスがBlueGreenではなく、ローリング更新の場合、aws ecs update-serviceコマンドで実行できます。

aws ecs update-service \
    --cluster ecs-fargate-demo-cluster \
    --service ecs-fargate-demo \
    --task-definition "ecs-fargate-demo:3"

9. ログの確認

 まず、サーバーサイドのエラーがどの様にログに表現されるかを確認するため、app/main.pyread_itemを次の様に変更し、0で割ると発生するエラーを意図的に生じさせてみます。

 @app.get("/items/{item_id}")
 def read_item(item_id: int, q: Optional[str] = None):
+    1 / 0
     return {"item_id": item_id, "q": q}

 この修正をしたのち、Docker Imageの作成から8のデプロイの一連の流れを実施します。その後、 /items/1 などにアクセスしてみると、画面上は、 Internal Server Error と表示されます。この辺りはエラーハンドリングを行っていないのでこうなります。
 次に、AWSコンソールからタスクのログを確認します。ログのタブでをクリックするとログが表示され、少しわかりにくいですが、ZeroDivisionError: division by zeroというエラーが表示されていることが確認できます。
image.png

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
20
Help us understand the problem. What are the problem?