티스토리 뷰
개요
AWS ECS 기초부터 파악해보기에서도 이야기했지만, 이번 프로젝트는 ECS로 구축했다. 그런데, 작업을 시작할 당시에 아직 상용 서버용 AWS 계정을 전달 받지 못 했고, 개발 서버를 운영해야한다는 문제가 있었다. 이럴 경우 계정을 옮겨가야 하는데 인프라 구성을 AWS 콘솔로 작업할 경우, 개발 서버의 인프라를 상용 서버와 동일하게 구성하기 어려울 수 있다.
워낙 작업이 많아서 뭔가 놓칠 가능성이 크고, 특히 IAM과 보안 그룹이 싱크가 안맞아서 예상치 못한 문제가 생길 여지가 있다. 그래서 개발 서버에서의 모든 인프라를 Terraform으로 구성해 상용 서버용 계정을 전달 받았을 때 바로 옮길 수 있게 작업 했고, 이 글은 이 내용을 정리했다.
들어가기전에
1. Terraform 리소스는 S3에 Terraform Backend에 저장해서 사용했다.
2. VPC는 이미 구성되어 있다고 가정하고 시작한다. 옛날에 작업했던 것과 거의 동일하게 구성했다.
2024.04.18 - [개발/인프라] - Terraform으로 EKS 배포하기 1. AWS VPC 셋업
2024.04.25 - [개발/인프라] - Terraform으로 EKS 배포하기 2. AWS VPC 셋업 추가 작업
3. IAM과 보안 그룹도 그렇게 자세하게 다루면 너무 늘어질 것 같아서 가볍게 전달해보려고 한다.
4. 프로바이더는 당연히 필요하니까 생략한다.
5. 범용적으로 쓴 local 파일은 다음과 같다.
locals {
vpc = data.terraform_remote_state.vpc.outputs.vpc
security_group = data.terraform_remote_state.security_group.outputs
iam = data.terraform_remote_state.iam.outputs
}
인프라 구성
인프라 구성은 가장 보편적인 위 그림과 같이 보편적으로 알려진 방식으로 구축했다.
1. ALB
resource "aws_lb" "my_app_alb" {
name = "my-alb"
internal = false
load_balancer_type = "application"
security_groups = [local.security_group.alb_sg_id]
subnets = local.vpc.public_subnets
}
resource "aws_lb_target_group" "my_app_tg" {
name = "my-target-group"
port = 8080
protocol = "HTTP"
target_type = "ip"
vpc_id = local.vpc.vpc_id
health_check {
path = "/actuator/health"
interval = 30
timeout = 5
healthy_threshold = 3
unhealthy_threshold = 3
}
}
resource "aws_lb_listener" "my_app_listener" {
load_balancer_arn = aws_lb.my_app_alb.arn
port = 80
protocol = "HTTP"
depends_on = [
aws_lb_target_group.my_app_tg
]
}
resource "aws_lb_listener_rule" "my_app_listener_rule" {
listener_arn = aws_lb_listener.my_app_listener.arn
priority = 1
action {
type = "forward"
target_group_arn = aws_lb_target_group.my_app_tg.arn
}
condition {
host_header {
values = ["dev.myapp.com"]
}
}
depends_on = [
aws_lb_listener.my_app_listener,
aws_lb_target_group.my_app_tg
]
}
ALB와 타겟그룹, 리스너, 리스너 룰을 함께 지정했다. 헬스체크 URL을 잘 만드는게 중요하다.
그런데 뭔가 실수했을 때 다시만들려고 terraform destroy를 할 때 타겟 그룹과 리스너가 삭제가 잘 안되서 수동을 매번 지워줘야하니 한번에 잘 할 수 있도록 하자.
2. ECS Cluster
module "ecs_cluster" {
source = "terraform-aws-modules/ecs/aws"
version = "~> 4.0"
cluster_name = "my-app-cluster"
}
ECS는 Cluster라는 가장 큰 가상 영역으로 구분된다.
3. ECS Service
service는 task에 정의될 컨테이너와 외부가 어떻게 연결되는지를 정의하면 된다.
resource "aws_ecs_service" "my_app_backend_service" {
name = "my-app-backend-service"
cluster = module.ecs_cluster.cluster_id
task_definition = aws_ecs_task_definition.my_app_backend_task.arn
desired_count = 2
launch_type = "FARGATE"
network_configuration {
subnets = local.vpc.private_subnets
security_groups = [local.security_group.backend_sg_id]
assign_public_ip = false
}
load_balancer {
target_group_arn = aws_lb_target_group.my_app_tg.arn
container_name = "my-app-backend"
container_port = 8080
}
deployment_minimum_healthy_percent = 50
deployment_maximum_percent = 200
lifecycle {
ignore_changes = [task_definition]
}
}
이번에는 Fargate를 써보려고 launch_type을 Fargate로 명시했다. EC2로 구성하게 된다면 현재와 다르게 구성해야한다.
task_definition을 무시한 이유는 인프라 작업이 되면 디폴트로 작업해둔 Task Definition에 지정해 놓은 이미지로 바뀌면서, 매번 재배포되는 문제가 있었다. 그래서 변경을 무시하도록 작업해뒀다.
4. ECS Task Definition
콘솔에서 작업하면 상당히 복잡하지만 테라폼에서 제공하는 리소스를 쓰면 간단하게 만들 수 있다.
resource "aws_ecs_task_definition" "my_app_backend_task" {
family = "my-app-backend-task"
network_mode = "awsvpc" # Fargate는 awsvpc를 사용
requires_compatibilities = ["FARGATE"]
cpu = "512"
memory = "1024"
execution_role_arn = local.iam.ecs_task_execution_role_arn
task_role_arn = local.iam.ecs_task_role_arn
container_definitions = jsonencode([
{
name = "my-app-backend"
image = "[ECR Repo]/myapp-backend:latest"
cpu = 512
memory = 1024
essential = true
portMappings = [
{
containerPort = 8080
hostPort = 8080
protocol = "tcp"
}
]
environment = [
{
name = "SPRING_PROFILES_ACTIVE"
value = "dev"
}
]
}
])
}
ECR 레포지토리만 지정해주면 거기있는 이미지를 가져온다. CI/CD를 구축할 때 Task Definition에 ECR에 이미지 경로를 갱신하고 재배포하게 유도하니 잘 알아둬야 한다.
6. IAM
resource "aws_iam_role" "ecs_task_role" {
name = "notes-ecs-task-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "ecs-tasks.amazonaws.com"
}
}
]
})
}
resource "aws_iam_policy" "ecs_task_policy" {
name = "ecs-task-policy"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "s3:*" # 모든 S3 작업 허용
Effect = "Allow"
Resource = "*" # 모든 S3 리소스
},
{
Action = "rds:*" # 모든 RDS 작업 허용
Effect = "Allow"
Resource = "*" # 모든 RDS 리소스
},
{
Action = [
"ssm:GetParameter",
"ssm:GetParameters",
"ssm:GetParametersByPath"
],
Effect = "Allow",
Resource = "*"
}
]
})
}
resource "aws_iam_policy_attachment" "ecs_task_policy_attachment" {
name = "ecs-task-policy-attachment"
roles = [aws_iam_role.ecs_task_role.name]
policy_arn = aws_iam_policy.ecs_task_policy.arn
}
resource "aws_iam_role" "ecs_task_execution_role" {
name = "ecs-task-execution-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "ecs-tasks.amazonaws.com"
}
}
]
})
}
resource "aws_iam_role_policy_attachment" "ecs_task_execution_policy_attachment" {
role = aws_iam_role.ecs_task_execution_role.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
}
task에겐 S3, RDS, SSM(파라미터 스토어용) 권한을 줬다. Task Definition에 IAM에 정의한 ecs_task_role_arn, ecs_task_execution_role_arn를 사용하면 된다. 생각보다 IAM 구성이 간단하다.
마치며
EKS를 처음 구성할 당시에 비해 이미 해본 경험이 있어선지, ECS를 이용한 서버구축 자체는 그렇게 어렵지 않았다.
그런데, ArgoCD를 ECS와 연동하려면 온몸을 비틀어야해서 제외하기로 해서 결국 CI/CD 구성이 바뀌게 되었다.
GitHub Actions에서 좋은 workflow를 제공해서 결과적으로는 완성할 수 있었지만,
CI/CD를 위한 작업들에서 조금 생각할 부분들이 있었어서 다음 글에 이 내용을 다뤄보겠다.
'개발 > 인프라' 카테고리의 다른 글
GitHub Actions에서 AWS ECS CI/CD 구성해보기 (0) | 2025.02.28 |
---|---|
[Terraform] aws_iam_policy_attachment, provider produced an unexpected new value: Root object was present 오류 수정하기 (0) | 2025.01.16 |
Terraform으로 EKS 배포하기 11. Grafana Loki와 로그 모니터링 (0) | 2024.07.07 |
AWS EKS 버전 업데이트하기 (2) | 2024.06.21 |
Terraform으로 EKS 배포하기 10. ArgoCD와 GitHub 연동 (1) | 2024.06.10 |
- Total
- Today
- Yesterday
- OpenAI
- Spring
- terraform
- AWS
- JWT
- Elastic cloud
- 티스토리챌린지
- java
- 오블완
- serverless
- 후쿠오카
- GIT
- springboot
- elasticsearch
- S3
- AWS EC2
- Kotlin
- cache
- docker
- object
- 람다
- 스프링부트
- Log
- ChatGPT
- MySQL
- AOP
- lambda
- openAI API
- EKS
- CloudFront
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |