BLOG
エンジニアブログ

【第1回】ALB+ECR+ECS Fargateを作って覚えるAWS ECSの基礎

shibuya
shibuya
【第1回】ALB+ECR+ECS Fargateを作って覚えるAWS ECSの基礎

はじめに

AWS ECS を使ったコンテナ環境をAWS マネジメントコンソールから手作業で構築し、そこに CI/CD を少しずつ足していきます。

想定している読者は次のような方です。

  • VPCやEC2等の設定、構築をした事がある方。
  • コンテナは触ったことがあるが、ECS はこれからという方。
  • ECS Fargateを使用して、ECSの基礎知識を確認しておきたい方。
  • 昔ECSを使ったが久しぶりのため、改めて作業方法の再確認をしたい方、復習をして知識を維持したい方。

EC2インスタンスにECS Agentを導入して、管理する方法は本記事では実施しません。

本記事について

  • 本記事ではALBやFargateなどの有料サービスを使用するため、費用が発生します。ALBとFargateは起動している時間に応じた課金のため、数時間で完了すれば数USドル程度ですみますが、削除し忘れて1か月放置すると40USドル程度になります。検証後は必ずリソースの削除をお願いします(手順は記事末尾に記載しています)。
  • AWSマネジメントコンソールの画面構成は、随時更新されます。本記事は2026年9月時点の画面をもとにしています。項目名や配置が異なる場合は、適宜読み替えてください。
  • 掲載している手順は、実際にAWS上で検証した内容です。ただし、学習しやすさを優先してセキュリティ設定を簡略化している箇所があります(タスクをパブリックサブネットに配置する、セキュリティグループで広い範囲からのアクセスを許可する、など)。そのため、検証用のAWSアカウントでの実施を推奨します。
  • 本記事では、GitHubとAWSを連携させる設定(OIDCのIDプロバイダー、GitHubアプリの連携など)を行います。これらは他のプロジェクトと共有される設定のため、検証後に削除する際はご注意ください。同じアカウントやGitHub組織で他にも利用しているものがあると、そちらの連携まで止まってしまう可能性があります。削除対象かどうか分からない場合は、無理に消さずに残しておくほうが安全です(残しておいても費用は発生しません)。
  • 共用アカウントや業務で使用しているアカウントで実施する場合は、組織のルールを確認したうえで設定内容を見直してから、本記事を参考にしたり、手順の実施をしてください。

テーマとゴール

いくつかテーマと、ゴールを決めて進めていきます。内容を1つのブログにすると長くなるため、複数回に分けています。

第何回 テーマ ゴール
第1回 コンテナと ECS の基礎、手動構築 ブラウザに「Hello from Amazon ECS」 が表示される
第2回 手動リリースと CodePipeline による自動リリース ECR への push でローリングアップデート
第3回 ECS ネイティブ Blue/Green デプロイ テストリスナーで確認してから本番切り替え
第4回 GitHub Actions で ECR への push を自動化

“git push”だけでリリースまで完了する

第5回 プライベートサブネットを使用した構成

プライベートサブネットとエンドポイントを利用した環境構築の検証をする

今回の記事の構成は以下のようになります。

この記事ではNATゲートウェイを使用しません。 タスクをパブリックサブネットに置いて、パブリックIPを割り当ててECRからイメージを取得します。パブリックサブネットを使用している理由としては、NATゲートウェイは単体で月45USドル前後かかるので学習用としてはコストが高いことや、学習用の構成として単純化するためです。実際の業務では、プライベートサブネットに配置しエンドポイントを使用します(途中でその話に触れます)。正確な金額は、AWS公式ページにてご確認ください。

プライベートサブネットに配置した構成については、第5回の記事に記載いたします。

 

概念の整理

ECSの作業に取り組む前に、用語の確認をします。

  •  クラスター(Cluster)
    • タスクを動かす場所の論理的なグループです。Fargate を使う場合、クラスター自体は「入れ物」で、実体としてのサーバーはありません。
  • タスク定義(Task Definition)
    • コンテナの設計図です。「どのイメージを、CPUとメモリでサイズ、どのポートを開けて動かすか」を書いた JSONファイルと思ってください。
      • タスク定義の考慮事項
        • イミュータブル(変更不可):一度作った定義は書き換えられません。

        • リビジョンの増加:修正すると、ecs-handson-task:1 、ecs-handson-task:2と番号が振られます。
  • タスク(Task)
    • タスク定義から起動された、実行中の実体です。設計図が「タスク定義」、そこから生まれた個体が「タスク」という関係です。
  • サービス(Service)
    • 「タスクを常に N 個動かし続ける」役割を持ちます。タスクが落ちたら起動し直し、ALB への登録・解除も行います。
  • Fargate
    • コンテナを動かすサーバーを AWS が管理します。利用者は、CPUとメモリの組み合わせを指定して使用します。EC2インスタンスや、ECSエージェントも見ることができません。
  • EC2起動タイプ
    • EC2インスタンスを用意し、ECS エージェントを入れてクラスターに参加させます。インスタンスの管理が必要になる代わりに、GPU や特殊なカーネル設定など、Fargate では選べない構成が使えます。本記事では扱いません。

環境構築

実際に、学習用の環境を作成します。

1. VPCの作成

VPC コンソールの「VPCを作成」から、ウィザードを使います。

    1. VPCコンソール → 左メニュー「お使いのVPC」→「VPCを作成」

    2. 「作成するリソース」で「VPCなど」を選択

以下の内容で設定します。

VPC: ecs-handson-vpc

項目 設定値
名前タグの自動生成 ecs-handson
IPv4 CIDR ブロック 10.0.0.0/16
IPv6 CIDR ブロック IPv6 CIDR ブロックなし
テナンシー デフォルト
アベイラビリティーゾーン (AZ) の数 2
パブリックサブネットの数 2
プライベートサブネットの数 0
NAT ゲートウェイ なし
VPC エンドポイント なし
DNS ホスト名を有効化 オン
DNS 解決を有効化 オン

ALBを作成するため、サブネットを2つにしています。

右側にプレビュー図が表示されるので、パブリックサブネットが 2 つの AZ に分かれていることを確認してから「VPC を作成」ボタンを押します。

2. セキュリティグループの作成

ALB 用とタスク用で2つのセキュリティグループを作成し、タスク用のセキュリティグループは「ALB からの通信のみ」を許可します。

EC2 コンソール → 左メニュー「セキュリティグループ」→「セキュリティグループを作成」

①ALB用: ecs-handson-alb-sg

項目 設定値
セキュリティグループ名 ecs-handson-alb-sg
説明 ALB for ECS handson
VPC ecs-handson-vpc

    ecs-handson-alb-sg インバウンドルール

タイプ プロトコル ポート ソース
HTTP TCP 80 0.0.0.0/0

    ecs-handson-alb-sg アウトバウンドルール

    • デフォルト(すべて許可)のままで問題ありません。

②タスク用 : ecs-handson-task-sg

項目 設定値
セキュリティグループ名 ecs-handson-task-sg
説明 ECS task for handson
VPC ecs-handson-vpc

    ecs-handson-task-sg インバウンドルール

タイプ プロトコル ポート ソース
HTTP TCP 80 ecs-handson-alb-sg

ソース欄でCIDRではなくセキュリティグループを選択してください。検索ボックスにecs-handson-alb-sgと入力すると候補に出ます

コラム:本番構成ではどうするか

本番環境では通常、次のパターンのどちらかを採ります。

  • パターン A:プライベートサブネット + NAT ゲートウェイ
    タスクをプライベートサブネットに置き、NAT経由でインターネットに出てECR からイメージを取得します。構成はシンプルですが、NAT ゲートウェイの固定費とデータ処理料金がかかります。

  • パターン B:プライベートサブネット + VPC エンドポイント
    NATを使わず、必要なAWS サービスへのVPC エンドポイントを個別に用意します。ECS on Fargate の場合以下の一覧にあるエンドポイントは必要です。

通信量が多ければ B、構成をシンプルに保ちたければ Aという形になります。業務要件に応じて設計をします。

また、使用するエンドポイントは以下になります。今回は学習用のため、コスト優先でタスクをパブリックサブネットに置き、パブリック IP を付ける構成としているため作成しませんが、業務で使用する場合はエンドポイントを使用した構成を検討します。

エンドポイント 種類 用途
com.amazonaws.ap-northeast-1.ecr.api Interface ECR の API 呼び出し
com.amazonaws.ap-northeast-1.ecr.dkr Interface イメージレイヤーの取得
com.amazonaws.ap-northeast-1.s3 Gateway ECR のレイヤー実体はS3にあるため使用
com.amazonaws.ap-northeast-1.logs Interface CloudWatch Logsへの出力

3.ECRにコンテナイメージを置く

3-1. ECR リポジトリを作成する

ECR コンソール → 「リポジトリ」→「リポジトリを作成」

項目 値
リポジトリ名 ecs-handson-nginx
イメージタグのミュータビリティ

ミュータブル(可変)

暗号化設定 デフォルト(AES-256)

ミュータブルにする理由:本記事では、「latest」タグを何度も上書きします。イミュータブル(変更不可)にすると2回目のpushが失敗するため、必ず可変にしてください。ただし、本番環境ではイミュータブル + コミット SHA タグが推奨です。「どのイメージがデプロイされているか」を後から追跡できるためです。
「イメージのスキャン設定」:デフォルトのまま(スイッチOFF)スキャンしないにの設定のままにします。 画面に「deprecated(非推奨)」と表示されます。スキャンの設定はリポジトリ単位からレジストリ単位(ECR の「プライベートレジストリ」→「スキャン設定」)に移行しました。本記事では脆弱性スキャンは扱いませんが、実運用ではレジストリ単位で基本スキャンや拡張スキャンを有効にし、フィルターで対象リポジトリを絞るのが現在の方法です。

作成すると、リポジトリ URI が表示されます。次の形式です。

123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/ecs-handson-nginx

先頭の 12 桁がご自身の AWS アカウント ID です。後で使うのでメモしておいてください。

3-2. CloudShell でイメージをビルド

AWS CloudShell にはDockerが組み込まれているため、それを利用します。個別に自身のPC上にセットアップは不要です。

マネジメントコンソール右上のターミナルアイコンからCloudShellを起動します。リージョンが東京になっていることを確認してください。

まず作業ディレクトリと HTML を作ります。CloudShell上で実行してください。


mkdir -p ~/ecs-handson && cd ~/ecs-handson

  cat > index.html <<'EOF'
  <!DOCTYPE html>
  <html lang="ja">
  <head>
    <meta charset="utf-8">
    <title>ECS ハンズオン</title>
  </head>
  <body style="font-family:sans-serif;text-align:center;padding-top:80px">
    <h1>Hello from Amazon ECS</h1>
    <p>version: <strong>v1</strong></p>
  </body>
  </html>
  EOF

次に Dockerfile を作ります。



  cat > Dockerfile <<'EOF'
  FROM public.ecr.aws/nginx/nginx:1.27-alpine
  COPY index.html /usr/share/nginx/html/index.html
  EOF

ベースイメージにpublic.ecr.awsを使う理由:Docker Hubのnginxは匿名pullのレート制限に引っかかることがあります。ECR Public GalleryにはAWSがミラーしたNginxイメージがあり、この制限を避けやすくなります。CodeBuildやGitHub Actionsからビルドするときも同じ理由でECR Publicを選ぶと制限を避けやすくなります。
なお ECR Public にもプル制限はありますが、public.ecr.awsにdocker loginしてから取得すれば上限が緩和されます。今回の規模であれば、匿名のままでも問題になりません

3-3. ECR にログインして push する

CloudShell はログイン中の IAM 権限をそのまま使えるので、認証情報の設定は不要になります。

以下のコマンドをCloudShellで実行します。



AWS_REGION=ap-northeast-1
AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
REPO=ecs-handson-nginx
REGISTRY=${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com
ECR_URI=${REGISTRY}/${REPO}

echo $ECR_URI

表示された URI が、先ほどコンソールで見たものと一致していることを確認します。

以下のコマンドを実行し、ECR にログインします。Login Succeededが表示されることを確認します。



aws ecr get-login-password --region ${AWS_REGION} \
  | docker login --username AWS --password-stdin ${REGISTRY}

ビルドして、タグを2つ付けてpushします。

docker build -t ${REPO}:v1 .

docker tag ${REPO}:v1 ${ECR_URI}:v1
docker tag ${REPO}:v1 ${ECR_URI}:latest

docker push ${ECR_URI}:v1
docker push ${ECR_URI}:latest

v1とlatestの両方のタグをつける理由:v1はどのバージョンかを人間が識別するため、latestは次回移行で構築予定のCodePipelineがイメージの更新を検知するためのタグです。CodePipelineのECRソースアクションは特定のタグ 1 つだけを監視するので、v2、v3と増やしていくだけでは検知されないためです。

なお、CloudShellのホームディレクトリは1GB までです。今回のAlpineベースのイメージは数十 MB なので問題ありませんが、大きなイメージを扱うときはdocker system prune で掃除してください

ECR コンソールに戻り、ecs-handson-nginxリポジトリにv1とlatestの2つのタグ(実体は同じイメージ)が入っていることを確認します。

4.ALBの設定

4-1. ターゲットグループを作成

EC2 コンソール → 左メニュー「ターゲットグループ」→「ターゲットグループの作成」

ターゲットタイプは必ず「IP アドレス」にしてください。「インスタンス」を選ぶとFargateでは使えません。FargateタスクにはEC2インスタンスIDが存在せず、ENIに割り当てられたIPアドレスしか持たないためです。

そしてターゲットは手で登録しません。ECS サービスがタスクを起動するたびに、その IP を自動でターゲットグループに登録し、タスクが止まれば自動で解除します。この「ECSがALBの登録を代行する」という関係が、ECSと ALBの連携になります。

項目 設定値
ターゲットタイプの選択 IPアドレス
ターゲットグループ名 ecs-handson-tg-blue
プロトコル : ポート HTTP:80
VPC ecs-handson-vpc
プロトコルバージョン HTTP1

※ ターゲットグループ名をecs-handson-tg-blueとしているのは、後半の回でブルーグリーンデプロイを検証するためです。

ヘルスチェック

項目 設定値
ヘルスチェックプロトコル HTTP
ヘルスチェックパス /
正常のしきい値 2
非正常のしきい値 2
タイムアウト 5秒
間隔 30秒
成功コード 200

「次へ」を押すとターゲット登録の画面になりますが、何も登録せずに「ターゲットグループの作成」を押してください。

4-2. ALB を設定する

EC2 コンソール → 左メニュー「ロードバランサー」→「ロードバランサーの作成」→「Application Load Balancer」

セキュリティグループの欄には、defaultが最初から入っていることがあります。defaultを外してecs-handson-alb-sgのみに設定してください。

項目 設定値
ロードバランサー名 ecs-handson-alb
スキーム インターネット向け
IP アドレスタイプ IPv4
VPC ecs-handson-vpc
マッピング 2つのAZ をチェックし、それぞれパブリックサブネットを選択
セキュリティグループ ecs-handson-alb-sg
リスナー HTTP : 80 → 転送先 ecs-handson-tg-blue

作成後、ALBの詳細画面に表示されるDNS名をメモします。次のような形式です。

ecs-handson-alb-1234567890.ap-northeast-1.elb.amazonaws.com

5.ECSクラスター・タスク定義・サービスの設定

5-1. クラスターを作成

ECSコンソール → 左メニュー「クラスター」→「クラスターの作成」

項目 設定値
クラスター名 ecs-handson-cluster
インフラストラクチャ AWS Fargate(サーバレス)
Container Insights オフ有効にすると CloudWatch の課金が発生します

5-2. タスク定義を作成

ECSコンソール → 左メニュー「タスク定義」→「新しいタスク定義の作成」

  • タスク定義の設定
項目 設定値
タスク定義ファミリー ecs-handson-task
  • インフラストラクチャの要件
    • タスクロール(Task Role):コンテナの中のアプリケーションがAWS APIを使用するためのロールです。今回のNginxはAWS APIを使用しないため、設定しません。
    • タスク実行ロール(Task Execution Role):ECSのインフラ側が使うロール。ECRからイメージをpullし、CloudWatch Logsにログを書くために必要になります。
項目 設定値
起動タイプ AWS Fargate
OS/アーキテクチャ Linux/X86_64
ネットワークモード awsvpc (Fargeteでは固定)
タスクサイズ – CPU .25 vCPU
タスクサイズ – メモリ .5 GB
タスクロール なし
タスク実行ロール 新しいロールの作成(ecsTaskExecutionRoleが自動生成されます)

コンテナ – 1

項目 設定値
名前 nginx
イメージ URI <アカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com/ecs-handson-nginx:latest
必須コンテナ はい
ポートマッピング – コンテナポート 80
ポートマッピング – プロトコル TCP
ポートマッピング – アプリケーションプロトコル HTTP

ECRイメージを閲覧ボタンを押下します。ウィンドウが表示されるので、対象イメージを選択してください。

ログ収集

「ログ収集を使用」をオンにします。/ecs/ecs-handson-taskというロググループが自動で作られ、コンテナの標準出力がCloudWatch Logsに流れます。トラブルシュートに必須なので必ず有効にしてください。

5-3. サービスを作成する

作成したクラスター ecs-handson-clusterを開き、「サービス」タブ →「作成」の順番に選択します。

サービスの設定

項目 設定値
タスク定義ファミリー ecs-handson-task
タスク定義のリビジョン 1
サービス名 ecs-handson-service

環境

項目 設定値
コンピューティングオプション 起動タイプ
起動タイプ Fargate
プラットフォームバージョン LATEST

デプロイ設定

項目 設定値
スケジューリング戦略 レプリカ
必要なタスク 2
アベイラビリティゾーンの再調整 チェックをつける
ヘルスチェックの猶予期間 デフォルト

デプロイオプション(展開してください)

項目 設定値
デプロイタイプ ローリングアップデート
最小実行タスク率 100
最大実行タスク率 200
デプロイ失敗の検出 – サーキットブレーカーを使用 チェックをつける
失敗時にロールバック チェックをつける

他の設定項目は、デフォルト設定値にします。

ネットワーキング

項目 設定値
VPC ecs-handson-vpc
サブネット 本記事は設定したパブリックサブネット2つ
セキュリティグループ 既存のセキュリティグループを使用を選択し、事前に作成したecs-handson-task-sgを一覧から選択
パブリックIP 本記事では、オンに設定します。
補足:NATゲートウェイまたは、エンドポイントを作成してその経路を経由する場合ははオフにします。

ロードバランシング

項目 設定値
ロードバランシングを使用 チェックをつける
VPC ecs-handson-vpc
ロードバランサーの種類 Application Load Balancer
コンテナ nginx 80:80
Application Load Balancer 既存のロードバランサーを使用を選択
ロードバランサー ecs-handson-alb
リスナー 既存のリスナーを使用 → リスナーはHTTP:80を選択します
ターゲットグループ 既存のターゲットグループを使用 → ターゲットグループはecs-handson-tg-blueを選択します。
ヘルスチェックパス /
ヘルスチェックプロトコル HTTP

設定したら作成ボタンを押下します。デプロイ完了まで、2~5分程度待ちます。

6.動作確認

ECSの画面で、クラスターの「タスク」タブを開くと、2 つのタスクが表示されます。ステータスが「実行中」になることを確認します。
ターゲットグループを見る
EC2 コンソール → ターゲットグループ → ecs-handson-tg-blue → 「ターゲット」タブ

ターゲットグループにターゲットを手動で設定していませんが、何も登録していないのに2 つの IP アドレスのヘルスステータスが、Healthyで存在している状態になります。ECS サービスが自動で登録してくれた結果を確認できます。

ブラウザで確認

ALB の DNS 名をブラウザで開きます。1234567890には、設定した際に生成された数値がはいります。

http://ecs-handson-alb-1234567890.ap-northeast-1.elb.amazonaws.com

実際に接続して、「Hello from Amazon ECS」と「version: v1」が表示されれば成功です。

  • ブラウザ表示確認時は、以下の点に注意してください。

    • 先頭にhttpと明示して入力するようにしてください。

      最近の Chrome / Edge / Safari は、アドレスバーに入力したホスト名を自動で https:// に書き換えます。今回の ALB は HTTP:80 のリスナーしか持たないため、HTTPS で接続しようとすると応答がなくタイムアウトします。

    • タスクもターゲットも正常なのに「繋がらない」と感じたら、まず先頭がhttpとなっているかを疑ってください。それでも HTTPS に昇格される場合は、シークレットウィンドウで試すか、CloudShell からcurl -I http://<ALBのDNS名>  で確認します。

    • アドレスバーに「保護されていない通信」と表示されますが、HTTP で接続しているので正常です。HTTPS 化には独自ドメインと ACM 証明書が必要になるため、本ブログでは扱いません。

まとめ

以下の点を確認しました。

  1. タスク定義はイミュータブルで、リビジョンが増えていく → 次回のリリース作業でリビジョン増加確認をします。
  2. サービスがタスクの数を維持し、ALBへの登録も代行する → ターゲットを手で登録せずに実施しました。実際にはECSにより登録されていることを確認しています。
  3. FargateではターゲットタイプがIPアドレスになる → インスタンスIDが存在しないためです。
  4. タスク実行ロールとタスクロールは役割が違う → それぞれのロールの使用目的が違います。
    • タスク実行ロール:ECS のインフラ側が使うロール
    • タスクロール : コンテナの中のアプリケーションがAWS APIを実行するためのロール

 

続き

次回は、手動リリースとCodePipelineを使用したリリースを確認します。(第2回へ移動する)

最後に

ここで作成したリソースを削除する場合は、以下のリソースを削除してください。次の回に進む場合は、同じリソースを使用しますので削除せずに進みます。

  1. ECS サービス … ecs-handson-service を削除(タスクも一緒に消えます)
  2. ECS クラスター … ecs-handson-cluster を削除
  3. ALB … ecs-handson-alb を削除
  4. ターゲットグループ … ecs-handson-tg-blue を削除(ALB 削除後でないと消せません)
  5. ECR リポジトリ … ecs-handson-nginx を削除(イメージが入っていても強制削除できます)
  6. CloudWatch ロググループ … /ecs/ecs-handson-task を削除
  7. VPC … ecs-handson-vpc を削除
    • VPC が削除できないとき:ENI(ネットワークインターフェース)が残っている可能性があります。ALB を削除した直後は ENI の解放に数分かかることがあるので、少し待ってから再試行してください。それでも消えない場合は、EC2 コンソールの「ネットワークインターフェース」でecs-handson-vpcに紐づくENI を探し、手動で削除する必要があります。

 

shibuya
著者:shibuya
クラウドセントリック株式会社 所属ITエンジニア(主にインフラ構築) 時々、演劇を観にいくことで気晴らししています。

エンジニア積極採用中!

クラウドセントリックでは、業務拡大に伴いエンジニアを積極採用中です。
一緒に働きたいと思った方は、採用フォームからぜひご応募ください。
MENU