🌊
19.ECS FargateをCloudFormationで動かしてみる試み
1.アプリケーションの作成
- プロジェクト名を
kyoto
として作成
mkdir kyoto
cd kyoto
go mod init kyoto
go get -u github.com/labstack/echo/v4
main.go
package main
import (
"net/http"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
)
func main() {
e := echo.New()
e.HideBanner = true
e.Use(middleware.Logger())
e.Use(middleware.Recover())
e.GET("/status", func(c echo.Context) error {
return c.JSON(http.StatusOK, "OK")
})
e.Start(":8080")
}
2.Docker イメージの作成
Dockerfile
# Step 1: Build the Go application
FROM golang:1.22.6-alpine AS builder
WORKDIR /app
COPY . .
RUN go mod tidy
RUN go build -o kyoto
# Step 2: Create a lightweight image with the compiled binary
FROM alpine:3.18.8
WORKDIR /app
COPY /app/kyoto .
EXPOSE 8080
CMD ["./kyoto"]
# イメージのビルド
$ docker build -t kyoto . --no-cache
動作確認
docker run --rm -p 8080:8080 kyoto
$ curl -i localhost:8080/status
HTTP/1.1 200 OK
Content-Type: application/json
Date: Sun, 11 Aug 2024 00:45:56 GMT
Content-Length: 5
"OK"
$ curl -i localhost:8080/
HTTP/1.1 404 Not Found
Content-Type: application/json
Date: Sun, 11 Aug 2024 00:45:59 GMT
Content-Length: 24
{"message":"Not Found"}
ECRコスト感確認
-
ストレージ利用料には
GB/月あたり0.10USD
-
イメージサイズの確認
$ docker image ls kyoto
REPOSITORY TAG IMAGE ID CREATED SIZE
kyoto latest c3b398a212a6 9 minutes ago 15.5MB
- 今回
0.0152GB
なので0.0152 GB/月 x 0.10 USD = 0.0015 USD
- 為替を150円/$として
150 x 0.0015 = 0.225円
3. ECRにイメージをPush
# `aws --version`して最新でなければupgradeしておく(https://raw.githubusercontent.com/aws/aws-cli/v2/CHANGELOG.rst)
$ brew upgrade awscli
# リポジトリの作成(プロジェクト名と同じ`kyoto`で作成)
$ aws ecr create-repository \
--profile <my-profile> \
--repository-name kyoto \
--region <your-region> \
--image-scanning-configuration scanOnPush=true
# ログイン
$ aws ecr get-login-password --profile <my-profile> | docker login --username AWS --password-stdin <account-id>.dkr.ecr.<your-region>.amazonaws.com
# 作ったイメージにタグ付け
$ docker tag kyoto:latest <account-id>.dkr.ecr.<your-region>.amazonaws.com/kyoto:latest
# ECRにイメージをプッシュ
$ docker push <account-id>.dkr.ecr.<your-region>.amazonaws.com/kyoto:latest
# 上がったイメージサイズの確認(ECR上は8.08MBになった様子)
$ aws ecr describe-images \
--profile <my-profile> \
--repository-name kyoto \
--query 'imageDetails[].imageSizeInBytes' --output text
8084683
4. ECS Fargateの作成
- 確認ができたらすぐにリソースを削除して利用料を最小限にしたいので、作って簡単に消せるCloudFormationのテンプレート作成して進める
- 後々のコスト確認のために各リソースのタグに
Name: kyoto
を指定
# stackの作成(10分程度)
$ aws cloudformation create-stack \
--profile <my-profile> \
--stack-name kyoto \
--template-body file://kyoto-template.yaml \
--capabilities CAPABILITY_IAM
# 状態確認(コマンドよりマネジメントコンソールの方がわかりやすい)
$ aws cloudformation describe-stacks \
--profile <my-profile> \
--stack-name kyoto
kyoto-template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: ECS Fargate with ALB and VPC
Metadata:
TimeoutInMinutes: 10
AWS::CloudFormation::Interface:
Tags:
- Key: Name
Value: kyoto
Resources:
# VPC
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
EnableDnsSupport: true
EnableDnsHostnames: true
Tags:
- Key: Name
Value: kyoto
# Public Subnet 1
PublicSubnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: 10.0.1.0/24
AvailabilityZone: !Select [0, !GetAZs ]
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: kyoto
# Public Subnet 2
PublicSubnet2:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: 10.0.2.0/24
AvailabilityZone: !Select [1, !GetAZs ]
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: kyoto
# Internet Gateway
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: kyoto
# Attach Internet Gateway to VPC
AttachGateway:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref VPC
InternetGatewayId: !Ref InternetGateway
# Route Table
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: kyoto
# Public Route
PublicRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
# Associate Public Subnets with Route Table
SubnetRouteTableAssociation1:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnet1
RouteTableId: !Ref PublicRouteTable
SubnetRouteTableAssociation2:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnet2
RouteTableId: !Ref PublicRouteTable
# ALB Security Group
ALBSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Allow HTTP traffic to ALB
VpcId: !Ref VPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
Tags:
- Key: Name
Value: kyoto
# ALB
LoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Name: kyoto-alb
Subnets:
- !Ref PublicSubnet1
- !Ref PublicSubnet2
SecurityGroups:
- !Ref ALBSecurityGroup
Tags:
- Key: Name
Value: kyoto
# ALB Target Group
TargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
VpcId: !Ref VPC
Port: 8080
Protocol: HTTP
TargetType: ip
HealthCheckIntervalSeconds: 30
HealthCheckPath: /status
HealthCheckPort: 8080
HealthCheckProtocol: HTTP
HealthCheckTimeoutSeconds: 5
HealthyThresholdCount: 2
UnhealthyThresholdCount: 2
Matcher:
HttpCode: 200
Tags:
- Key: Name
Value: kyoto
# ALB Listener
Listener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
DefaultActions:
- Type: forward
TargetGroupArn: !Ref TargetGroup
LoadBalancerArn: !Ref LoadBalancer
Port: 80
Protocol: HTTP
# ECS Cluster
ECSCluster:
Type: AWS::ECS::Cluster
Properties:
ClusterName: kyoto-cluster
Tags:
- Key: Name
Value: kyoto
# ECS Task Definition
TaskDefinition:
Type: AWS::ECS::TaskDefinition
Properties:
Family: kyoto-task
Cpu: '256'
Memory: '512'
NetworkMode: awsvpc
RequiresCompatibilities:
- FARGATE
ExecutionRoleArn: !GetAtt ECSExecutionRole.Arn
ContainerDefinitions:
- Name: kyoto-container
Image: <image>
Essential: true
PortMappings:
- ContainerPort: 8080
HostPort: 8080
Protocol: tcp
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-group: !Ref LogGroup
awslogs-region: !Ref AWS::Region
awslogs-stream-prefix: kyoto
Tags:
- Key: Name
Value: kyoto
# ECS Fargate Service
ECSService:
Type: AWS::ECS::Service
DependsOn: Listener # https://houdoukyokucho.com/2022/07/14/post-4158/
Properties:
Cluster: !Ref ECSCluster
TaskDefinition: !Ref TaskDefinition
DesiredCount: 1
LaunchType: FARGATE
NetworkConfiguration:
AwsvpcConfiguration:
Subnets:
- !Ref PublicSubnet1
- !Ref PublicSubnet2
AssignPublicIp: ENABLED
SecurityGroups:
- !Ref ECSServiceSecurityGroup
LoadBalancers:
- ContainerName: kyoto-container
ContainerPort: 8080
TargetGroupArn: !Ref TargetGroup
DeploymentConfiguration:
MaximumPercent: 200
MinimumHealthyPercent: 100
Tags:
- Key: Name
Value: kyoto
# ECSサービスのセキュリティグループ
ECSServiceSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Allow ECS traffic
VpcId: !Ref VPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 8080
ToPort: 8080
SourceSecurityGroupId: !Ref ALBSecurityGroup
Tags:
- Key: Name
Value: kyoto
# Log Group
LogGroup:
Type: AWS::Logs::LogGroup
DeletionPolicy: "Delete" # default: Delete
UpdateReplacePolicy: "Retain" # default: Delete
Properties:
LogGroupName: /ecs/kyoto
RetentionInDays: 7
Tags:
- Key: Name
Value: kyoto
# ECS Execution Role
ECSExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: ecs-tasks.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: ECSExecutionPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- ecr:GetDownloadUrlForLayer
- ecr:BatchGetImage
- ecr:BatchCheckLayerAvailability
- logs:CreateLogStream
- logs:PutLogEvents
- s3:GetObject
- ecr:GetAuthorizationToken
Resource: '*'
Tags:
- Key: Name
Value: kyoto
Outputs:
VPCId:
Description: The ID of the VPC
Value: !Ref VPC
PublicSubnet1Id:
Description: The ID of the first public subnet
Value: !Ref PublicSubnet1
PublicSubnet2Id:
Description: The ID of the second public subnet
Value: !Ref PublicSubnet2
ECSClusterName:
Description: The name of the ECS Cluster
Value: !Ref ECSCluster
ECSServiceName:
Description: The name of the ECS Service
Value: !Ref ECSService
TaskDefinitionArn:
Description: The ARN of the ECS Task Definition
Value: !Ref TaskDefinition
5. 後片付け
$ aws cloudformation delete-stack \
--profile <my-profile> \
--stack-name kyoto
6. 検証にかかったコスト
- 0から試行錯誤しながらで
$0.55
- 為替を150円/$として
150 x 0.55 = 82.5円
Discussion