
今回は証明書、SSL、クラウドフロント、ドメイン名を使ったECSをやってみたいと思います
今回の構成
今回の構成は下記のようになります。
+-----------------+ +-----------------+
| インターネット |-----> | Application |
+-----------------+ | Load Balancer |
+--------+--------+
|
| (HTTPS/443)
v
+-----------------+ +-----------------+
| パブリック | | パブリック |
| サブネット 1 | | サブネット 2 |
+--------+--------+ +--------+--------+
| |
| (HTTP/80) | (HTTP/80)
v v
+-------------------------------------------------+
| ECS Fargate クラスター |
+-------------------------------------------------+
| +-----------------+ +-----------------+ |
| | コンテナ | | コンテナ | |
| | (nginx) | | (nginx) | |
| | (ポート80) |<----->| (ポート80) | |
| +--------+--------+ +--------+--------+ |
| | | |
+----------+------------------------+------------+
|
| (VPC 内部ネットワーク)
v
+-----------------+
| VPC |
| (10.0.0.0/16) |
+-----------------+
インターネット: 外部からのHTTPSリクエストは、まずインターネットを経由してAWSのネットワークに入ってきます。
Application Load Balancer (ALB):
- インターネットからのHTTPS (ポート443) のリクエストを受け付けます。
- SSL/TLSの終端 (HTTPSの暗号化/復号) を行います。
- ヘルスチェックに基づいて、正常な状態のECS Fargateタスク内のコンテナにHTTP (ポート80) でリクエストを分散します。
- 図では、2つのパブリックサブネットにまたがって配置されています。これにより、可用性が向上します。
パブリックサブネット 1 & パブリックサブネット 2:
- これらはVPC内に作成された、インターネットへのルーティングが可能なサブネットです。
- ALBとECS Fargateタスク (ENI: Elastic Network Interface) がこれらのサブネット内に配置されます。
ECS Fargate クラスター:
- コンテナ化されたアプリケーションを実行するための基盤となる論理的なグループです。
- 図では、2つのコンテナ (nginxを実行) がFargateタスクとしてクラスター内で動作しています。
- 各コンテナはポート80でHTTPサービスを提供しています。
コンテナ (nginx):
- 実際にアプリケーション (この場合はnginx Webサーバー) が動作するDockerコンテナです。
MyTaskDefinition
で定義された内容に基づいて起動されます。- ポート80でHTTPリクエストを待ち受けています。
VPC (Virtual Private Cloud):
- AWS上に構築された隔離されたプライベートネットワークです。
- 指定されたIPアドレス範囲 (10.0.0.0/16) を持ちます。
- パブリックサブネットはこのVPCの中に作成されます。
- ECS Fargateタスク間の通信や、ALBとの通信はVPC内部のネットワークを通じて行われます
今回使用するCFテンプレート
AWSTemplateFormatVersion: '2010-09-09'
Description: ECS Fargate with HTTPS using ALB and ACM
# このテンプレートの簡単な説明です。HTTPSを使用するALB(Application Load Balancer)とACM(AWS Certificate Manager)を使ったECS Fargate構成を定義しています。
Parameters:
# パラメータは、テンプレート実行時に入力できる変数です。
VpcCIDR:
Type: String
Default: 10.0.0.0/16
# VPC(Virtual Private Cloud)のIPアドレス範囲を指定します。デフォルトは10.0.0.0/16です。
PublicSubnet1CIDR:
Type: String
Default: 10.0.1.0/24
# パブリックサブネット1のIPアドレス範囲を指定します。デフォルトは10.0.1.0/24です。
PublicSubnet2CIDR:
Type: String
Default: 10.0.2.0/24
# パブリックサブネット2のIPアドレス範囲を指定します。デフォルトは10.0.2.0/24です。
DomainName:
Type: String
Default: cosicosilife.com
# 取得済みのドメイン名を指定します。ACM証明書の発行とRoute 53のレコード作成に使用されます。
#このドメインの部分は皆さんのドメインで書き換えてください
HostedZoneId:
Type: String
Default: Z00500672Rcsdfsdfs
# Route 53のホストゾーンIDを指定します。DNSレコードの作成に使用されます。
#皆さんはここを書き換えてください
Resources:
# AWSリソースの定義セクションです。ここで様々なAWSサービスのリソースを作成・設定します。
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref VpcCIDR
# VPCのIPアドレス範囲として、Parametersセクションで定義したVpcCIDRを使用します。
EnableDnsSupport: true
# VPC内でDNS解決を有効にします。
EnableDnsHostnames: true
# VPC内のインスタンスにDNSホスト名を割り当てます。
InternetGateway:
Type: AWS::EC2::InternetGateway
# インターネットゲートウェイを作成します。VPCとインターネット間の通信を可能にします。
AttachGateway:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref VPC
# 作成したVPCを指定します。
InternetGatewayId: !Ref InternetGateway
# 作成したインターネットゲートウェイを指定し、VPCにアタッチします。
PublicSubnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
# サブネットが属するVPCを指定します。
CidrBlock: !Ref PublicSubnet1CIDR
# サブネットのIPアドレス範囲として、Parametersセクションで定義したPublicSubnet1CIDRを使用します。
AvailabilityZone: !Select [ 0, !GetAZs '' ]
# 利用可能なアベイラビリティゾーンの最初の1つを選択します。
MapPublicIpOnLaunch: true
# このサブネットで起動するインスタンスに自動的にパブリックIPアドレスを割り当てます。
PublicSubnet2:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
# サブネットが属するVPCを指定します。
CidrBlock: !Ref PublicSubnet2CIDR
# サブネットのIPアドレス範囲として、Parametersセクションで定義したPublicSubnet2CIDRを使用します。
AvailabilityZone: !Select [ 1, !GetAZs '' ]
# 利用可能なアベイラビリティゾーンの2番目の1つを選択します。
MapPublicIpOnLaunch: true
# このサブネットで起動するインスタンスに自動的にパブリックIPアドレスを割り当てます。
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
# ルートテーブルが関連付けられるVPCを指定します。
PublicRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PublicRouteTable
# ルートを追加するルートテーブルを指定します。
DestinationCidrBlock: 0.0.0.0/0
# 全てのIPアドレス宛(インターネット宛)のトラフィックを指定します。
GatewayId: !Ref InternetGateway
# インターネットゲートウェイを経由するようにルーティングを設定します。
PublicSubnet1RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnet1
# 関連付けるサブネットを指定します。
RouteTableId: !Ref PublicRouteTable
# 関連付けるルートテーブルを指定します。パブリックサブネット1をパブリックルートテーブルに関連付けます。
PublicSubnet2RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnet2
# 関連付けるサブネットを指定します。
RouteTableId: !Ref PublicRouteTable
# 関連付けるルートテーブルを指定します。パブリックサブネット2をパブリックルートテーブルに関連付けます。
ALBSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Allow HTTPS
# セキュリティグループの説明です。HTTPSトラフィックを許可します。
VpcId: !Ref VPC
# セキュリティグループが属するVPCを指定します。
SecurityGroupIngress:
# インバウンドルール(入ってくるトラフィックの許可設定)です。
- IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: 0.0.0.0/0
# 全てのIPアドレスからのTCPポート443(HTTPS)へのアクセスを許可します。
SecurityGroupEgress:
# アウトバウンドルール(出ていくトラフィックの許可設定)です。
- IpProtocol: -1
CidrIp: 0.0.0.0/0
# 全てのIPアドレスへの全てのプロトコルでのアウトバウンドトラフィックを許可します。
ECSSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Allow ECS
# セキュリティグループの説明です。ECSタスクからのトラフィックを許可します。
VpcId: !Ref VPC
# セキュリティグループが属するVPCを指定します。
SecurityGroupIngress:
# インバウンドルールです。
- IpProtocol: tcp
FromPort: 80
ToPort: 80
SourceSecurityGroupId: !Ref ALBSecurityGroup
# ALBSecurityGroupからのTCPポート80(HTTP)へのアクセスを許可します。
SecurityGroupEgress:
# アウトバウンドルールです。
- IpProtocol: -1
CidrIp: 0.0.0.0/0
# 全てのIPアドレスへの全てのプロトコルでのアウトバウンドトラフィックを許可します。
SSLCertificate:
Type: AWS::CertificateManager::Certificate
Properties:
DomainName: !Ref DomainName
# 証明書をリクエストするドメイン名を指定します。
ValidationMethod: DNS
# DNS検証を使用して証明書を検証します。
DomainValidationOptions:
- DomainName: !Ref DomainName
HostedZoneId: !Ref HostedZoneId
# ドメイン名とRoute 53のホストゾーンIDを指定してDNS検証を行います。
LoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Subnets: [!Ref PublicSubnet1, !Ref PublicSubnet2]
# ロードバランサーを配置するパブリックサブネットを指定します。
SecurityGroups: [!Ref ALBSecurityGroup]
# ロードバランサーに適用するセキュリティグループを指定します(HTTPSを許可するALBSecurityGroup)。
Scheme: internet-facing
# インターネットからのアクセスを許可するロードバランサーを作成します。
Type: application
# Application Load Balancer(ALB)を作成します。
TargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
Protocol: HTTP
# ターゲットへのトラフィックにHTTPプロトコルを使用します。
Port: 80
# ターゲットのポート番号を指定します(ECSコンテナのポート80)。
VpcId: !Ref VPC
# ターゲットグループが属するVPCを指定します。
TargetType: ip
# ターゲットをIPアドレスで指定します(FargateタスクはENIにIPアドレスを持ちます)。
HealthCheckPath: /
# ヘルスチェックのエンドポイントを指定します。
Listener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
LoadBalancerArn: !Ref LoadBalancer
# リスナーが関連付けられるロードバランサーを指定します。
Port: 443
# リッスンするポート番号を指定します(HTTPSの標準ポート)。
Protocol: HTTPS
# リッスンするプロトコルをHTTPSに設定します。
Certificates:
- CertificateArn: !Ref SSLCertificate
# 使用するSSL証明書のARN(Amazonリソースネーム)を指定します。
DefaultActions:
- Type: forward
TargetGroupArn: !Ref TargetGroup
# 受信したHTTPSリクエストをTargetGroupに転送するアクションを設定します。
DNSRecord:
Type: AWS::Route53::RecordSet
Properties:
HostedZoneId: !Ref HostedZoneId
# DNSレコードを作成するRoute 53のホストゾーンIDを指定します。
Name: !Ref DomainName
# 作成するDNSレコードの名前(ドメイン名)を指定します。
Type: A
# Aレコード(IPアドレスへのマッピング)を作成します。
AliasTarget:
DNSName: !GetAtt LoadBalancer.DNSName
# ロードバランサーのDNS名(自動的に割り当てられます)をエイリアスとして設定します。
HostedZoneId: !GetAtt LoadBalancer.CanonicalHostedZoneID
# ロードバランサーのCanonical Hosted Zone IDを指定します。
ECSCluster:
Type: AWS::ECS::Cluster
# ECS(Elastic Container Service)のクラスターを作成します。コンテナを実行する基盤となります。
TaskExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: ecs-tasks.amazonaws.com
# ECSタスクがこのロールを引き受けることを許可します。
Action: sts:AssumeRole
# AssumeRoleアクションを許可します。
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
# ECSタスクの実行に必要な権限を持つAWS管理ポリシーをアタッチします。
TaskDefinition:
Type: AWS::ECS::TaskDefinition
Properties:
RequiresCompatibilities: [FARGATE]
# Fargate起動タイプを使用することを指定します(サーバーレスコンテナ)。
Cpu: 256
# タスクに割り当てるCPUユニット数を指定します(0.25 vCPU)。
Memory: 512
# タスクに割り当てるメモリ量をMiBで指定します(512 MiB)。
NetworkMode: awsvpc
# タスクのネットワークモードとしてawsvpcを使用します(ENIを持ちます)。
ExecutionRoleArn: !GetAtt TaskExecutionRole.Arn
# タスクの実行ロールのARNを指定します。
ContainerDefinitions:
# コンテナの定義です。
- Name: web
# コンテナの名前を指定します。
Image: nginx
# 使用するDockerイメージを指定します(ここではnginx)。
PortMappings:
# コンテナのポートとホストのポートのマッピングです。
- ContainerPort: 80
# コンテナ内で公開するポート番号を指定します。
ECSService:
Type: AWS::ECS::Service
DependsOn: Listener
# このサービスはListenerリソースが作成されてから作成されます。
Properties:
Cluster: !Ref ECSCluster
# サービスが実行されるECSクラスターを指定します。
LaunchType: FARGATE
# Fargate起動タイプを使用することを指定します。
DesiredCount: 1
# 実行するタスクの希望数を指定します(ここでは1つ)。
TaskDefinition: !Ref TaskDefinition
# サービスが使用するタスク定義を指定します。
NetworkConfiguration:
AwsvpcConfiguration:
AssignPublicIp: ENABLED
# FargateタスクにパブリックIPアドレスを割り当てます。
Subnets: [!Ref PublicSubnet1, !Ref PublicSubnet2]
# タスクを配置するパブリックサブネットを指定します。
SecurityGroups: [!Ref ECSSecurityGroup]
# タスクに適用するセキュリティグループを指定します(ECSトラフィックを許可するECSSecurityGroup)。
LoadBalancers:
- TargetGroupArn: !Ref TargetGroup
# サービスに関連付けるロードバランサーのターゲットグループを指定します。
ContainerName: web
# ロードバランサーのターゲットとして登録するコンテナの名前を指定します。
ContainerPort: 80
# ロードバランサーがトラフィックを転送するコンテナのポートを指定します。
ドメイン名と、ゾーンIDの部分だけ皆さんがお使いのものに置き換えればデプロイは成功するはずです。
template の論理IDごとの説明
リソース作成 (CREATE) 時の論理IDと役割:
- nginx-fargate-demo: これはCloudFormationスタック自体の名前です。複数のAWSリソースをまとめて管理するコンテナのような役割を果たします。
- ECSService: これはECS (Elastic Container Service) のサービスを定義しています。指定したタスク定義に基づいて、指定した数のコンテナ(この場合はnginx)を起動・維持する役割を持ちます。ロードバランサーとの連携もここで設定されます。
- PublicRoute: これはVPCのパブリックルートテーブルに設定されたルーティングルールです。インターネットゲートウェイ (
InternetGateway
) へのすべてのトラフィック (0.0.0.0/0) を転送する役割を持ち、VPC内のリソースがインターネットと通信できるようにします。 - PublicRouteTable: これはVPC内に作成されたパブリックルートテーブルです。サブネット (
PublicSubnet1
,PublicSubnet2
) に関連付けられ、それらのサブネットからのトラフィックの行き先を決定する役割を持ちます。 - ECSSecurityGroup: これはECSタスクに適用されるセキュリティグループです。コンテナへのインバウンドトラフィック(この場合はALBからのHTTPトラフィック)や、コンテナからのアウトバウンドトラフィックを制御する役割を持ちます。
- MyTaskDefinition: これはECSタスクの設計図です。使用するDockerイメージ (nginx)、ポートマッピング、リソース要件 (CPU、メモリ) などを定義します。ECSサービスはこの定義に基づいてコンテナを起動します。
- SubnetRouteTableAssociation2, SubnetRouteTableAssociation1: これらは、作成したパブリックサブネット (
PublicSubnet1
,PublicSubnet2
) をパブリックルートテーブル (PublicRouteTable
) に関連付けるための設定です。これにより、これらのサブネット内のインスタンスやコンテナはパブリックルートテーブルのルーティングルールに従ってトラフィックを送信できます。 - PublicSubnet1, PublicSubnet2: これらはVPC内に作成されたパブリックサブネットです。アベイラビリティゾーンを分けて作成されており、ここにECS Fargateタスクやロードバランサーなどが配置され、インターネットからのアクセスを可能にします。
- ECSTaskExecutionRole: これはECSタスクがAWSの他のサービスと連携するために必要な権限を付与するIAMロールです。例えば、コンテナイメージの取得やログのCloudWatch Logsへの書き込みなどに使用されます。
- InternetGateway: これはVPCとインターネット間の通信を可能にするためのVPCコンポーネントです。VPC内のリソースをインターネットに接続したり、インターネットからのアクセスを受け付けたりするために必要です。
- AttachGateway: これは作成したインターネットゲートウェイ (
InternetGateway
) をVPC (MyVPC
) にアタッチするための設定です。これによって、VPCがインターネットゲートウェイを利用できるようになります。 - MyVPC: これは作成した仮想プライベートクラウド (Virtual Private Cloud) です。AWS上にプライベートなネットワーク空間を提供し、ここに他のAWSリソース(サブネット、EC2インスタンス、ECSタスクなど)を配置します。
- MyLogGroup: これはCloudWatch Logsのロググループです。ECSタスクから出力されるログをこのロググループに集約し、監視や分析を可能にします。
- ECSCluster: これはECSのクラスターです。コンテナを実行するための基盤となる論理的なグループであり、複数のコンテナインスタンス(Fargateの場合はAWSによって管理されます)をまとめて管理します。
GUIではどう見えるか?
クラスタはこのように正常に作成

サービスもアクティブ

タスクも正常に実行

タスク定義でnginxのコンテナが正常起動していることが確認できています

VPCも作成済み

その下に紐づくルートテーブルやサブネットも作成済み

インターネットゲートウェイも作成済み

アプリケーションロードバランサーが作成され、ターゲットグループが指定されています

ターゲットグループも登録済み

ACSで正常に証明書が登録されています

証明書の内容を見てみるとCNAMEが登録されています

その値はRoute 53 でも登録されているはずです

cname名 _82870c02cea6c39a64d43e4aa42a1657.cosicosilife.org.
値 _88516f10e462a2c02a80da1e526a44d0.zfyfvmchrl.acm-validations.aws.
Route53の値が伝播されているかを、https://www.whatsmydns.net/ で確認することができます

正常に起動出来たら、指定したドメインでnginx の初期画面が見えるはずです

今回は証明書を使っているので、下記のようにHTTPSで接続できるはずです

最後に
放っておくと課金されるのでCFのクラスタは検証が終わったら削除しましょう