ECSとFargateでシンプルなWebアプリをデプロイしよう

このハンズオンでは、シンプルなWebページをAWSのECS/Fargateにデプロイする方法について実際にハンズオン形式で手を動かしながら体験します。

  • ECRへのコンテナイメージのプッシュ
  • ECSクラスターの作成
  • タスク定義の作成
  • Fargateサービスのデプロイ
  • パブリックIPでの動作確認

1. 事前準備

Gitの基礎知識を習得していることを前提とします。自信のない方は先に以下の講座を実施してください。

この講座のハンズオンでは、以下のツールやアカウントが必要です。まだ準備できていない場合は、リンク先の手順に沿って準備をお願いします。

2. ハンズオンの概要

このハンズオンでは、NginxでシンプルなHTMLページを表示するコンテナを、AWS上で動かす体験をします。

実際の業務でECS/Fargateを使う場合は、クラスター、タスク定義、タスク、サービスといったECSの構成要素に加えて、ロードバランサーやオートスケーリング、データベースとの接続など、様々な要素が絡み合います。さらに、実行するコンテナイメージ自体に問題があるケースも考えられます。

最初からこのような構成を作ろうとすると、ECS/Fargate自体の仕組みがわからないままになってしまうおそれがあります。また、なにか問題があったときに、ECS/Fargateの問題なのか別の原因なのかが切り分けられず、解析に苦労することになります。

そのため、このハンズオンでは複雑な設定を除外し、シンプルなHTMLベースのWebサイトを最低限の構成のECS/Fargateでデプロイする方法を体験していただきます。実際の業務レベルの構成は、次の講座のハンズオンで扱います。

2.1 全体の概要

以下は、このハンズオンで構築する全体構成です。

VPC内にパブリックサブネットを1つ作成し、そこにECS/Fargateのタスクを配置します。インターネットゲートウェイを経由して、インターネットからタスクに直接アクセスできる構成です。また、ECS/FargateのタスクはECRリポジトリからコンテナイメージを取得して起動します。

2.2 ECS関連リソース

以下は、ECSで作成するリソースの関係を示した図です。

ECSクラスターの中にサービスを作成し、サービスがタスク定義に基づいてタスクを起動します。タスク定義には、ECRリポジトリにプッシュしたコンテナイメージのURIを指定します。

3. VPCの構築

ECS/Fargateでコンテナを動かすには、コンテナを配置するネットワーク環境が必要です。ここでは、VPCとパブリックサブネットを作成し、インターネットからコンテナにアクセスできるネットワーク環境を構築します。

3.1 VPCの作成

AWSマネジメントコンソールの左上の検索ボックスで「VPC」を検索し、VPCを選択します。

左側のメニューから「お使いのVPC」を選択し、右上のVPCを作成をクリックします。

その上で下記の内容を設定してください。

設定項目 設定の基準
作成するリソース VPCなど VPCと一緒にサブネットやインターネットゲートウェイなどの関連リソースをまとめて作成するため
名前タグの自動生成 ecs-simple-handson 各リソースにこの名前をプレフィックスとしたタグが自動付与される
IPv4 CIDRブロック 10.0.0.0/16 約65,000個のIPアドレスを確保でき、サブネットを作成するのに十分な範囲
IPv6 CIDRブロック なし 今回はIPv4のみ使用するため
アベイラビリティゾーンの数 1 今回のハンズオンではAZ1つで十分なため
パブリックサブネットの数 1 Fargateタスクを配置するパブリックサブネットが1つあれば十分なため
プライベートサブネットの数 0 今回のハンズオンでは不要なため
NATゲートウェイ なし プライベートサブネットを使用しないため不要
VPCエンドポイント なし 今回のハンズオンでは不要なため

入力が終わったら「VPCを作成」をクリックします。

しばらく経過すると、VPCと関連リソースが無事に作成されます。

💡 ポイント
「VPCなど」を選択すると、VPCに加えてサブネット、インターネットゲートウェイ、ルートテーブルといった関連リソースが一括で作成されます。個別に作成する手間が省けるため便利です。

作成が完了すると、以下のリソースが自動的に作成されます。

リソース 名前
VPC ecs-simple-handson-vpc
パブリックサブネット ecs-simple-handson-subnet-public1-ap-northeast-1a
インターネットゲートウェイ ecs-simple-handson-igw
ルートテーブル ecs-simple-handson-rtb-public

ここまでで、VPCのネットワーク環境の構築が完了しました。

4. ECRへのプッシュ

続いて、ECRのリポジトリを作成し、イメージをプッシュしていきます。

4.1 リポジトリの作成

まずはECRのリポジトリを作成していきます。

AWSマネジメントコンソールの左上の検索ボックスで「ECR」を検索し、表示されるメニューの中からElastic Container Registryを選択します。

左側のメニューからリポジトリを選択し、右上のリポジトリを作成をクリックします。

リポジトリ名の部分は、今回のアプリケーション名であるecs-simple-handsonを入力します。

それ以外の項目はデフォルトで構いませんが、それぞれの内容を簡単に説明します。

イメージタグのイミュータビリティは、同じタグ名での上書きを許可するかどうかの設定です。「Mutable(変更可能)」にすると同じタグで何度でもプッシュできますが、「Immutable(変更不可)」にすると一度使ったタグは上書きできなくなります。今回はデフォルトの「Mutable」のままで問題ありません。

暗号化設定は、保存されるイメージの暗号化方式の設定です。デフォルトではAWSが管理するAES-256暗号化が適用されるため、特別な要件がなければそのままで問題ありません。

イメージスキャンの設定は、プッシュされたイメージに対してセキュリティ上の脆弱性がないかを自動でスキャンする機能です。今回のハンズオンではデフォルトのままにしておきます。

入力が終わったら作成をクリックします。

無事にecs-simple-handsonのリポジトリが作成されれば、ここまでは成功です。

4.2 イメージの準備

今回コンテナを起動するために使うDockerfileと、コンテナで表示するindex.htmlを用意します。

作業フォルダの作成

任意の場所にecs-simple-handsonフォルダを作成し、Visual Studio Codeの「ファイル」→「フォルダーを開く」から、作成したecs-simple-handsonフォルダを開きます。

ecs-simple-handson  ← このフォルダを作成

Dockerfileの作成

Dockerfileを作成します。Visual Studio Codeのエクスプローラーでecs-simple-handsonフォルダを右クリックし、「新しいファイル」を選択してDockerfileという名前で作成してください。

ecs-simple-handson
└── Dockerfile  ← このファイルを作成

作成したファイルに以下の内容を記述して保存します。

FROM nginx:alpine

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

CMD ["nginx", "-g", "daemon off;"]

なお、このDockerfileの説明については過去のハンズオンで行っているため、ここでは割愛します。

index.htmlの作成

index.htmlファイルを作成します。Visual Studio Codeのエクスプローラーでecs-simple-handsonフォルダを右クリックし、「新しいファイル」を選択してindex.htmlという名前で作成してください。

ecs-simple-handson
├── Dockerfile
└── index.html  ← このファイルを作成

作成したファイルに以下の内容を記述して保存します。

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>ECS Hands-on</title>
</head>
<body>
    <h1>Hello from ECS/Fargate!</h1>
    <p>This container is running on AWS Fargate.</p>
</body>
</html>

index.htmlのファイルの内容の説明についても、ここでは割愛しています。

4.3 プッシュコマンドの取得

続いて、ローカルからECRリポジトリにイメージをプッシュするためのコマンドを取得します。

作成したリポジトリを選択し、「プッシュコマンドの表示」をクリックします。

そうすると、使用するOSごとに、プッシュコマンドが表示されます。

例えばMacの場合は以下のコマンドです。アカウントIDの部分はご自身のIDが設定されていると思います。

基本的にはそのまま順番に実行すればよいのですが、各コマンドの内容を解説します。

ECRにログイン

aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin 125605020607.dkr.ecr.ap-northeast-1.amazonaws.com

aws ecr get-login-passwordでECRの認証トークンを取得し、それをdocker loginにパイプで渡してログインしています。これにより、ローカルのDockerクライアントがECRにイメージをプッシュできるようになります。「Login Succeeded」と表示されれば成功です。

イメージのビルド

docker build -t ecs-simple-handson .

Dockerイメージをビルドするコマンドです。

Apple SiliconのMac(M1、M2、M3、M4など)を使用している場合は、上記の代わりに以下のコマンドを実行してください。--platform linux/amd64 オプションを付けることで、Fargateで動作するx86_64アーキテクチャのイメージをビルドできます。Apple Siliconではデフォルトでarm64アーキテクチャのイメージがビルドされますが、今回のタスク定義では Linux/X86_64 を指定しているため、このオプションがないとFargateでの起動時に exec format error が発生します。

docker build --platform linux/amd64 -t ecs-simple-handson .

タグの付与

docker tag ecs-simple-handson:latest 125605020607.dkr.ecr.ap-northeast-1.amazonaws.com/ecs-simple-handson:latest

ローカルのイメージに、ECRリポジトリのURIをタグとして付与するコマンドです。Dockerは、タグに含まれるURIを見てプッシュ先を判断するため、この手順が必要になります。ここが間違えていると正しくプッシュされません。

ECRリポジトリにプッシュ

docker push 125605020607.dkr.ecr.ap-northeast-1.amazonaws.com/ecs-simple-handson:latest

タグ付けしたイメージをECRリポジトリにプッシュするコマンドです。プッシュが完了すると、ECRコンソール上でイメージを確認できるようになります。

4.4 プッシュの実施

それでは実際にプッシュを行っていきたいと思います。

「プッシュコマンドの表示」で取得したコマンドを順番に、Visual Studio Codeのターミナルで実行してみてください。

その後、ECRリポジトリの詳細画面を開くと、以下のようにlatestというイメージタグがついたイメージが、無事にプッシュされていることがわかります。

ここまでで、ECRリポジトリへのイメージのプッシュが完了です。 

5. ECSクラスターの作成

続いて、ECSのクラスターを作成していきます。

📝 クラスターとは
クラスターは、ECSの論理的なグループ単位です。開発環境、本番環境などでクラスターを分けて管理することが一般的です。

AWSマネジメントコンソールの左上の検索ボックスでECSと入力し、表示されるメニューの中からElastic Container Serviceを選択してください。

ECSのダッシュボードが開かれたら、左側のメニューからクラスターを選択し、右上のクラスターの作成をクリックしてください。

クラスターを作成していきます。クラスター名にはecs-simple-handson-clusterと入力します。

それ以外の項目はデフォルトで構いませんので、作成をクリックします。

クラスターの作成には少し時間がかかるので、しばらく待ちます。

時間経過により、無事にecs-simple-handson-clusterが作成されることを確認します。

6. タスク定義の作成

続いて、タスク定義の作成を行っていきます。

📝 タスク定義とは
タスク定義は、コンテナの実行設定を定義するテンプレートです。Dockerのdocker runコマンドに指定する内容(イメージ、ポート、リソースなど)をJSON形式で記述したものと考えることができます。

ECSのダッシュボードにて左側のメニューからタスク定義を選択し、右上の新しいタスクの作成から新しいタスクの作成をクリックします。

💡 ポイント
「新しいタスクの作成」には「JSONを使用した新しいタスクの作成」という選択肢もあります。タスク定義の実体はJSON形式のファイルであり、GUIで設定した内容も最終的にはJSONとして保存されます。JSONを直接記述すれば、バージョン管理やIaC(Infrastructure as Code)ツールとの連携がしやすくなり便利です。今回のハンズオンでは、各設定項目の意味を理解しやすいようにGUIを使って作成します。

タスク定義ファミリーですが、ecs-simple-handson-taskと入力します。

起動タイプはAWS Fargateを選択します。これにより、サーバレスでコンテナを実行するFargateが利用されます。

コンテナの定義を行っていきます。名前は今回はecs-simple-handsonと入力します。

イメージURIは、ECRリポジトリのURIを指定します。ECRコンソールで先ほど作成したecs-simple-handsonリポジトリを開き、プッシュしたイメージの「URIをコピー」ボタンからURIをコピーして貼り付けてください。

それ以外の項目はデフォルトで構いませんので、作成をクリックします。

無事にタスク定義が作成されれば、ここまでの操作は完了です。

7. サービスの作成

続いて、ECSのサービスを作成していきます。

📝 サービスとは
サービスは、タスクの起動数や配置を管理する仕組みです。「常に1つのタスクを実行する」「タスクが異常終了したら自動的に再起動する」といった運用ルールを定義します。

なお、サービスには入力するべき項目が非常に多いですが、すべてを説明すると重要な点が理解しにくくなるため、今回は最低限の部分のみの解説とします。

ECSのダッシュボードの左側のメニューから、ふたたびクラスターを選択します。そのうえで、先程作成したecs-simple-handson-clusterをクリックします。

さらに、「サービス」タブの中央右側にある作成をクリックします。

タスク定義ファミリーですが、これはサービスの起動に利用するタスク定義を指定します。先程作成したecs-simple-handson-taskを選択します。

タスク定義のリビジョンは、今回は空欄とします。これにより、バージョンが常に最新のものが利用されるため、イメージを更新した場合などにもサービスに反映しやすくなります。

サービス名は、ここではecs-simple-handson-serviceとします。

デプロイ設定の部分ですが、これはデフォルトで構いません。

ネットワーキングの部分ですが、ここではECSタスクを配置するVPC、サブネット、セキュリティグループなどを指定します。

VPCは、冒頭に作成したecs-simple-handson-vpc、サブネットはecs-simple-handson-subnet-public1-ap-northeast-1aを指定します。このサブネットはパブリックサブネットであるため、インターネットとの接続が可能です。

セキュリティグループですが、今回作成します。新しいセキュリティグループの作成をチェックし、セキュリティグループ名にecs-simple-handson-sg、タイプにHTTP、ソースにAnywhereを指定します。これにより、すべてのHTTP通信が許可されるようになります。

それ以外の項目は特に未指定で構いませんので、入力が完了したら作成をクリックします。

ECSサービスの起動には数分かかるため、しばらく待ちます。

8. 動作確認

サービスが作成されると、タスクが自動的に起動します。実際に動作確認をしていきたいと思います。

8.1 タスクの状態確認

クラスターのサービスタブで、タスクの「デプロイとタスク」が「1/1件の実行中のタスク」になるまで待ちます(1〜2分程度かかります)。これが表示されれば、1件のタスクが正常に起動されていることが読み取れます。

合わせてタスクタブで、1件のタスクが表示され、前回のステータスが実行中になっていることも確認しておきます。

⚠️ タスクが実行中にならない場合
(1) セキュリティグループでアウトバウンド通信(すべてのトラフィック)が許可されているか確認してください。デフォルトでは許可されていますが、カスタマイズしている場合は注意が必要です。
(2) パブリックIPが「オン」になっているか確認してください。パブリックIPがないと、ECRからイメージを取得できません。

8.2 パブリックIPの確認

ECSタスクにも、EC2と同様パブリックIPアドレスを用いてアクセスすることができます。ここでパブリックIPアドレスを確認しておきます。

タスクのIDをクリックして詳細を開き、ネットワーキングセクションでパブリックIPを確認します。

このIPアドレスをコピーしておいてください。

8.3 ブラウザでアクセス

確認したパブリックIPをブラウザのアドレスバーに入力してアクセスします。

http://<パブリックIP>

「Hello from ECS/Fargate!」というメッセージが表示されれば成功です!

⚠️ アクセスできない場合
(1) セキュリティグループでポート80のインバウンドが許可されているか確認してください。
(2) タスクの状態が「RUNNING」になっているか確認してください。
(3) ブラウザのキャッシュをクリアしてみてください。
💡 ポイント
タスクを停止して再起動すると、パブリックIPが変わります。固定のIPアドレスやドメイン名でアクセスしたい場合は、ALB(Application Load Balancer)を使用します。次の講座で詳しく学びます。

9. 不要リソースの削除

ハンズオンが終わったら、課金を避けるため、作成したリソースを削除します。以下の順序で削除してください。

9.1 ECSサービスの削除

ECSコンソールでクラスターを選択し、「サービス」タブでサービスを選択してサービスを削除をクリックします。

確認画面で「強制削除」オプションをチェックし、「削除」と入力して削除します。

💡 ポイント
「強制削除」を選択すると、実行中のタスクも同時に停止・削除されます。

9.2 ECSクラスターの削除

サービスが削除されたら、クラスター詳細画面でアクションクラスターを削除をクリックします。

確認画面でクラスター名を入力して削除します。なお、クラスターの削除にはしばらく時間がかかります。

9.3 タスク定義の登録解除

「タスク定義」から「ecs-simple-handson-task」を選択し、すべてのリビジョンを選択して「登録解除」をクリックします。

9.4 ECRリポジトリの削除

ECRコンソールで「ecs-simple-handson」リポジトリを選択し、「削除」をクリックします。確認画面で「delete」と入力して削除します。

9.5 セキュリティグループの削除

9.6 VPCの削除

VPCコンソールの左側のメニューから「お使いのVPC」を選択し、「ecs-simple-handson-vpc」を選択して「アクション」→「VPCを削除」をクリックします。

確認画面で「削除」と入力して削除します。

VPCを削除すると、「VPCなど」で一括作成したサブネット、インターネットゲートウェイ、ルートテーブル、セキュリティグループも一緒に削除されます。

10. まとめ

このハンズオンでは、シンプルなWebアプリをECS/Fargateにデプロイする方法を体験しました。

  • VPCとパブリックサブネットを作成し、コンテナを配置するネットワーク環境を構築した
  • ECRにDockerイメージをプッシュし、AWS上でイメージを管理した
  • ECSクラスターを作成し、コンテナを実行する環境を用意した
  • タスク定義でコンテナの設定(イメージ、ポート、リソース)を定義した
  • サービスでタスクを起動し、自動管理を有効にした
  • セキュリティグループでアクセス制御を設定した
  • パブリックIPでコンテナに直接アクセスして動作確認した