ECS를 통한 서비스 배포 경험을 잊어먹지 않기 위한 포스팅!
Elastic Container Service
AWS 에서 제공하는 Container 관리 서비스이다. 좀 더 쉽게 container 를 run, stop 하고 monitoring 할 수 있다. 필요에 따라서 AWS 에서 제공하는 Loadbalacner, EC2 Autoscaler, ECR ( Elastic Container Registry ) 와 함께 사용되어 진다.
Region 으로 구별되어 지는 Cluster가 가장 큰 단위이고 그 안에 Service, Task 들이 존재하게 된다. 어느 VPC, Subnet으로 Task를 배치할 것인지를 설정할 수 있다.
Task 란 Docker container 의 작업 단위이다. Task를 만들기 위해서는 Task definition 이 라는 blueprint가 필요한데 이를 통해서 ECS가 task를 만들게 된다. task definition 에는 configuration, resource requirement 들이 들어가 있다.
Service 란 Task 정의를 바탕으로 가동되는 container 들의 집합을 고수준으로 관리하는 상위 집합체라 보면 된다. 서비스는 running task 의 수와 healthy check를 담당할 수 있다. 즉 running task에 장애가 발생하여 failed 될 때 service 가 알아서 새로운 task로 교체해주게 된다.
핵심적인 기능으로 auto scaling 이 가능하다는 점이다. 필요에 따라서 task를 늘리거나 줄일 수 있고 Service에는 load balancing 기능이 있어 service로 오는 traffic을 자동으로 분산 시킬 수 있다.
Capacity provider 는 ECS Cluster 에서 동작하고 있는 Task의 정의에 따른 리소스 관리를 해주는 방식이다.
우선순위 설정, scale up/down 등을 설정할 수 있고 여러 service, task 들을 동시에 관리할 수 있다.
AWS 에는 Fargate, EC2 사용에 따라서 각각 Capacity Provider가 제공되는데 사용자에 맞게 설정해주면 된다. 모든 Task 들은 각자의 capacity provider를 설정해야하고 만약 설정이 안돼있다면 default capacity provider가 적용된다. 만약 Instance_type을 지정해준다면 capacity provider 는 자동으로 설정된다. ( 해당하는 provider로)
aws cli 를 통해서 capacity provider를 등록할 수 있다.
aws ecs put-cluster-capacity-providers \
--cluster xxxxxxx \
--capacity-providers FARGATE FARGATE_SPOT \
--default-capacity-provider-strategy \
capacityProvider=FARGATE,weight=1,base=1 \
capacityProvider=FARGATE_SPOT,weight=4
{
"cluster": {
"clusterArn": "arn:aws:ecs:us-west-1:xxxx:cluster/xxxxxxx",
"clusterName": "xxxxxx",
"status": "ACTIVE",
"registeredContainerInstancesCount": 0,
"runningTasksCount": 0,
"pendingTasksCount": 0,
"activeServicesCount": 0,
"statistics": [],
"tags": [],
"settings": [
{
"name": "containerInsights",
"value": "disabled"
}
],
"capacityProviders": [
"FARGATE",
"FARGATE_SPOT"
],
"defaultCapacityProviderStrategy": [
{
"capacityProvider": "FARGATE",
"weight": 1,
"base": 1
},
{
"capacityProvider": "FARGATE_SPOT",
"weight": 4,
"base": 0
}
],
"attachments": [],
"attachmentsStatus": "UPDATE_IN_PROGRESS"
}
}
Task를 만들 때 ecs-cli 를 사용하면 굉장히 편하다. macOS기준 brew를 이용해서 받을 수 있다. aws cli도 service를 deploy 할 때 더 디테일한 설정이 가능하기 때문에 받아두는게 좋다.
brew install awscli
brew install amazon-ecs-cli
ecs-cli는 docker-compose 를 지원하기 때문에 docker-compose yaml파일을 만들어서 container를 적용하면 되고 추가적인 parameter들은 따로 yaml 파일로 만들어서 실행할 때 옵션으로 추가해주면 된다.
version: '3'
services:
app_main:
image: xxxxx:xxx
restart: always
env_file:
- .env
logging:
driver: awslogs
options:
awslogs-group: app_main
awslogs-region: your-region
awslogs-stream-prefix: prefix
command: main
app_dead:
image: xxxxx:xxx
restart: always
env_file:
- .env
logging:
driver: awslogs
options:
awslogs-group: app_main
awslogs-region: your-region
awslogs-stream-prefix: prefix
command: dead
만약 awslog ( CloudWatch ) 를 이용하고 싶다면 awslogs driver 를 선택하고 옵션들을 설정해줘야한다.
version: 1
task_definition:
task_execution_role: ecsTaskExecutionRole
ecs_network_mode: awsvpc
task_size:
mem_limit: 2.0GB
cpu_limit: 1024
run_params:
network_configuration:
awsvpc_configuration:
subnets:
- ${YOUR_SUBNET_A}
- ${YOUR_SUBNET_B}
security_groups:
- ${YOUR_SG}
assign_public_ip: "DISABLED"
ecs param에는 task_definition 정보와 run_params를 넣어줄 수 있단 task_definition의 configuration 정보를 통해서 task를 만들게 되고 run_param은 해당 task 의 환경구성에 필요한 configuration정보들을 담고 있다. aws 를 사용하기 때문에 해당 vpc, subnet 정보를 넣어주면 된다. 따로 private nat gateway 에 대한 routing table 설정이 docker image를 가져올 수 있도록 설정돼 있다면 public ip가 필요하지 않기 때문에 DISABLED 시켰다.
나는 ecs-cli 를 통해서 task definition을 만들고 난후 aws-cli를 통해서 만들어진 task definition 정보를 통해서 service를 등록시켰다. ecs-cli를 통해서 deploy 를 하게 되면 설정할 수 있는 option들에 제한이 있었다. ( 대표적으로 desired_count )
ecs-cli compose --project-name YOUR_PROJECT_NAME --ecs-params ecs-param.yml create --create-log-groups --cluster-config ${ECS_ALERT_CLUSTER_CONFIG}
cluster-config 는 mac 기준 ~/.ecs/config 안에 있는 cluster configuration name을 기반으로 선택되게 된다.
version: v1
default: default-cluster-name
clusters:
your-cluster-name-alias:
cluster: your-cluster-name
region: your-region
default_launch_type: "FARGATE"
cli 를 통해서 cluster 를 만들면 자동으로 폴더가 만들어지고 설정값을 토대로 config 파일이 만들어지게 된다. ( console를 통해서 만들었다면 따로 파일을 만들어줘도 됨 )
aws-cli를 통해서 service를 등록한다.
aws ecs create-service --cluster arn:aws:ecs:${AWS_REGION}:${AWS_ACCOUNT_ID}:cluster/${AWS_ECS_CLUSTER_NAME} \
--service-name your-service-name \
--task-definition YOUR_PROJECT_NAME \
--desired-count 1 \
--network-configuration "awsvpcConfiguration={subnets=[${YOUR_SUBNET_A},${YOUR_SUBNET_B}],securityGroups=[${YOUR_SG}]}" \
--capacity-provider-strategy capacityProvider=FARGATE,weight=1 \
--tags key=Owner,value="${AWS_TAG_OWNER}" key=Env,value=${AWS_TAG_ENV} key=Project,value=${AWS_TAG_PROJECT} key=Name,value=${AWS_TAG_NAME}
;;
다음과 같이 필요한 Option들을 추가하여서 service를 만들어줄 수 있다. service를 만들면 바로 service 에서 task definition에 맞춰서 task를 질행시켜준다.
service 에 대한 update가 필요하면 update-service를 통해서 가능하다.
aws ecs update-service --cluster arn:aws:ecs:${AWS_REGION}:${AWS_ACCOUNT_ID}:cluster/${AWS_ECS_CLUSTER_NAME} \
--service your-service-name \
--task-definition YOUR_PROJECT_NAME \
--desired-count 4 \
--network-configuration "awsvpcConfiguration={subnets=[${YOUR_SUBNET_A},${YOUR_SUBNET_B}],securityGroups=[${YOUR_SG}]}" \
--capacity-provider-strategy capacityProvider=FARGATE,weight=1 \
Control plane, Cluster 같은 부분들을 AWS에서 모두 관리해주기 때문에 좀 더 container의 관리에 집중할 수 있는 것 같다. service를 통한 task들의 가용성도 확보할 수 있다는 점이 가볍게 Production 인프라 구축을 하는데 이점이 있을 것 같다.