【AWS】ECRへpushされたimageから、ECSでコンテナを起動

2024/10/25に公開

はじめに

この記事では、AWSのコンテナ運用に関して、主にECSについて取り上げています。前回、ECRへpushを行なったため、今回はECSでタスクを起動する手順について、記述します。

<前回までの記事>
【Docker】【AWS】ECRへpushするまでの作業: https://zenn.dev/jun202407060017/articles/3ff7208354a205

対象の方

AWSで、ECRやECSなどコンテナサービスを使ったクラウドインフラの初期キャッチアップを行いたい方。
運用ではAWSを触っているが、自力でAWSのECRやECSをこれから設定してみたい、という方。
従来のサーバーは触ったことがあるが、dockerを絡めたコンテナ系のクラウドサービスはこれから触る予定という方

環境

AWS
AmazonLinux2023
macbookpro2019

内容

前回までの記事で、ECRのプライベートリポジトリへ簡素なpythonファイルをpushし、AWS上のコンソールで確認できるところまで作業を行いました。

ECSへdeploy可能なECRが作成できた段階です。

ECRへpush後、pushしたEC2インスタンス内で、ssm-user(rootは✖️)で下記のコマンドを実行

aws ecr describe-images --repository-name <your-ecr-repository-name>

ここで、ECRへpushした<your-ecr-repository-name>が表示されていれば、pushは成功していて、imageがecrのrepositoryにある状態とわかります。

ここで、ECSでコンテナを走らせるにあたり、事前キャッチアップを行います。
下記の順でAWSの画面から、概要をおさえます。

概要

クラスター ・・・クラスターを作成すると、下記の3つの操作ができるようになる。(最大)
サービス  ・・・タスクを定期的に扱えるよう設定として残せるイメージ(大)
タスク   ・・・タスク定義を元に、1つのコンテナサービスを起動・停止させる管理用の機能(中)
タスク定義 ・・・使用するECRのリポジトリ指定(小)

クラスター
「ECS」→「クラスター」から移動後の画面で、クラスターをUIに沿って作成しておきましょう。

下記の3つの操作が確認できるようになります。

サービス
「タスクを定期的に扱えるよう設定として残せるイメージ(大)」のイメージをつけることにとどめ、割愛。

タスク

タスクでは、「デプロイ」ボタンを押して確認できるように、「サービスの作成・更新・タスクの実行」が選択でき、下記の一覧に表示されている中から、「タスク定義」を選択し、上記のいずれかからデプロイ先を選択することで、「サービスの作成・更新・タスクの実行」が行えます。
「タスク定義」を一度作成してしまえば、今後再設定をスキップし、タスクを何度も起動できます。

タスク定義

タスク定義を右上のオレンジのボタン、「新しいタスク定義の作成」から下記の流れで作成します。

ECSの最小単位「タスク定義」の作成1

「ECSの画面」→「左側のサイドバー」→「タスク定義」→「新しいタスク定義の作成」を選択します。

タスク定義ファミリー
ECR_second
インフラストラクチャの要件
AWS Fargate
オペレーティングシステム/アーキテクチャ
Linux/X86_64
タスクサイズ
1vCPU
メモリ
3GB
条件付きタスクロール  デフォルト
タスク配置 - オプション デフォルト

コンテナの詳細
名前      <your-ecr-repository-name>
イメージ URI [AWSaccount識別用の12桁の数値].dkr.ecr.ap-northeast-1.amazonaws.com/[dockerhub-accountname]/[repo-name]:[tag-name]
必須コンテナ  はい

ここで、「コンテナの詳細」にあたる情報を、ECR上で確認します。下記の画面の、

[dockerhub-accountname]/my-python-app
[dockerhub-accountname]/[repo-name]

にあたる部分が、「名前」の<your-ecr-repository-name>になっています。

次に、プライベートレジストリのプライベートレジストリ認証をONにすると、「Secrets Manager の ARN または名前」が求められます。ここで、「Secrets Manager」の設定画面へ移り、AWSのUIに沿って「Secrets Manager」を作成します。

Secrets Managerの作成


上記の詳細画面で、「シークレットのARN」が確認できます。ただし、末尾に英数の羅列が6つ分、ハイフンと一緒にくっついています。

[入力するところ]-99DDeK

「シークレットのARN」から-99DDeKにあたる部分を取り除き、arnを下記の入力欄に入力します。

Secrets Manager の ARN または名前

今回は、初期キャッチアップのため、Secrets Managerで固定値で、トークン発行されたパスワードをAWSに登録します。12時間後にトークンの仕様により、認証が行えなくなってしまいますが、12時間以内であれば可能です。

EC2インスタンスへ入ります。

cd
pwd

で、ホームディレクトリ(/home/ssm-userか、/home/ec2-user)に居ることを確認します。

aws ecr get-login-password --region ap-northeast-1 > secretmanager_auth.txt

などで、その時にトークン生成されるパスワードを、.txtファイルへ保存し、

cat secretmanager_auth.txt

で覗きましょう。表示された結果をコピーし、

「その他のシークレットのタイプ」を選択し、
キー/値のペアを、
username : AWS
password : [cat secretmanager_auth.txt]

「シークレットの名前」
ecr-pullthroughcache/[任意の名前]

で設定し、そのほかは全てデフォルトで保存します。
この時の
ecr-pullthroughcache/[任意の名前]
は後ほど使います。

タスク定義に戻り「ECSの最小単位「タスク定義」の作成1」の続きを作成していきます。

ECSの最小単位「タスク定義」の作成2

さらに進んで、

ポートマッピング
リソース割り当て制限
環境変数
ログ収集

をすべてデフォルト(初期設定)のまま、今回は作成します。

手動でのタスク定義が作成できました。


この後の方針としては、「デプロイ」から「タスクの実行」を行い、後ほど確認可能な「ログ」から、起動されたかどうかの確認を行います。

一度手動設定したタスク定義から「jsonのダウンロード」を行い、細かな設定値を変更後、再度タスク定義を作成する場合

先ほど行った、手動でのタスク定義には時間がかかります。2回目以降、「ECS」の「タスク定義」の画面で、下部に見られる「json」から、「jsonのダウンロード」を選択・実行し、jsonファイルを取得します。

上記のjsonの設定値を修正し、「ECRへpushされた同じコンテナを起動したい」かつ「タスク定義の設定値を変更したい」といった、「利用するリポジトリは同じだが、コンテナ起動方法のみ修正が必要」といった場合に、上記の方法が役立ちます。

まだ未確認の状態ですが、「JSONのダウンロード」「AWS CLI入力をダウンロード」「クリップボードにコピー」のいずれも、下記の「値」を「設定対象の値」へ書き換えると、機能するのではないかと思われます。まだ、筆者は実動作は未確認で、デプロイで起動した時のjsonと、上記の3つのコマンドで取得できるjson形式のデータで、「必要な固有の値」の差分を記述しています。
<修正箇所>
family
containerDefinitions 内の name
containerDefinitions 内の image
containerDefinitions 内の portMappings 内の name
containerDefinitions 内の logConfiguration 内の options 内の awslogs-group
executionRoleArn

{
    "family": "[任意の名前1]",
    "containerDefinitions": [
        {
            "name": "[任意の名前2]",
            "image": "[AWS_account_id(12桁の数値)].dkr.ecr.ap-northeast-1.amazonaws.com/[dockerhub-accountname]/[repo-name]:[tag-name]",
            "memory": 512,
            "cpu": 256,
            "essential": true,
            "portMappings": [
                {
                    "name": "[任意の名前2]-80-tcp",
                    "containerPort": 80,
                    "hostPort": 80,
                    "protocol": "tcp",
                    "appProtocol": "http"
                }
            ],
            "environment": [],
            "environmentFiles": [],
            "mountPoints": [],
            "volumesFrom": [],
            "ulimits": [],
            "logConfiguration": {
                "logDriver": "awslogs",
                "options": {
                    "awslogs-group": "/ecs/[任意のロググループ]",
                    "mode": "non-blocking",
                    "awslogs-create-group": "true",
                    "max-buffer-size": "25m",
                    "awslogs-region": "ap-northeast-1",
                    "awslogs-stream-prefix": "ecs"
                },
                "secretOptions": []
            },
            "systemControls": []
        }
    ],
    "executionRoleArn": "arn:aws:iam::[AWS_account_id]:role/ecsTaskExecutionRole",
    "networkMode": "awsvpc",
    "volumes": [],
    "placementConstraints": [],
    "requiresCompatibilities": [
        "FARGATE"
    ],
    "cpu": "1024",
    "memory": "3072",
    "runtimePlatform": {
        "cpuArchitecture": "X86_64",
        "operatingSystemFamily": "LINUX"
    }
  }

では、「タスク定義」が作成できたら、ECRからECSへデプロイし、タスクを起動させてみましょう。

タスクの起動(ECRからECSへの「デプロイ(deploy)」及び「コンテナの起動」)

「クラスター」の画面で作成したクラスターの名前を選択し、「タスク」→「新しいタスクの実行」から、「タスクの設定画面」へ移ります。

既存のクラスター: デフォルト(変更不可)
コンピューティング設定(アドバンスト): 起動タイプ
起動タイプ: FARGATE

プラットフォームバージョン: LATEST
アプリケーションタイプ: タスク
ファミリー: 作成したタスクのfamily名を選択
リビジョン: 1(最新) ・・・ファミリーを選択した後のデフォルト設定値のまま
必要なタスク: 1(今回は初期キャッチアップのため、検証など都度応じて増減)

他はそのまま何も触らず、デフォルト設定値のまま「作成」を選択。
タスクが起動します。

今回のアプリケーションの場合、「Hello,Docker!」を出力した後、そのままコンテナが停止します。
その様子を確認してみましょう。

タスクが起動できたかどうかは、「クラスター」→「クラスター名」→下へスライドし、「タスク」→「希望するステータスをフィルタリング」→「あらゆる希望するステータス」で、停止済みのコンテナも、表示されます。

「タスク」の下の「f288e...」をクリックし、「ログ」を確認すると、「Hello,Docker!」

最初の記事
【Docker】最小限の仕様確認
https://zenn.dev/jun202407060017/articles/c21bd3a9be8cfb
で確認した、コンテナが起動し、「Hello,Docker!」が確認できた後、コンテナは終了する。といった旨の動きが、上記ログで確認できました。

つまり、ECSでコンテナを立ち上げ、起動時にDockerfileを読み込み、

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

により、app.pyのpythonファイルを実行し、

print("Hello, Docker!")

が出力された後、コンテナを停止させる、といった動きを行えました。

次は、ECSコンテナの中に、リクエストした際にIPを返すpythonAPIをdeployし、指定時間の間ECSを起動し、そのリクエスト数やCPU使用率、loadaverageなどのログをcloudwatchで確認し、リクエスト数の上限を算出するなど、実運用を意識したECR/ECSの仕様把握・確認とともに、記事にできれば良いと考えています。
長文となりましたが、最後まで読んで頂き、ありがとうございました!

Discussion