🍇

Amazon ECSでFargateを使ってNginxコンテナを起動し「Hello Wolrd」を表示してみる

2023/10/12に公開

概要

表題の通り、ECSでNginxコンテナを起動し「Hello Wolrd」を表示してみます。

さらに、ALBと連携することでALBにアクセスしたら「Hello Wolrd」が表示されるようにしてみます。

構成図

Dockerイメージを作成する

今回使用するnginxコンテナのイメージをビルドします。

このイメージは、後ほどECRにプッシュしていきます。

Amazon ECR(Amazon Elastic Container Registry)とは

AWSが提供するコンテナイメージを管理保管できるサービスです。

https://docs.aws.amazon.com/ja_jp/AmazonECR/latest/userguide/what-is-ecr.html

イメージをビルドする

Dockerfile
FROM nginx:latest

COPY index.html /usr/share/nginx/html

EXPOSE 80
index.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
  <h1>Hello World!!</h1>
</body>
</html>

上記の2つのファイルを作成し、下記コマンドを実行します。

bash
docker build -t nginx-test .

ECRにDockerイメージをプッシュする

ビルドしたnginx-testというイメージをECRにプッシュしていきます。

ECRにリポジトリを作成する

プッシュするには、そのイメージ用のリポジトリが必要です。

AWSコンソールからリポジトリのページに行き、「リポジトリを作成」します

  • リポジトリ名: nginx-test
  • 他の設定はデフォルトのまま

AWS CLIのインストールと初期設定

長くなるので別記事にまとめました。

https://zenn.dev/shimiyu/articles/a582ff12087d32

Dockerイメージにタグ付けする

bash
$ docker tag nginx-test:latest <your-account-id>.dkr.ecr.ap-northeast-1.amazonaws.com/nginx-test:latest
bash
$ docker images
REPOSITORY                                                     TAG       IMAGE ID       CREATED       SIZE
<your-account-id>.dkr.ecr.ap-northeast-1.amazonaws.com/nginx-test              latest    57289*******   7 days ago    192MB
nginx-test                                                     latest    57289*******   6 days ago    192MB

<your-account-id>.dkr.ecr.ap-northeast-1.amazonaws.com/nginx-testという名前のイメージが追加されました。

タグ付けとは何か?

タグ付けは、既存のイメージに新しい名前(タグ)を付けることを指します。イメージが新しく作成されたようにも見えますが、ローカルに存在するイメージに新しい名前(タグ)が増えただけです。そのため、"IMAGE ID"を見ると、全く一緒であることが分かります。

bash
$ docker tag 元となるイメージ[:TAG] タグ付けされた後のイメージ名[:TAG]

何のためにタグ付けするのか?

前提として、ECRにプッシュするには次のような形式である必要があります。

<your-account-id>.dkr.ecr.<region>.amazonaws.com/<repository-name>

この形式に従っていないイメージはそもそもプッシュできません。

ただ、ローカルでビルドを行う際に、最初からECRの形式に従った長々しい名前を付けるのは微妙です。そこで、最初にビルドする時は汎用的な名前を付けておき、実際にECRにプッシュする前にタグ付けをして、ECRにプッシュ可能な名前とします。

Docker イメージをプッシュする

DockerイメージをECRにプッシュする

今タグ付けしたDockerイメージをECRにプッシュします。

bash
$ docker push <your-account-id>.dkr.ecr.ap-northeast-1.amazonaws.com/nginx-test:latest
The push refers to repository [<your-account-id>.dkr.ecr.ap-northeast-1.amazonaws.com/nginx-test]
626d2ef86498: Pushed 
e005f3c090e0: Pushed 
3ccc534e961c: Pushed 
def14911cf6a: Pushed 
78cdcd2ba4bb: Pushed 
8cc0ce26d320: Pushed 
dcb816d1345e: Pushed 
1c3daa065742: Pushed 
latest: digest: sha256:***** size: 1985

AWSコンソールから確認すると、プッシュしたイメージが追加されたことが確認できます。

ALBの準備

ALBの作成については割愛します。下記記事内でALBを作成していますので、参考にしてください。

https://zenn.dev/shimiyu/articles/80bd299fbd404d#1.-alb-(application-load-balancer)

ターゲットグループを作成する

AWSコンソールからターゲットグループのページに行き、ターゲットグループを作成します。

ターゲットグループは、ALBがトラフィックを振り分ける対象先のことを指します。今回、ALBにトラフィックが到達したら、nginxコンテナを起動するECSにトラフィックを振り分けてもらいたいです。そのため、ECS用のターゲットグループを作成します。

  • 基本的な設定
    • ターゲットタイプの選択: IPアドレス
    • ターゲットグループ名: ecs-target-group
    • プロトコル: HTTP
    • ポート: 80
    • IPアドレスタイプ: IPv4
    • VPC: 対象となるECSが属するネットワークを選択
    • プロトコルバージョン: HTTP1
  • IPアドレス
    • 何も指定しない

それぞれの項目について補足して説明します。

ターゲットタイプの選択

Fargateを使用する場合、ターゲットタイプはIPアドレスを選択する必要があります。

プロトコルとポートについて

ターゲットグループを作成するときのプロトコルとポートは、ALBがトラフィックを転送する先(ターゲット)のプロトコルとポートを指します。今回のケースでは、ECSタスク(コンテナ)のプロトコルとポート(nginxコンテナがリッスンしている80番ポート)です。

IPアドレス

IPアドレスを手動で入力する箇所がありますが、ここは空欄で問題ありません。Fargateを使用する場合、特定のIPアドレスを指定する必要がないからです。

タスクが起動するとタスク内で起動するコンテナ(今回はnginxコンテナ)に動的にIPアドレスが割り当てられるのですが、ECSサービスが自動でターゲットグループにIPアドレスを登録してくれます。そのため、ターゲットグループに手動でIPアドレスを指定する必要はないのです。

ただし、ECSサービスを作成するときに、ターゲットタイプがIPアドレスのターゲットグループを指定する必要はあります。後ほどECSサービスを作成するときに、ここで作成したecs-target-groupを指定します。

ターゲットグループとロードバランサを関連付けする

ALBにHTTPアクセスがあった時にECSにトラフィックが振り分けられるようにするため、以下の要領でリスナーを追加します。

  • 対象のロードバランサを選択
  • 「リスナーとルール」タブを選択
  • リスナーの追加
    • プロトコル / ポート: HTTP 80
    • アクションの種類: ターゲットグループへ転送
    • ターゲットグループの選択: ecs-target-group

ECS

タスク定義の作成

AWSコンソールのECSのページに行き、タスク定義を選択します。

タスク定義とは

1つ以上のコンテナとその設定を定義する場所です。

docker-compose.ymlで定義する内容とほぼ同じになります。

タスクとは

タスク定義の内容に従って、実際に実行されるコンテナのインスタンスのことです。

インフラストラクチャの要件

  • タスク定義ファミリー: nginx-test
  • 起動タイプ: AWS Fargate
  • オペレーティングシステム/アーキテクチャ: Linux/X86_64
  • タスクサイズ
    • CPU: .5vCPU
    • メモリ: 1GB
  • タスクロール: なし
  • タスク実行ロール: 新しいロールの作成

nginxで「Hello world!」を表示するだけなので、最小構成とします。

タスクロールとは?

タスクロールは、タスク内のコンテナがAWSリソースと通信する際に使用するロールで、タスクが起動した後に使用されるロールです。

https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/task-iam-roles.html

タスク実行ロールとは?

タスク起動時に使用されるロールです。

https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/task_execution_IAM_role.html

コンテナの定義

  • コンテナの詳細
    • 名前: nginx
    • イメージURI <your-account-id>.dkr.ecr.ap-northeast-1.amazonaws.com/nginx-test
  • ポートマッピング
    • コンテナポート: 80
    • プロトコル: TCP
    • ポート名: nginx-80-tcp(自動設定されたもの)
    • アプリケーションプロトコル: HTTP

イメージURIには、先ほどプッシュした<your-account-id>.dkr.ecr.ap-northeast-1.amazonaws.com/nginx-testを指定します。コンテナポートにはnginxデフォルトの80番を指定します。

ポートマッピングと言いつつ、ホスト側ポートを指定する箇所がありません。これは、Fargateがホストポートを動的に割り当てるためです。Fargateを使用すると、コンテナのインスタンスをユーザー側で管理する必要はなく、ポートの衝突を心配する必要もないようです。

  • リソース割り当て制限
    • CPU: .5
    • メモリのハード制限: 1
    • メモリのソフト制限: 1

コンテナ1つ1つレベルでのリソース制限の設定です。nginxで「Hello world!」を表示するだけなので、最小構成とします。

作成します。

ECSサービスを作成する

サービスとは

タスク定義を用いて、タスクの実行と管理を行う役割を担うのがサービスです。サービスが作成されると、タスク定義を元に実際のタスク(コンテナ)が起動し、実行されます。

クラスターとは

サービスを作成する前に、クラスターを作成する必要があります。

クラスターとは、サービスとタスクを実行する基盤のことを指します。この単位でインフラストラクチャ(Fargate or EC2)も決定します。

また、複数サービスを束ねる区切りと考えることもできます。アプリケーションには複数のサービスが連携することで成立するものがあります(バックエンドのサービス、フロントエンドのサービス、認証用のサービスetc...)。このような場合、アプリケーション単位でサービスをまとめることで管理がしやすくなります。

クラスターを作成する

AWSコンソールのECSページに行き、サイドバーからクラスターを選択します。

  • クラスター設定
    • クラスター名: nginx-dev-cluster
  • インフラストラクチャ
    • AWS Fargate (サーバーレス)にチェック

作成します。

サービスを作成する

作成したクラスターのページに行くと、サービスという項目があります。
サービス作成ボタンを押します。

  • 環境
    • 既存のクラスター: nginx-dev-cluster(さっき作ったクラスター)
    • コンピューティングオプション: 起動タイプにチェック
    • 起動タイプ: FARGATEを選択
    • プラットフォームのバージョン: LATEST
  • デプロイ設定
    • アプリケーションタイプ: サービス
    • タスク定義
      • ファミリー: nginx-test(作成したタスク定義を選択)
      • リビジョン: 1(最新)
      • サービス名: nginx-test-server
    • サービスタイプ: レプリカにチェック
    • 必要なタスク: 1
  • ネットワーキング
    • VPC: ALBと同じVPC
    • サブネット: ALBとは異なる別のサブネットを選択
    • セキュリティグループ
      • 新しいセキュリティグループの作成
      • セキュリティグループ名: ecs-sg
      • セキュリティグループのインバウンドルール
        • タイプ: カスタムTCP
        • ポート範囲: 80
        • ソース: Source group
        • : ALBに紐付けているセキュリティグループを選択
  • ロードバランシング
    • ロードバランサーの種類: Application Load Balancer
    • 既存のロードバランサーを使用にチェック
    • ロードバランサー: sample-alb
    • ロードバランス用のコンテナの選択: nginx 80:80
    • リスナー
      • 「既存のリスナーを使用」にチェック
      • ポート 80 / プロトコル HTTP
    • ターゲットグループ
      • 「既存のターゲットグループを使用」にチェック
      • ターゲットグループ名: ecs-target-group

サービス作成時のエラー対応

サービスを作成しようとすると、exec /docker-entrypoint.sh: exec format errorというエラーが発生しました。

これは、イメージをビルドしたローカルマシンとECSのアーキテクチャが異なることが原因です。

Apple SiliconのMacでは、デフォルトでARMベースのイメージがビルドされますが、多くのクラウドサービスやサーバーの環境はAMD64(x86_64)アーキテクチャ上で実行されています。このアーキテクチャ(ARM64 or AMD64)が合っていないと、コンテナ起動時にエラーが出ます。

対応として、--platform linux/amd64を付けてイメージをビルドし直し、そのイメージを再度ECRにプッシュします。

bash
# AMD64で再ビルド
docker build -t nginx-test . --platform linux/amd64
# ログイン
aws ecr get-login-password --region ap-northeast-1 --profile ECRLoginAndPushRoleProfile | docker login --username AWS --password-stdin <your-account-id>.dkr.ecr.ap-northeast-1.amazonaws.com
# タグ付け
docker tag nginx-test:latest <your-account-id>.dkr.ecr.ap-northeast-1.amazonaws.com/nginx-test:latest
# 再プッシュ
docker push <your-account-id>.dkr.ecr.ap-northeast-1.amazonaws.com/nginx-test:latest

※ 2023/10/08現在、タスク定義のOS/アーキテクチャはLinux/ARM64を選択できます。AWS側をARM64にしても良いですが、ここではAMD64(x86_64)で統一することとします。

コンテナの起動を確認

上記のエラー対応の後、再度サービスを作成します。

すると、サービスとその中のタスクが起動していることが確認できます。

ALBのドメインをブラウザに打ち込むと、Hello World!!が表示されることが確認できました。

起動中のタスクを停止

起動し続けていると課金されるので、以下の手順でタスクを停止しておきます。

  • クラスタのサービスタブを開きます。
  • 停止したいサービスにチェックを入れて、更新ボタンを押します
  • 必要なタスクを0にします
  • 更新ボタンを押します

これによって、タスクが停止され、新しいタスクも起動されなくなります。

Discussion