はじめに
この記事では、前回手動で作成したリソースをCloudFormationを使ってコード化する方法について解説します。
具体的には、ECRにDockerイメージをプッシュした後、そのイメージをECSでデプロイし、実際に動作させる流れをCloudFormationを使って実装します。
記事内容は将来の自分への備忘録として記録したものであるため、ご了承ください。
前回のおさらい
前回の記事では、ECRのプライベートリポジトリに以下のような構成のDockerfileと簡単なHTMLファイルを準備し、そのDockerイメージをECRにプッシュしました。
プロジェクト構成
├── Dockerfile
└── html/
└── index.html
このDockerイメージをECRにプッシュした後、そのイメージをECSでデプロイし、実際に動作させました。
本記事では、VPCなどのシンプルなネットワーク構成からECS周りの設定までをCloudFormationでコード化し、より簡単にデプロイする方法を紹介します。
CloudFormationによるECSとECRの設定
以下のCloudFormationテンプレートを使用して、ECSの設定をコードで管理していきます。これにより、手動での設定作業を減らし、インフラの再現性を高めることができます。
AWSTemplateFormatVersion: "2010-09-09"
Resources:
# VPCの作成
VPC:
Type: "AWS::EC2::VPC"
Properties:
CidrBlock: "10.0.0.0/16"
EnableDnsSupport: true
EnableDnsHostnames: true
Tags:
- Key: "Name"
Value: "honda-vpc"
# サブネットの作成
PublicSubnet1:
Type: "AWS::EC2::Subnet"
Properties:
VpcId: !Ref VPC
CidrBlock: "10.0.0.0/20"
AvailabilityZone: "ap-northeast-1a"
Tags:
- Key: "Name"
Value: "honda-subnet-public1-ap-northeast-1a"
# インターネットゲートウェイの設定
InternetGateway:
Type: "AWS::EC2::InternetGateway"
Properties:
Tags:
- Key: "Name"
Value: "honda-igw"
AttachInternetGateway:
Type: "AWS::EC2::VPCGatewayAttachment"
Properties:
InternetGatewayId: !Ref InternetGateway
VpcId: !Ref VPC
# ルートテーブルの設定
PublicRouteTable:
Type: "AWS::EC2::RouteTable"
Properties:
VpcId: !Ref VPC
Tags:
- Key: "Name"
Value: "honda-rtb-public"
PublicRoute:
Type: "AWS::EC2::Route"
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: "0.0.0.0/0"
GatewayId: !Ref InternetGateway
SubnetRouteTableAssociation:
Type: "AWS::EC2::SubnetRouteTableAssociation"
Properties:
SubnetId: !Ref PublicSubnet1
RouteTableId: !Ref PublicRouteTable
# ECS用セキュリティグループ
SecurityGroup:
Type: "AWS::EC2::SecurityGroup"
Properties:
GroupDescription: "Allow HTTP traffic"
VpcId: !Ref VPC
SecurityGroupIngress:
- IpProtocol: "tcp"
FromPort: 80
ToPort: 80
CidrIp: "0.0.0.0/0"
# ECSクラスターの作成
ECSCluster:
Type: "AWS::ECS::Cluster"
Properties:
ClusterName: "my-ecs-cluster"
# ECSタスク定義
ECSTaskDefinition:
Type: "AWS::ECS::TaskDefinition"
Properties:
Family: "my-app-task"
RequiresCompatibilities:
- FARGATE
NetworkMode: "awsvpc"
Cpu: "256"
Memory: "512"
ExecutionRoleArn: !GetAtt ECSTaskExecutionRole.Arn
ContainerDefinitions:
- Name: "my-app-container"
Image: "xxx.dkr.ecr.ap-northeast-1.amazonaws.com/my-app-repo:latest"
PortMappings:
- ContainerPort: 80
HostPort: 80
Protocol: "tcp"
# ECSタスク実行ロール
ECSTaskExecutionRole:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Principal:
Service: "ecs-tasks.amazonaws.com"
Action: "sts:AssumeRole"
Policies:
- PolicyName: "ecsTaskExecutionPolicy"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Action:
- "ecr:GetAuthorizationToken"
- "ecr:GetDownloadUrlForLayer"
- "ecr:BatchGetImage"
- "logs:CreateLogStream"
- "logs:PutLogEvents"
- "s3:GetObject"
Resource: "*"
# ECSサービスの作成
ECSService:
Type: "AWS::ECS::Service"
Properties:
Cluster: !Ref ECSCluster
TaskDefinition: !Ref ECSTaskDefinition
DesiredCount: 1
LaunchType: "FARGATE"
NetworkConfiguration:
AwsvpcConfiguration:
Subnets:
- !Ref PublicSubnet1
SecurityGroups:
- !Ref SecurityGroup
AssignPublicIp: "ENABLED"
# ECSタスク定義
のImage: "xxx.dkr.ecr.ap-northeast-1.amazonaws.com/my-app-repo:latest"
の部分は、実際のECRのURIに置き換えて修正してください。
CloudFormationテンプレートの説明
AWS::ECS::Cluster
リソースを使って、Fargateを使ったECSクラスターを作成します。
# ECSクラスターの作成
ECSCluster:
Type: "AWS::ECS::Cluster"
Properties:
ClusterName: "my-ecs-cluster"
AWS::ECS::TaskDefinition
リソースを使って、コンテナの設定(イメージ、メモリ、CPUなど)を記述したタスク定義を作成します。
# ECSタスク定義
ECSTaskDefinition:
Type: "AWS::ECS::TaskDefinition"
Properties:
Family: "my-app-task"
RequiresCompatibilities:
- FARGATE
NetworkMode: "awsvpc"
Cpu: "256"
Memory: "512"
ExecutionRoleArn: !GetAtt ECSTaskExecutionRole.Arn
ContainerDefinitions:
- Name: "my-app-container"
Image: "340823193247.dkr.ecr.ap-northeast-1.amazonaws.com/my-app-repo:latest"
PortMappings:
- ContainerPort: 80
HostPort: 80
Protocol: "tcp"
AWS::ECS::Service
リソースで、作成したクラスター上にタスクを実行するためのサービスを作成します。ネットワーキング設定には、必要なVPCやセキュリティグループを設定します。
# ECSサービスの作成
ECSService:
Type: "AWS::ECS::Service"
Properties:
Cluster: !Ref ECSCluster
TaskDefinition: !Ref ECSTaskDefinition
DesiredCount: 1
LaunchType: "FARGATE"
NetworkConfiguration:
AwsvpcConfiguration:
Subnets:
- !Ref PublicSubnet1
SecurityGroups:
- !Ref SecurityGroup
AssignPublicIp: "ENABLED"
実際にやってみた
上記のCloudFormationテンプレートを使って、ECSクラスターを作成し、タスク定義とサービスを設定しました。
CloudFormationのサービス画面からスタックの作成に進み、作成したyamlファイル
をアップロードします。
スタック名は任意で設定できます。ここでは私の名前「honda
」としています。
画面遷移後のスタックオプション設定については、個々の設定に依存するため、私は設定せずに進めていきます。
ただ、スタックの中でIAMを使う場合は、以下のように確認が求められますでの、✅を入れて進んでください。
最後に確認を行い、問題がなければ最下部までスクロールし、「送信
」をクリックして完了です。
スタックを実行後、イベントタブで成功したことが確認できました(しばらく時間がかかります)。
デプロイ後の動作確認
デプロイが完了したら、タスクが正常に実行されていることを確認します。
ECSページの「クラスター」→「タスク」タブから、実行中のタスクを選択し、タスクのパブリックIPを確認します。
ここで、タスクが使用しているパブリックIP(例:18.183.70.246
)を確認したら、ブラウザでアクセスしてみます。
http://18.183.70.246/
実際に、前回ECRにプッシュしたindex.html
ファイルの内容が表示されていることを確認できました。この確認をもって、検証は大成功です!
今回は検証のため、ホストポートとコンテナポートを両方 80 に設定しているため、HTTP通信で接続しています。
まとめ
ECSなどのコンテナをコード化するのは、思った以上に手間がかかり、何度もエラーに直面しました。
特に、CloudFormationを使ったリソースの設定には多くの調整が必要で、時間がかかりました。
最初は手動で設定した方が早いと思いましたが、最終的にはコード化することで再利用性が高まり、運用面での効率化が実現できました。
関連記事
CloudFormationによるインフラのコード化についても過去の記事で解説しているため、より深く知りたい方は読んでみてください!