Alice in Cloud

外資系ITコンサルタントのブログです

FlaskアプリをAWS ACMでSSL化しながらFargateで公開する全ステップ (後編)

FlaskアプリをAWS ACMSSL化しながらFargateで公開する全ステップの後編です。Flaskアプリ公開まで一気に行きましょう。

アーキテクチャ(再掲)


実装手順(グレーアウト部分は前編で実施済み)
Step 項目 対象サービス
1 ドメイン発行Route 53
2 証明書発行ACM、Route53
3 VPC作成VPC
4 セキュリティグループ作成セキュリティグループ
5 VPCエンドポイント作成VPCエンドポイント
6 ALB作成ALB
7 コンテナイメージ作成 ローカルマシンなど
8 レポジトリ作成とイメージアップロード ECR
9 コンテナ作成 ECS/Fargate
10 ALBのリスナー修正 ALB
11 CNAMEレコード登録 Route 53
12 テスト テスト
7. コンテナイメージ作成

Flaskのコンテナイメージを作成します。サンプルとしてHello Worldを表示するFlaskのイメージをローカルマシンで作成します。

ディレクトリ構造

flask-testというフォルダを作成し、その中に次のような階層でコードを配置します。

dockerfile
FROM python:3.8.11-slim-buster

RUN pip install --upgrade pip
RUN pip install flask==2.0.1
RUN pip install pillow==8.00

ENV PORT 80

WORKDIR /app

COPY ./app/ /app

CMD ["python", "app.py"]
app.py
import os 

from flask import Flask


port = int(os.environ['PORT'])
app = Flask(__name__)

@app.route('/')
def index():
   return 'Hello World!!!'

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

dockerをまだ導入していない方は、Docker Desktopを先に導入してください。私の実行環境はMacですが、ターミナルを開いてflask-hwの階層に移動し、以下コマンドを投入してください。

% docker image build -t flask-hw .
% docker image ls
REPOSITORY                                                       TAG       IMAGE ID       CREATED          SIZE
flask-hw                                                         latest    2d783442b531   10 seconds ago   146MB

続いて以下コマンドを実行しコンテナを起動させます。

% docker container run --rm -d -p 5000:80 -v ${PWD}/app:/app --name flask-hw flask-hw

ブラウザでlocalhost:5000と入力すると以下の様にHello Worldが表示されるはずです。今しがた作ったイメージからコンテナを作成し、ポート5000→80と変換しアクセスしています。

無事ローカルでテストできたら、稼働しているコンテナを停止しておきます。

% docker ps -a                                                                       
CONTAINER ID   IMAGE      COMMAND           CREATED         STATUS         PORTS                                   NAMES
06f87ee03035   flask-hw   "python app.py"   7 minutes ago   Up 7 minutes   0.0.0.0:5000->80/tcp, :::5000->80/tcp   flask-hw
% docker stop  06f87ee03035
06f87ee03035
8. レポジトリ作成とイメージアップロード

ECRのプライベートレポジトリを作成し、7で作成したイメージをアップします。ローカルマシンでAWS CLIを叩く必要があるので、導入されていない方はインストールしてください。

Amazon ECR < リポジトリ < リポジトリを作成から、リポジトリを作成していきます。リポジトリ名は先程作成したイメージに合わせると後の手順が簡単です。イメージスキャンや暗号化は推奨項目ではありますが、今回は主題と逸れるので省略します。

リポジトリが作成できたら「プッシュコマンドの表示」をクリックし、プッシュコマンドを表示します。表示されたコマンドをローカルマシンのターミナルで実行するだけですが、ECRにイメージをプッシュするための権限が必要です。簡単な例として、必要な権限を付与したIAMユーザを作成し、アクセスキーを発行してaws configureコマンドで設定しておきましょう。

コマンド実行結果はこんな感じです。

% aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com
Login Succeeded
% docker build -t flask-hw .
[+] Building 2.2s (12/12) FINISHED                                                                                                                                      
 => [internal] load build definition from Dockerfile                                                                                                               0.0s
 => => transferring dockerfile: 177B                                                                                                                               0.0s
 => [internal] load .dockerignore                                                                                                                                  0.0s
 => => transferring context: 2B                                                                                                                                    0.0s
 => [internal] load metadata for docker.io/library/python:3.8.11-slim-buster                                                                                       2.1s
 => [auth] library/python:pull token for registry-1.docker.io                                                                                                      0.0s
 => [1/6] FROM docker.io/library/python:3.8.11-slim-buster@sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx                                 0.0s
 => [internal] load build context                                                                                                                                  0.0s
 => => transferring context: 193B                                                                                                                                  0.0s
 => CACHED [2/6] RUN pip install --upgrade pip                                                                                                                     0.0s
 => CACHED [3/6] RUN pip install flask==2.0.1                                                                                                                      0.0s
 => CACHED [4/6] RUN pip install pillow==8.00                                                                                                                      0.0s
 => CACHED [5/6] WORKDIR /app                                                                                                                                      0.0s
 => CACHED [6/6] COPY ./app/ /app                                                                                                                                  0.0s
 => exporting to image                                                                                                                                             0.0s
 => => exporting layers                                                                                                                                            0.0s
 => => writing image sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx                                                                  0.0s
 => => naming to docker.io/library/flask-hw                                                                                                                        0.0s

Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them
% docker tag flask-hw:latest 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/flask-hw:latest
% docker push 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/flask-hw:latest
The push refers to repository [123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/flask-hw]
xxxxxxxxxxxx: Pushed 
xxxxxxxxxxxx: Pushed 
xxxxxxxxxxxx: Pushed 
xxxxxxxxxxxx: Pushed 
xxxxxxxxxxxx: Pushed 
xxxxxxxxxxxx: Pushed 
xxxxxxxxxxxx: Pushed 
xxxxxxxxxxxx: Pushed 
xxxxxxxxxxxx: Pushed 
xxxxxxxxxxxx: Pushed 
latest: digest: sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx size: 2417
% 

ECR側でもイメージがプッシュされました。

9. コンテナ作成

いよいよアプリが稼働するコンテナを作っていきます。AWSでのコンテナ実行環境としてECSを利用し、ホストマシンの管理が不要なFargateを使っていきます。
ECS<クラスター<クラスターの作成から、クラスターを作成します。Fargateを使いたいので、ネットワーキングのみを選択します。

前編作成したVPCを利用するため、VPCの作成はチェックしません。

続いてタスク定義<新しいタスクの定義を行います。Fargateを選択します。

パラメータを設定していきます。特にコンテナがAWS操作をするわけではないので、タスクロールは不要です。

CPUやメモリをお好みの値を入れて、「コンテナの追加」をクリックします。

ECRへ先程アップしたイメージのURIをコピーして入力します。またALBでSSL終端された後、ALBからコンテナへのアクセスはHTTPとなるためポートマッピングに80(HTTP)を入力しておきます。残りはデフォルトのままとし、コンテナの追加<タスク定義の作成と進みタスク定義作成を完了させます。

作成したタスクをもとにコンテナを作成するためにサービスを作成します。起動タイプはFargateとし、任意のサービス名とタスクの数(コンテナの数)を入力し次に行きます。


続いてネットワーク関連の設定です。前編で作成したVPC、サブネット(コンテナ用なのでプライベートサブネットx2)、SG(ECS用)を選択します。

続いてロードバランサーの設定です。ここが一番わかりにくい。。。ALBを選択するとタスク定義で設定したポート(今回は80番)の設定ができるので、「ロードバランサーの追加」をクリックします。

画像のように設定します。リスナーポートとはインターネットからALBへのアクセスを受け付けるポートのことなので本当はHTTPSなのですが、なぜかACMの証明書が選択できません。一旦HTTPでリスナーポートの設定をし、後ほど設定変更します。

Auto-scalingはスキップし、サービスの作成を完了します。サービス作成が完了すると、サービスで設定した数のタスク(コンテナ)が自動で作成されます。

上手くNW周りの設定が出来ていればタスクがRUNNINGとなります。ここがPENDINGのままになっていたり、何度もタスクの作成削除が繰り返されて不安定になる場合NW周りの設定に問題がある可能性があります。挙動として、VPC内のクラスターがECRやS3にエンドポイント経由でアクセスしイメージを取得してくるので、エンドポイントやSGなどで通信がブロックされているなどが考えられます。

10. ALBのリスナー修正

リスナーポートを443に修正するため、前編で作成したALBを選択し、ECSが作成したリスナーの設定を変更します。

プロトコルがHTTPになっていると思いますのでHTTPSに修正します。

下段のACMの部分で前編で作成したACMの証明書を選択し、変更内容を保存します。

またHTTPでリクエストが有った場合にHTTPSへリダイレクトする処理を追加します。リスナー追加をクリックし次のように設定します。これによりHTTPSでの接続を強制できます。

設定が完了したら、ALBの説明タブの中からDNS名をコピーしておきます。

11. CNAMEレコード登録

さて、いよいよ最後のステップです。Route 53に遷移し、ACMを発行したFlask アプリのドメインをCNAMEレコードでALBのDNS名に紐付けます。


12. テスト

ここまで来たらテストしてみましょう。ステップ11でRoute 53に登録したドメインに対してブラウザで接続してみましょう(https://www.xyz.comなど)。Hello Worldの画面が表示されれば無事設定完了です。