실습 예제 · 9
ECS Fargate 서비스 배포
Fargate를 쓰면 컨테이너를 올릴 인스턴스가 사라집니다. 패치와 용량 관리라는 책임이 AWS 쪽으로 넘어가고, 남는 것은 태스크 정의와 서비스 설정입니다. 이 실습에서 반드시 구분하고 넘어갈 것은 태스크 역할과 태스크 실행 역할입니다.
학습 목표
- 태스크 역할과 태스크 실행 역할이 각각 무엇을 위한 권한인지 구분할 수 있습니다.
awsvpc네트워크 모드에서 태스크가 갖는 네트워크 인터페이스를 설명할 수 있습니다.- Fargate 태스크를 로드 밸런서 대상 그룹에 연결할 수 있습니다.
- 서비스 오토스케일링으로 태스크 수를 조절할 수 있습니다.
- EC2 시작 유형과 Fargate의 책임 경계 차이를 설명할 수 있습니다.
시나리오
컨테이너 이미지로 배포된 API 서비스를 올립니다.
- 컨테이너를 올릴 인스턴스를 관리하고 싶지 않습니다.
- 태스크는 프라이빗 서브넷에 있고 로드 밸런서를 통해서만 접근됩니다.
- 애플리케이션이 Amazon S3에 파일을 씁니다.
- 이미지는 Amazon ECR의 프라이빗 저장소에 있습니다.
- 부하에 따라 태스크 수가 조절되어야 합니다.
세 번째와 네 번째 요구가 서로 다른 역할을 필요로 합니다. 애플리케이션 코드가 S3를 호출하는 것과, ECS 에이전트가 ECR에서 이미지를 받아오는 것은 주체가 다릅니다.
아키텍처 개요
| 항목 | 태스크 역할(taskRoleArn) |
태스크 실행 역할(executionRoleArn) |
|---|---|---|
| 누가 쓰는가 | 컨테이너 안의 애플리케이션 코드 | ECS 에이전트(플랫폼) |
| 무엇에 쓰는가 | Amazon S3·DynamoDB 등 AWS 서비스 호출 | ECR에서 이미지 가져오기, 로그 전송, 비밀 주입 |
| 누락되면 | 애플리케이션이 실행 중에 권한 오류를 낸다 | 태스크가 시작조차 되지 않는다 |
| 신뢰 주체 | ecs-tasks.amazonaws.com |
ecs-tasks.amazonaws.com |
| 항목 | EC2 시작 유형 | Fargate |
|---|---|---|
| 컨테이너 인스턴스 | 고객이 관리(패치·용량·확장) | 없다 |
| 자원 지정 | 인스턴스 타입 선택 | 태스크 단위 CPU·메모리 조합 |
| 네트워크 모드 | bridge·host·awsvpc·none |
awsvpc만 |
| 보안 그룹 | 인스턴스 단위(또는 awsvpc면 태스크 단위) |
태스크마다 네트워크 인터페이스와 보안 그룹 |
| 고르는 신호 | GPU·특수 인스턴스·비용 최적화가 중요할 때 | LEAST operational overhead |
구성 단계
1 · 역할 두 개
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": { "Service": "ecs-tasks.amazonaws.com" },
"Action": "sts:AssumeRole"
}
]
}
# 실행 역할 — ECR 이미지 가져오기와 로그 전송
aws iam create-role \
--role-name Lab09TaskExecutionRole \
--assume-role-policy-document file://ecs-tasks-trust.json
aws iam attach-role-policy \
--role-name Lab09TaskExecutionRole \
--policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
# 태스크 역할 — 애플리케이션이 쓰는 권한만 최소로
aws iam create-role \
--role-name Lab09TaskRole \
--assume-role-policy-document file://ecs-tasks-trust.json
aws iam put-role-policy \
--role-name Lab09TaskRole \
--policy-name WriteUploads \
--policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": ["s3:PutObject"],
"Resource": "arn:aws:s3:::example-lab09-uploads/*"
}]
}'
2 · 태스크 정의
{
"family": "lab09-api",
"networkMode": "awsvpc",
"requiresCompatibilities": ["FARGATE"],
"cpu": "512",
"memory": "1024",
"executionRoleArn": "arn:aws:iam::123456789012:role/Lab09TaskExecutionRole",
"taskRoleArn": "arn:aws:iam::123456789012:role/Lab09TaskRole",
"containerDefinitions": [
{
"name": "api",
"image": "123456789012.dkr.ecr.ap-northeast-2.amazonaws.com/lab09-api:1.0.0",
"essential": true,
"portMappings": [
{ "containerPort": 8080, "protocol": "tcp" }
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/lab09-api",
"awslogs-region": "ap-northeast-2",
"awslogs-stream-prefix": "api"
}
}
}
]
}
aws logs create-log-group --log-group-name /ecs/lab09-api
aws logs put-retention-policy --log-group-name /ecs/lab09-api --retention-in-days 14
aws ecs register-task-definition \
--cli-input-json file://task-definition.json \
--query 'taskDefinition.[family,revision,cpu,memory]' --output table
3 · 서비스 만들기
aws ecs create-cluster --cluster-name lab09-cluster
aws ecs create-service \
--cluster lab09-cluster \
--service-name lab09-api-svc \
--task-definition lab09-api \
--desired-count 2 \
--launch-type FARGATE \
--network-configuration "awsvpcConfiguration={
subnets=[subnet-0prv00000000000a,subnet-0prv00000000000b],
securityGroups=[sg-0task00000000000],
assignPublicIp=DISABLED
}" \
--load-balancers "targetGroupArn=$TG_ARN,containerName=api,containerPort=8080" \
--health-check-grace-period-seconds 60
대상 그룹은 대상 유형이 ip여야 합니다.
awsvpc 모드에서는 태스크마다 네트워크 인터페이스와 사설 IP가 생기고,
로드 밸런서는 인스턴스가 아니라 그 IP를 대상으로 등록하기 때문입니다.
4 · 서비스 오토스케일링
aws application-autoscaling register-scalable-target \
--service-namespace ecs \
--resource-id service/lab09-cluster/lab09-api-svc \
--scalable-dimension ecs:service:DesiredCount \
--min-capacity 2 \
--max-capacity 10
aws application-autoscaling put-scaling-policy \
--service-namespace ecs \
--resource-id service/lab09-cluster/lab09-api-svc \
--scalable-dimension ecs:service:DesiredCount \
--policy-name lab09-cpu-target \
--policy-type TargetTrackingScaling \
--target-tracking-scaling-policy-configuration '{
"TargetValue": 60.0,
"PredefinedMetricSpecification": {
"PredefinedMetricType": "ECSServiceAverageCPUUtilization"
},
"ScaleInCooldown": 300,
"ScaleOutCooldown": 60
}'
ECS 서비스에 쓸 수 있는 사전 정의 지표에는
ECSServiceAverageCPUUtilization과
ECSServiceAverageMemoryUtilization이 있습니다.
로드 밸런서 뒤라면 대상당 요청 수를 사용자 지정 지표로 쓸 수도 있습니다.
검증 방법
aws ecs describe-services \
--cluster lab09-cluster \
--services lab09-api-svc \
--query 'services[0].[status,desiredCount,runningCount,pendingCount,events[0:3].message]' \
--output json
aws ecs list-tasks --cluster lab09-cluster --service-name lab09-api-svc \
--query 'taskArns' --output text
aws ecs describe-tasks \
--cluster lab09-cluster \
--tasks $(aws ecs list-tasks --cluster lab09-cluster --service-name lab09-api-svc --query 'taskArns[0]' --output text) \
--query 'tasks[0].[lastStatus,healthStatus,stoppedReason,containers[].[name,lastStatus,reason]]' \
--output json
| 증상 | 가장 흔한 원인 |
|---|---|
태스크가 PENDING에서 멈춘다 |
이미지 가져오기 경로 없음(NAT나 엔드포인트 부재) 또는 실행 역할 권한 부족 |
| 태스크가 시작되고 곧 종료된다 | 컨테이너 프로세스가 즉시 종료됨. 로그를 확인한다 |
| 대상 그룹에 등록되지 않는다 | 대상 유형이 ip가 아니거나 컨테이너 포트 불일치 |
| 상태 확인이 계속 실패한다 | 보안 그룹이 로드 밸런서에서 컨테이너 포트를 허용하지 않음 |
| 애플리케이션이 권한 오류를 낸다 | 태스크 역할에 해당 권한이 없음 |
| 로그가 보이지 않는다 | 로그 그룹이 없거나 실행 역할에 로그 권한 없음 |
aws logs describe-log-streams \
--log-group-name /ecs/lab09-api \
--order-by LastEventTime --descending --max-items 3 \
--query 'logStreams[].logStreamName' --output text
비용 고려
| 항목 | 성격 |
|---|---|
| Fargate 태스크 | 요청한 CPU와 메모리에 대해 실행 시간만큼 |
| 최소 태스크 수 | 가용성을 위한 하한이 곧 상시 비용의 바닥이다 |
| 로드 밸런서 | 가동 시간과 처리 용량 단위 |
| NAT 게이트웨이 | 이미지 가져오기와 외부 호출이 지나면 처리량 항목이 커진다 |
| VPC 인터페이스 엔드포인트 | 엔드포인트 항목이 생기지만 NAT 처리량을 줄인다 |
| CloudWatch Logs | 수집량과 보존 기간 |
| ECR 저장 | 이미지 저장 용량. 오래된 태그는 수명 주기 정책으로 정리한다 |
Fargate는 요청한 자원에 대해 과금되므로 실제 사용량보다 크게 잡으면 그대로 낭비입니다. 지표를 보고 CPU와 메모리를 조정하는 것이 가장 직접적인 절감입니다.
프로덕션 고려사항
- 이미지 태그를 고정합니다.
latest를 쓰면 어떤 이미지가 돌고 있는지 특정할 수 없고 롤백이 어렵습니다. - 비밀은 태스크 정의의
secrets로 주입합니다. 환경 변수에 평문으로 넣으면 태스크 정의를 볼 수 있는 사람이 모두 읽을 수 있습니다. - 배포 구성을 정합니다. 최소 정상 비율과 최대 비율로 무중단 롤링 배포의 폭을 조절합니다.
- 가용 영역을 둘 이상 지정하고 태스크가 고르게 퍼지는지 확인합니다.
- 컨테이너 상태 확인을 태스크 정의에 넣으면 프로세스는 살아 있는데 응답하지 못하는 상태를 잡을 수 있습니다.
- ECR 이미지 스캔을 켜 취약점을 배포 전에 확인합니다.
- 인터페이스 엔드포인트로 ECR·로그 트래픽을 인터넷 밖으로 빼면 보안과 비용을 함께 개선할 수 있습니다(실습 10).
정리 — 리소스 삭제
aws application-autoscaling deregister-scalable-target \
--service-namespace ecs \
--resource-id service/lab09-cluster/lab09-api-svc \
--scalable-dimension ecs:service:DesiredCount
aws ecs update-service \
--cluster lab09-cluster \
--service lab09-api-svc \
--desired-count 0
aws ecs delete-service \
--cluster lab09-cluster \
--service lab09-api-svc \
--force
aws ecs delete-cluster --cluster lab09-cluster
aws logs delete-log-group --log-group-name /ecs/lab09-api
aws iam delete-role-policy --role-name Lab09TaskRole --policy-name WriteUploads
aws iam delete-role --role-name Lab09TaskRole
aws iam detach-role-policy \
--role-name Lab09TaskExecutionRole \
--policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
aws iam delete-role --role-name Lab09TaskExecutionRole
태스크 정의는 등록을 취소해도 목록에는 남습니다. 요금이 발생하지는 않지만, 필요하면 개별 리비전을 등록 취소해 정리합니다.
자주 하는 실수
| 실수 | 증상 | 고치는 법 |
|---|---|---|
| 두 역할을 하나로 씀 | 권한이 과하거나 태스크가 시작되지 않는다 | 실행 역할과 태스크 역할을 분리한다 |
| 프라이빗 서브넷에 이미지 경로 없음 | 태스크가 PENDING에서 실패한다 |
NAT 또는 ECR·로그·S3 엔드포인트를 추가한다 |
대상 그룹 유형이 instance |
태스크가 등록되지 않는다 | awsvpc 모드에서는 ip 유형을 쓴다 |
| CPU·메모리 조합이 유효하지 않음 | 태스크 정의 등록이 거부된다 | Fargate가 허용하는 조합을 확인한다 |
| 로그 그룹을 만들지 않음 | 로그가 남지 않거나 태스크가 실패한다 | 로그 그룹을 미리 만들고 보존 기간을 설정한다 |
이미지 태그가 latest |
어떤 버전이 돌고 있는지 알 수 없다 | 불변 태그를 쓰고 배포마다 갱신한다 |
| 비밀을 환경 변수 평문으로 | 태스크 정의를 조회하면 노출된다 | secrets로 주입하고 실행 역할에 읽기 권한을 준다 |
이어서 볼 곳
- 13장 · 컨테이너 ECS 구성요소와 시작 유형, 태스크 역할 구분, 서비스 오토스케일링을 다룹니다.
- 실습 2 · ALB + ASG 웹 티어 같은 로드 밸런서 구성을 인스턴스 기반으로 만들어 비교합니다.
- 실습 10 · VPC 엔드포인트로 S3 비공개 접근 프라이빗 서브넷 태스크가 이미지와 로그 경로를 확보하는 방법입니다.
- 케이스 1 · EC2에 액세스 키를 심어 배포했다 컨테이너에서도 같은 원칙이 적용됩니다 — 키가 아니라 역할입니다.
- 실습 6 · Lambda + API Gateway REST API 실행 역할과 리소스 기반 권한이라는 같은 구분을 다른 서비스에서 봅니다.
공식 문서 출처
- Amazon ECS API 레퍼런스 — RegisterTaskDefinition
—
taskRoleArn과executionRoleArn의 구분, 네트워크 모드와 호환성 값, CPU·메모리 지정 - Amazon ECS API 레퍼런스 — CreateService
— 시작 유형,
awsvpc네트워크 구성, 로드 밸런서 연결, 상태 확인 유예 기간 - Amazon ECS API 레퍼런스 — UpdateService — 원하는 태스크 수 변경과 새 배포 강제
- Application Auto Scaling API 레퍼런스 — RegisterScalableTarget
—
ecs:service:DesiredCount확장 차원 - Application Auto Scaling API 레퍼런스 — PutScalingPolicy — ECS 서비스용 사전 정의 지표
- Elastic Load Balancing API 레퍼런스 — CreateTargetGroup
— 대상 유형
ip의 의미