반응형
이번 포스트에서는 ECS를 실행시키기 위해 필요한 (cluster, service, task)를 구성해 보자
📌 서론
2편에서 vpc, subnet을 생성했고 3편에서는 그 안에서 사용되는 Auto Scaling Group과 용량 공급자를 모듈로 구성하고 연결했다. 이번에는 ECS 클러스터를 모듈화 하고 클러스터 내에서 운영될 ECS 서비스와 이 서비스가 사용할 ECS 태스크들도 모듈화 하여 ecs 클러스터 구성을 완료시켜 보자
이번 포스트부터는 서브모듈의 폴더 구조 설명은 생략하도록 하겠다. (서브모듈 폴더의 구성은 이전 포스트에 작성되어 있다.)
이전 포스트의 게시글을 읽고 와야 내용이 이어지니 보고 오시는 것을 추천합니다!
이번 포스트에서 다룰 내용을 다이어그램으로 이해해 보자
1. ECS 클러스터 모듈 생성하기
1-1. ECS 클러스터의 구조 쉽게 이해하기
쉽게 이해하기 위해 ECS 클러스터를 카페에 있는 바리스타 팀으로 생각해 보자. 이 팀에는 여러 바리스타(서버들)가 있고, 각자 다양한 커피와 음료(애플리케이션)를 만든다.
- 카페가 바쁠 때(애플리케이션의 수요가 많을 때), 매니저(ECS 클러스터)는 바리스타(서버)를 더 많이 불러들여 손님들을 빠르게 응대한다. 반대로, 카페가 한가할 때는 바리스타(서버) 수를 줄여서 효율적으로 운영한다. [ASG]
- 매니저(ECS 클러스터)는 카페의 메뉴(ECS 서비스)가 항상 일관된 품질을 유지하도록 관리한다. 예를 들어, 에스프레소, 라떼, 아메리카노(ECS 태스크)가 항상 같은 맛으로 나오도록 한다.
- 간단히 말해서, ECS 클러스터는 카페의 바리스타 팀처럼 여러 작업을 조직하고 관리해서, 손님들(사용자들)에게 좋은 서비스를 제공하는 역할을 한다고 볼 수 있다.
결론적으로 ECS 클러스터의 구조는 다음과 같다.
- 바리스타(서버들)
- 카페에서 바리스타가 다양한 음료를 만드는 것처럼, ECS 클러스터 내의 서버들은 여러 애플리케이션(커피와 음료)을 실행한다.
- 카페에서 바리스타가 다양한 음료를 만드는 것처럼, ECS 클러스터 내의 서버들은 여러 애플리케이션(커피와 음료)을 실행한다.
- 매니저(ECS 클러스터)
- 카페 매니저가 바쁠 때는 바리스타를 더 많이 배치하고 한가할 때는 줄이는 것처럼, ECS 클러스터(매니저)는 애플리케이션의 수요에 따라 서버(바리스타)의 수를 조절한다. 이는 ASG(Auto Scaling Group)를 통해 이루어진다.
- 카페 매니저가 바쁠 때는 바리스타를 더 많이 배치하고 한가할 때는 줄이는 것처럼, ECS 클러스터(매니저)는 애플리케이션의 수요에 따라 서버(바리스타)의 수를 조절한다. 이는 ASG(Auto Scaling Group)를 통해 이루어진다.
- 메뉴(ECS 서비스)와 음료(ECS 태스크)
- 카페 메뉴가 일관된 품질을 유지하도록 하는 것처럼, ECS 클러스터는 서비스와 태스크를 통해 애플리케이션의 일관된 실행과 관리를 보장한다.
- 카페 메뉴가 일관된 품질을 유지하도록 하는 것처럼, ECS 클러스터는 서비스와 태스크를 통해 애플리케이션의 일관된 실행과 관리를 보장한다.
1-2. ECS Cluster 코드 작성하기
- ECS 클러스터는 리소스를 선언할 때 간단하게 이름만 받아도 생성이 가능하다.
- main.tf
# AWS ECS 클러스터 리소스를 선언
resource "aws_ecs_cluster" "cluster" {
name = var.cluster_name # ECS 클러스터의 이름. 변수를 통해 외부에서 이름 지정
}
- outputs.tf
output "ecs_cluster_name" {
value = aws_ecs_cluster.cluster.id
}
- variables.tf
variable "cluster_name" {
description = "Name of the cluster"
}
1-3. root 경로의 main.tf에 "ECS 클러스터" 모듈 코드 작성하기
- main.tf에서 모듈을 작성할 때도 간단히 원하는 클러스터 명만 적어주면 된다.
# ECS 클러스터 생성
module "{모듈명 작성}" {
source = "{클러스터 서브모듈 경로}"
cluster_name = "{클러스터 이름 작성}"
}
ECS 서비스를 생성하기 전에 태스크 정의를 먼저 만들도록 하자
ECS 서비스는 내부에서 태스크 정의를 사용하기 때문에 태스크를 먼저 정의하는 것이 순서에 맞다.
2. ECS 태스크 정의 모듈 생성하기
2-1. ECS 태스크 정의 쉽게 이해하기
- ECS 태스크 정의를 요리 레시피에 비유해 보자. 요리할 때 레시피가 있어야 하듯이, ECS에서 애플리케이션을 실행하기 위해서는 태스크 정의가 필요하다.
- 레시피(태스크 정의):
- 레시피가 요리의 재료, 양, 조리법을 정의하는 것처럼, 태스크 정의는 컨테이너의 이미지, 필요한 CPU 및 메모리 양, 환경 변수, 네트워크 설정 등을 지정한다. 이는 컨테이너가 어떻게 실행될지 결정하는 규칙이다.
- 레시피가 요리의 재료, 양, 조리법을 정의하는 것처럼, 태스크 정의는 컨테이너의 이미지, 필요한 CPU 및 메모리 양, 환경 변수, 네트워크 설정 등을 지정한다. 이는 컨테이너가 어떻게 실행될지 결정하는 규칙이다.
- 재료(컨테이너 이미지):
- 레시피에 나열된 재료가 요리의 기본이 되는 것처럼, 태스크 정의에서 컨테이너 이미지는 애플리케이션을 실행하는 데 필요한 기본 구성요소다.
- 레시피에 나열된 재료가 요리의 기본이 되는 것처럼, 태스크 정의에서 컨테이너 이미지는 애플리케이션을 실행하는 데 필요한 기본 구성요소다.
- 조리법(설정과 명령):
- 레시피에 따라 요리를 어떻게 준비하고 조리할지 안내하는 것처럼, 태스크 정의는 컨테이너가 어떻게 실행되어야 하는지, 어떤 명령을 따라야 하는지 알려준다.
2-2. ECS Task Definition 코드 작성하기
- 태스크 정의 리소스가 이전까지와 조금 다른 점은 그 내부 코드에 json 형식으로 컨테이너 정의를 외부에서 주입하도록 작성되어 있다는 점이다. 지금까지는 그냥 일반적인 값들을 넣었지만 이번에는 json으로 컨테이너 정의를 넣어줘야 한다.
- main.tf
# AWS ECS 태스크 정의 리소스를 선언
resource "aws_ecs_task_definition" "ecs_task_prod" {
family = var.family_name # 태스크 정의 가족 이름
network_mode = "bridge" # 네트워크 모드 설정
requires_compatibilities = ["EC2"] # 호환 가능한 실행 환경 (여기서는 EC2)
execution_role_arn = var.execution_role_arn # 실행 역할의 ARN
cpu = var.cpu # 태스크에 할당할 CPU 단위
memory = var.memory # 태스크에 할당할 메모리 크기
container_definitions = var.container_definitions # 컨테이너 정의 (JSON 형식)
}
- outputs.tf
output "ecs_task_definition_arn" {
description = "ARN of the ECS task definition"
value = aws_ecs_task_definition.ecs_task_prod.arn
}
- variables.tf
variable "execution_role_arn" {
description = "Execution role ARN for ECS task execution"
type = string
default = "arn:aws:iam::473709498430:role/ecsTaskExecutionRole"
}
variable "cpu" {
description = "CPU units for the ECS task"
type = number
default = 256
}
variable "memory" {
description = "Memory for the ECS task (in MiB)"
type = number
default = 512
}
variable "family_name" {
description = "task_name"
type = string
}
variable "container_definitions" {
description = "JSON formatted container definitions for the zipkin service"
type = string
}
2-3. 컨테이너 정의를 json 코드로 생성하는 방법
- 먼저 "태스크 정의" 페이지로 들어온다. (aws 콘솔에서 ecs 클러스터로 들어온 다음 좌측의 메뉴바를 확인하면 존재할 것이다.)
- 여기서 필요한 것은 이미 작성한 태스크 정의다. (없다면 실제로 사용할 태스크 정의를 하나 생성하고 다시 이 화면으로 오자)
- 태스크 정의를 생성했다면 이름을 클릭해서 "개요"페이지로 들어간다. 그다음 우측의 "작업" 버튼을 누르고 "JSON을 사용하여 새 개정 생성"버튼을 누른다.
- 아래와 같이 페이지가 이동되면서 하단에는 JSON 코드가 작성되어 있을 것이다. 이때 잘 보면 family 하단에 containerDefinitions 부분이 있는데 이곳을 복사해서 테라폼 코드에 넣어주면 된다.
2-4. root 경로의 main.tf에 "ECS 태스크 정의" 모듈 코드 작성하기
- 하단의 코드에서 위에서 가져온 container_definitions(json 코드)를 작성해 준다.
# ECS 태스크 정의: 멤버
module "{모듈명}" {
source = "{태스크 정의 서브모듈 경로}"
family_name = "{태스크 정의 이름}"
execution_role_arn = "{ecsTask 실행 역할}"
cpu = {cpu 사양}
memory = {메모리 사양}
container_definitions = jsonencode([
{
name = "{컨테이너명}"
image = "{ecr 이미지 uri}"
cpu = {cpu 사양}
memory = {메모리 사양}
essential = true
portMappings = [
{
containerPort = 8081
hostPort = 0
protocol = "tcp"
}
]
secrets = [
{
name = "{시크릿의 id}"
valueFrom = "{시크릿의 value (나는 secrets manager를 사용해서 arn에서 값을 선택해서 넣어줌)}"
}
]
logConfiguration = {
logDriver = "awslogs"
options = {
"awslogs-group" = "/ecs/recipia-member-prod"
"awslogs-region" = "ap-northeast-2"
"awslogs-stream-prefix" = "ecs"
}
}
}
])
}
3. ECS 서비스 모듈 생성하기
3-1. ECS 서비스 쉽게 이해하기
- 식당(ECS 서비스)
- 식당은 여러 요리사(ECS 태스크)가 요리(애플리케이션)를 만들어 손님(사용자)에게 제공하는 곳이다. 마찬가지로, ECS 서비스는 정의된 ECS 태스크 정의들을 관리하고, 애플리케이션의 가용성을 유지하는 역할을 한다.
- 식당은 여러 요리사(ECS 태스크)가 요리(애플리케이션)를 만들어 손님(사용자)에게 제공하는 곳이다. 마찬가지로, ECS 서비스는 정의된 ECS 태스크 정의들을 관리하고, 애플리케이션의 가용성을 유지하는 역할을 한다.
- 요리사 관리(태스크 인스턴스 관리)
- 식당에서 요리사들이 요리를 잘 만들어 내기 위해서는 적절한 수의 요리사가 필요하다. 너무 적으면 요리가 지연되고, 너무 많으면 비효율적이다. ECS 서비스는 ECS 태스크의 인스턴스(컨테이너)의 수를 자동으로 조절해서, 애플리케이션이 필요한 작업량을 처리할 수 있게 해 준다.
- 식당에서 요리사들이 요리를 잘 만들어 내기 위해서는 적절한 수의 요리사가 필요하다. 너무 적으면 요리가 지연되고, 너무 많으면 비효율적이다. ECS 서비스는 ECS 태스크의 인스턴스(컨테이너)의 수를 자동으로 조절해서, 애플리케이션이 필요한 작업량을 처리할 수 있게 해 준다.
- 손님 수에 따른 요리사 조절(스케일링)
- 식당은 손님이 많을 때는 더 많은 요리사를 투입하고, 손님이 적을 때는 줄여야 효율적이다. ECS 서비스도 마찬가지로, 트래픽이나 작업량에 따라 태스크 인스턴스의 수를 늘리거나 줄여서, 리소스를 효율적으로 관리한다.
- 식당은 손님이 많을 때는 더 많은 요리사를 투입하고, 손님이 적을 때는 줄여야 효율적이다. ECS 서비스도 마찬가지로, 트래픽이나 작업량에 따라 태스크 인스턴스의 수를 늘리거나 줄여서, 리소스를 효율적으로 관리한다.
- 지속적인 서비스 제공(고가용성)
- 식당은 언제나 손님에게 요리를 제공할 준비가 되어 있어야 한다. ECS 서비스는 태스크 인스턴스가 실패하거나 문제가 생겼을 때 자동으로 새 인스턴스를 시작해서 애플리케이션의 지속적인 가용성을 보장한다.
- 식당은 언제나 손님에게 요리를 제공할 준비가 되어 있어야 한다. ECS 서비스는 태스크 인스턴스가 실패하거나 문제가 생겼을 때 자동으로 새 인스턴스를 시작해서 애플리케이션의 지속적인 가용성을 보장한다.
- 특정 규칙에 따른 서비스(로드 밸런싱)
- 식당에 손님이 몰리면, 요리사들이 일을 고르게 분배받아서 해야 한다. ECS 서비스는 로드 밸런서를 사용해서 들어오는 요청을 태스크 인스턴스에 고르게 분배한다. 이렇게 해서 각 인스턴스에 과부하가 걸리지 않도록 조절하는 것이다.
이렇게 ECS 서비스는 애플리케이션의 실행, 관리, 스케일링, 그리고 고가용성을 제공하는 '식당'과 같은 역할을 한다. 태스크 정의가 '레시피'라면, ECS 서비스는 그 레시피를 사용해 지속적으로 '요리'를 만들어내고 관리하는 '식당'의 역할이라고 할 수 있다.
3-2. ECS Service 코드 작성하기
- 여기서 주목할 점은 dynamic 블록을 사용한다는 점이다.
- 다이나믹 블록을 사용하는 ecs 서비스 설정은 아래의 글에 자세히 적어놨다.
- main.tf
# AWS ECS 서비스 리소스를 선언
resource "aws_ecs_service" "ecs_service" {
name = var.ecs_service_name # ECS 서비스 이름
cluster = var.ecs_cluster_name # 사용할 ECS 클러스터의 이름
task_definition = var.task_definition # 사용할 태스크 정의
desired_count = 1 # 원하는 태스크 수 (이 경우 1개)
force_new_deployment = false # 새 배포 강제 여부 (false: 강제하지 않음)
# 용량 공급자 전략을 동적으로 정의
dynamic "capacity_provider_strategy" {
for_each = var.capacity_provider_name != null ? [1] : [] # 조건에 따라 반복
content {
capacity_provider = var.capacity_provider_name # 용량 공급자 이름
weight = 100 # 용량 공급자에 대한 가중치
}
}
# 로드 밸런서 설정을 동적으로 정의
dynamic "load_balancer" {
for_each = var.target_group_arn != null ? [1] : [] # 조건에 따라 반복
content {
target_group_arn = var.target_group_arn # 타깃 그룹 ARN
container_name = var.container_name # 컨테이너 이름
container_port = var.container_port # 컨테이너 포트
}
}
}
- outputs.tf
output "ecs_service_name" {
value = aws_ecs_service.ecs_service.name
}
- variables.tf
variable "ecs_cluster_name" {
description = "Name of the cluster include id"
default = null
}
variable "ecs_service_name" {
description = "Name of the ecs service"
default = null
}
variable "task_definition" {
description = "JSON formatted task definition"
}
variable "capacity_provider_name" {
description = "Name of the capacity provider"
default = null
}
variable "target_group_arn" {
description = "ARN of the target group for the load balancer"
default = null
}
variable "container_name" {
description = "Name of the container in the task definition"
default = null
}
variable "container_port" {
description = "Port on which the container is listening"
default = null
}
# ecs_cluster_service 모듈 내부
variable "asg_arn" {
description = "Auto Scaling Group ARN for dependency management"
type = string
default = null
}
3-3. root 경로의 main.tf에 "ECS 서비스" 모듈 코드 작성하기
# ECS 서비스 생성 (capacity_provider, loadbalancing사용 o)
module "{모듈명}" {
source = "{서브모듈 경로}"
ecs_service_name = "{ecs 서비스 이름}"
ecs_cluster_name = {ecs 클러스터 이름}
# recipe_asg 모듈의 출력값을 입력으로 사용 (단순히 의존성만을 위함)
asg_arn = {오토 스케일링 그룹 설정}
task_definition = {ecs 태스크 정의 arn}
capacity_provider_name = {용량 공급자 이름}
target_group_arn = "{alb 타겟그룹 arn}"
container_name = "{alb 적용시킬 컨테이너 이름}"
container_port = {포트 설정}
}
# ECS 서비스 생성 (capacity_provider, loadbalancing사용 x)
module "{모듈명}" {
source = "{서브모듈 경로}"
ecs_service_name = "{ecs 서비스 이름}"
ecs_cluster_name = {ecs 클러스터 이름}
# recipe_asg 모듈의 출력값을 입력으로 사용 (단순히 의존성만을 위함)
task_definition = {ecs 태스크 정의 arn}
}
이렇게 Auto Scaling Group과 Capacity Provider를 사용하는 ecs 모듈 생성을 완료했다. 다만 지프킨 서버를 동작시키는 ecs는 asg를 사용하지 않도록 설계되어 있어 지금까지와는 다른 방법으로 ecs 모듈을 생성하게 된다. 이 내용을 다음 포스트에서 소개하도록 하겠다.
반응형
'AWS > 테라폼(Terraform)' 카테고리의 다른 글
실전! 테라폼 적용기 [추가설명] - dynamic 블록 사용하기 (0) | 2023.12.18 |
---|---|
실전! 테라폼 적용기3편 - Auto Scaling Group과 용량 공급자(Capacity provider) 작성 (1) | 2023.12.18 |
실전! 테라폼 정복기 2편 - vpc, subnet 설정과 인프라 구축 (0) | 2023.12.16 |
실전! 테라폼 정복기 1편 - 테라폼(Terraform) 모듈화 (1) | 2023.12.09 |
테라폼(Terraform) 사용 가이드 7: AutoScailingGroup과 LoadBalancer을 생성하고 연결하기 (0) | 2023.12.07 |