はじめに
この記事では、ECSクラスターにコンテナアプリをデプロイする手順を紹介します。
全体構成図
検証に使用したAWS環境では、VPC内のパブリックサブネットに踏み台サーバーが用意されていました。
同じVPC内に新たにプライベートサブネットを作成し、さらにその中にECSクラスターとALBを作成することで踏み台サーバーからコンテナアプリにアクセス出来るようにします。
踏み台サーバーからALBのリスナーにリクエストがいき、パスによってターゲットグループが振り分けられ、適切なサービスに接続されコンテナアプリにアクセスします。
ECRリポジトリの作成・イメージのpush
まずはコンテナイメージを格納するためのECRリポジトリを作成します。
リポジトリ構成
ECRにはOpenShiftでいうところの名前空間のような概念はないようです。
ただし、リポジトリを作成する際に/で区切ることで擬似的に名前空間のような使い方をすることは可能です。
また、ECRの管理画面では「リポジトリを作成」となっていますが、実際は「リポジトリ+イメージ名」の組み合わせで管理されるため、リポジトリ名の設計がそのままイメージの分類にも影響します。
例えばリポジトリ作成時、リポジトリ名に「nodejs」とだけ指定するとイメージ名として扱われます。
「myrepo/nodejs」と指定すると、myrepoをリポジトリ名、nodejsをイメージ名としてリポジトリを作成出来ます。
OpenShiftの感覚だとnamespaceごとにリポジトリがあって、そこにいろいろな名前のイメージを追加していく形なので、ECRを使い始めたときは設計思想の違いにちょっと混乱しました。
最終的には下記の名称でリポジトリを作成しました。
| 環境 | リポジトリ+イメージ名 |
|---|---|
| 開発 | app-dev/myjava |
| 検証 | app-test/myjava |
| ステージング・本番 | app-release/myjava |
イメージのpush
リポジトリが出来たらイメージをpushします。
「プッシュコマンドを表示」で必要なコマンドを表示できます。
コマンドはdockerベースなのでpodmanを使っている場合は修正する必要があります。
また、MFA認証をしているため、--profileでAWSプロファイルを指定しています。
# ECRリポジトリへのログイン
$ aws ecr get-login-password --region us-east-1 --profile mfa-user| podman login --username AWS --password-stdin <AWSアカウント名>.dkr.ecr.us-east-1.amazonaws.com
# イメージのビルド
$ podman build -t app-dev/myjava .
# タグ付け
$ podman tag app-dev/myjava:latest <AWSアカウント名>.dkr.ecr.us-east-1.amazonaws.com/app-dev/myjava:latest
# プッシュ
$ podman push <AWSアカウント名>.dkr.ecr.us-east-1.amazonaws.com/app-dev/myjava:latest
ECSクラスターの作成
アプリをデプロイするECSクラスターを作成します。
| 設定項目 | 値 |
|---|---|
| クラスター名 | 任意の名前を入力 |
| インフラストラクチャ | AWS Fargate(サーバーレス) |
ALBの作成
アプリアクセスのためのエンドポイントを作成するため、ALBを作成します。
ALBの作成時には他のAWSリソースを指定する必要があります。
これらのリソースはALBの作成時に同時に作成出来るのですが、個別に作成した場合と比べて設定できる項目が限られているものもあります。
ですのでALBを作成する前にあらかじめ必要なリソースを作成しておき、ALB時に作成したリソースを指定する、という形で作った方が安心です。
1. サブネットの作成
検証環境VPC内に、ALB用のサブネットを作成します。
ALB用には最低2つのサブネットが必要でした。
| 設定項目 | 値 |
|---|---|
| VPC ID | ALBを作成するVPCを指定 |
| サブネット名 | 任意の名前を入力 |
| アベイラビリティーゾーン | 任意のAZを選択 |
| IPv4 VPC CIDRブロック | 任意のCIDRブロックを入力 |
2. セキュリティグループの作成
ALBにアタッチするセキュリティグループを作成します。
踏み台サーバーからALBのHTTPリスナー(80番ポート)への通信を許可します。
インバウンドルールに下記を追加します。
| 設定項目 | 値 |
|---|---|
| タイプ | カスタムTCP |
| プロトコル | TCP |
| ポート範囲 | HTTPリスナーの80番ポート |
| ソース | 踏み台サーバーのあるVPCのセキュリティグループ |
3. ターゲットグループの作成
ターゲットグループとはALBがリクエストを受けたあと、トラフィックを振り分ける先(ターゲット)の集合です。
ターゲットの種類にはIP、EC2インスタンス、Lambdaなどがあります。
今回はIPを使用します。
ECSクラスターとALBが連携している場合、サービス定義にターゲットグループを指定することで、ECSクラスターがサービスに任意のIPを自動で割り当ててくれます。
そのためユーザーが手動でIPアドレスを指定する必要はありません。
| 設定項目 | 値 |
|---|---|
| ターゲットタイプの選択 | IP |
| ターゲットグループ名 | 任意の名前を入力 |
| プロトコル | HTTP or HTTPS |
| ポート | アプリケーションの公開ポート |
| VPC | ECSクラスターを作成するVPCを指定 |
4. ALBの作成
アプリを外部公開するため、ALBを作成します。
EC2 > ロードバランサー > ロードバランサーの作成 > Application Load Balancerを作成 の順にクリックします。
下記の項目を設定しました。他はすべてデフォルト値 or 設定なしのままです。
基本的な設定
| 設定項目 | 値 |
|---|---|
| ロードバランサー名 | 任意の名前を入力 |
| スキーム | 内部 |
ネットワークマッピング
| 設定項目 | 値 |
|---|---|
| VPC | ECSクラスターを構築するVPCを指定 |
| アベイラビリティーゾーンとサブネット | 作成したサブネットを指定 |
| セキュリティグループ | 作成したALB用のセキュリティグループを指定 |
リスナーとルーティング
ALB作成時に指定できるリスナーはルートパス(/)へのリクエストのみです。
/javaと/nodejsの振り分けルールは、ALB作成後に行います。
| 設定項目 | 値 |
|---|---|
| プロトコル | HTTP |
| ポート | 80 |
| デフォルトアクション | 作成したターゲットグループのうち片方を指定 |
ここまで設定できたらALBを作成します。
5. リスナールール
ALBが作成できたら、リスナールールを追加します。
構成図のこの部分ですね。

ALB > リスナーとルール > リスナーの追加 をクリックします。
| 設定項目 | 値 |
|---|---|
| プロトコル | HTTP |
| ポート | アプリのリッスンポート |
続いて個別のルールを追加します。
リスナー > リスナールール > ルールを追加する をクリックします。
ここではJavaアプリ用、Nodejsアプリ用のルールをそれぞれ追加します。
条件
| 設定項目 | 値 |
|---|---|
| パス | Javaアプリ用:/java Nodejsアプリ用:/nodejs |
アクション
| 設定項目 | 値 |
|---|---|
| アクションのルーティング | ターゲットグループへ転送 |
| ターゲットグループ | Javaアプリ用、Nodejsアプリ用のターゲットグループからそれぞれ選択 |
コンテナアプリの作成
ECSクラスターにコンテナアプリをデプロイします。
1. セキュリティグループの作成
ECSクラスター用のセキュリティグループを作成します。
ALBからECSクラスター上のコンテナアプリへの通信を許可します。
| 設定項目 | 値 |
|---|---|
| タイプ | カスタムTCP |
| プロトコル | TCP |
| ポート範囲 | 8080 |
| ソース | ALBのセキュリティグループ |
1. タスク定義の作成
タスク定義とは、ECSクラスターで動かすタスクの設定内容を記述したテンプレートです。
OpenShiftで言うとタスクがPod、タスク定義がDeployment内のPodSpecに相当します。
| 設定項目 | 値 |
|---|---|
| タスク定義ファミリー | 任意の名前を入力 |
| 機動タイプ | ECS Fargate |
| コンテナの詳細 > 名前 | 任意の名前を入力 |
| イメージURI | イメージURIを指定 |
| コンテナポート | アプリケーションの公開ポート |
| プロトコル | TCP |
| ポート名 | 任意の名前を入力 |
| アプリケーションプロトコル | HTTP or HTTPS |
2. サービスの作成
サービスは指定した数のタスクを稼働させ続ける仕組みです。ALBと連携してトラフィックを分散することもできます。
OpenShiftで言うとReplicaSetとServiceの機能を合わせたものに近いです。
| 設定項目 | 値 |
|---|---|
| タスク定義ファミリー | 作成したタスク定義を作成 |
| サービス名 | 任意のサービス名を入力 |
| VPC | ECSクラスターを構築したVPCを指定 |
| サブネット | 作成したサブネットをすべて指定 |
| セキュリティグループ | 既存のセキュリティグループを使用 |
| セキュリティグループ名 | 作成したECSクラスター用セキュリティグループを指定 |
| ロードバランシングを使用 | ✅️ |
| ロードバランサーの種類 | Application Load Balancer |
| コンテナ | 作成したタスク定義を指定 |
| Application Load Balancer | 既存のロードバランサーを使用 |
| ロードバランサー | 作成したロードバランサーを指定 |
| リスナー | 既存のリスナーを使用 タスク定義で作成したリスナーを指定 |
| ターゲットグループ | 既存のターゲットグループを使用 |
| ターゲットグループ名 | 作成したターゲットグループを指定 |
アプリの起動確認
これでようやくアプリの作成が完了しました。
アプリの状況はサービスからタスク一覧に表示されたタスクをクリックすることで確認できます。
ステータスが「実行中」となるまで待機します。
ログタブからログの確認もできます。
より詳しく見たい場合はCloudWatchのダッシュボードのほうが見やすいです。

アプリへのアクセス
今回はアプリを外部公開していないため、同じVPC内にある踏み台サーバー経由でアプリにアクセスしてみます。
アプリへのアクセスにはALBのDNS名を使用します。ALBのページから確認できます。
# Javaアプリ
$ curl <ALB DNS名>/java
# Node.jsアプリ
$ curl <ALB DNS名>/nodejs
ローカルからアクセスしたい場合はトンネルを張ります。
# ローカルにて
$ ssh -i <鍵ファイル> -L 80:<ALB DNS名> <踏み台ユーザー名>:<踏み台IPアドレス>
ローカルのブラウザよりlocalhost/nodejsにアクセスします。

無事にアプリが表示できました!
おわりに
ネットワーク周りの知識がないため、特にALB関連のリソース作成や設定に苦労しました。
今回はECSクラスターへコンテナアプリのデプロイを行いましたが、別途CodePipelineによるCI/CD化も行いました。
こちらも別途記事にする予定です。

