Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

51장. 통합 흐름 — 우리 서비스를 ECS 위로 올려보기

이 장에서 말하고자 하는 것

Part 8에서 우리는 컨테이너의 모든 부품을 다뤘다.

  • 41장. 왜 컨테이너인가
  • 42장. 도커 기초
  • 43장. ECR
  • 44장. ECS 구조
  • 45장. Task Definition
  • 46장. EC2 vs Fargate
  • 47장. Service와 ALB 연결
  • 48장. Service Discovery
  • 49장. 배포 전략
  • 50장. EKS

이번 장은 이 부품들이 한 묶음으로 어떻게 굴러가는지 정리한다.

작은 마이크로서비스 한 개를 처음부터 끝까지 띄워본다


1. 우리가 띄울 서비스

매우 단순한 orders 서비스다.

GET /health         → 200
GET /api/orders     → 주문 목록
POST /api/orders    → 주문 생성

이 서비스를 컨테이너로 패키징해 ALB 뒤에 ECS Fargate로 띄운다.


2. 전체 구성도

[사용자]
  ↓ HTTPS
[CloudFront]
  ↓
[ALB]   ── /api/orders/* ──→ TG-orders
  ↓
[ECS Service "orders"]
  ├─ Task 1 (Fargate)
  └─ Task 2 (Fargate)
  ↓ 내부 호출
[RDS]   ← (다음 단원에서 다룸)

이번 장에서는 ALB ~ ECS 까지 닫는다. DB는 다음 단원.


3. 단계 — 무엇을 어떤 순서로 만드는가

1. Dockerfile 작성 → 이미지 빌드
2. ECR 리포지토리 생성
3. 이미지 푸시 (orders:v1)
4. Task Definition 등록 (이미지 URL · CPU · 메모리)
5. ALB · Target Group 준비
6. ECS Cluster · Service 생성
   - desiredCount = 2
   - Target Group 연결
7. ALB Listener Rule에 /api/orders/* → TG-orders 추가
8. (있다면) Service Discovery 등록
9. ALB DNS로 호출 테스트 → 헬스 체크 통과 확인

4. 핵심 4개 파일

.
├─ Dockerfile             ← 컨테이너 이미지 정의
├─ task-definition.json   ← (또는 Terraform 리소스로 대체)
├─ buildspec / workflow   ← 빌드/푸시 자동화
└─ terraform/             ← 인프라 정의

다음 단원(자동화)에서 한 번에 묶는 흐름을 다룬다.


5. 우리 서비스에서

[ALB]
 └─ /api/orders/*  →  TG-orders
                          ↑
                  [ECS Service "orders"]
                    ├─ task v1
                    └─ task v1

이걸 그대로 users · payments 로 복제하면 마이크로서비스가 늘어 간다.

[ALB]
 ├─ /api/orders/*   → ECS "orders"
 ├─ /api/users/*    → ECS "users"
 └─ /api/payments/* → ECS "payments"

이게 1~51장의 결과물이다.


6. 직접 확인해보기 — CLI 한 바퀴

# 1. ECR 로그인 & 이미지 푸시
aws ecr get-login-password --region ap-northeast-2 \
  | docker login --username AWS --password-stdin <acct>.dkr.ecr.ap-northeast-2.amazonaws.com

docker build -t orders:v1 .
docker tag orders:v1 <acct>.dkr.ecr.ap-northeast-2.amazonaws.com/orders:v1
docker push <acct>.dkr.ecr.ap-northeast-2.amazonaws.com/orders:v1

# 2. Task Definition 등록
aws ecs register-task-definition --cli-input-json file://task-def.json

# 3. Service 만들기
aws ecs create-service \
  --cluster my-cluster \
  --service-name orders \
  --task-definition orders:1 \
  --desired-count 2 \
  --launch-type FARGATE \
  --load-balancers "targetGroupArn=<tg-arn>,containerName=app,containerPort=8080" \
  --network-configuration "awsvpcConfiguration={subnets=[subnet-x,subnet-y],securityGroups=[sg-x]}"

# 4. 호출 테스트
curl https://api.example.com/api/orders

7. 코드로는 이렇게 생겼다 — Terraform 한 묶음

resource "aws_ecr_repository" "orders" {
  name = "orders"
}

resource "aws_ecs_cluster" "main" {
  name = "msa"
}

resource "aws_ecs_task_definition" "orders" {
  family                   = "orders"
  network_mode             = "awsvpc"
  requires_compatibilities = ["FARGATE"]
  cpu                      = "512"
  memory                   = "1024"
  execution_role_arn       = aws_iam_role.task_execution.arn
  task_role_arn            = aws_iam_role.task.arn

  container_definitions = jsonencode([{
    name      = "app"
    image     = "${aws_ecr_repository.orders.repository_url}:v1"
    essential = true
    portMappings = [{ containerPort = 8080 }]
    logConfiguration = {
      logDriver = "awslogs"
      options = {
        awslogs-group         = "/ecs/orders"
        awslogs-region        = "ap-northeast-2"
        awslogs-stream-prefix = "app"
      }
    }
  }])
}

resource "aws_ecs_service" "orders" {
  name            = "orders"
  cluster         = aws_ecs_cluster.main.id
  task_definition = aws_ecs_task_definition.orders.arn
  desired_count   = 2
  launch_type     = "FARGATE"

  deployment_circuit_breaker {
    enable   = true
    rollback = true
  }

  network_configuration {
    subnets         = [aws_subnet.private_a.id, aws_subnet.private_b.id]
    security_groups = [aws_security_group.task.id]
  }

  load_balancer {
    target_group_arn = aws_lb_target_group.orders.arn
    container_name   = "app"
    container_port   = 8080
  }
}

이 한 묶음이 Part 8 전체 + 33~37장 + 19~28장 (VPC) 의 결과물이다.


8. 이렇게 쓰면 망한다 — 통합 단계의 흔한 함정

함정 1. Task의 보안 그룹이 ALB 보안 그룹과 안 맞다

ALB → Task 8080 포트가 막혀 있으면 헬스 체크 영원히 실패.

Task SG는 ALB SG에서 오는 8080을 허용해야 한다

함정 2. 서브넷이 외부 인터넷 접근이 안 된다

ECR · CloudWatch · Secrets Manager 호출이 막힌다.

프라이빗 서브넷이면 NAT Gateway 또는 VPC Endpoint 필요

함정 3. Execution Role 권한 부족

ECR pull · CloudWatch 쓰기 권한이 없으면 Task 시작 실패.

AmazonECSTaskExecutionRolePolicy 부터 붙인다

함정 4. ALB Listener Rule을 까먹는다

Service · TG는 만들었는데 ALB 규칙이 없으면 트래픽이 안 들어온다.


9. 한 줄로 정리

컨테이너 한 개를 띄우는 일은 ECR · TaskDef · Service · ALB · VPC · IAM 이 한 묶음으로 맞물려야 한다


10. 이 장의 핵심 정리

  1. Part 8 부품들이 모이면 ALB → ECS → 컨테이너 흐름이 완성된다.
  2. 새 서비스를 추가하는 패턴은 “ECR 푸시 → TaskDef → Service → ALB 규칙” 의 반복이다.
  3. 실패의 80%는 보안 그룹 · 네트워크 · IAM에서 온다.
  4. 같은 패턴을 복제해 여러 마이크로서비스로 확장한다.
  5. 다음 단원은 이 서비스에 데이터 계층을 붙인다.