2026-04-10 | Prod 팀원 apply 시 Route53 삭제, Secret 값 초기화 이슈


문제 상황#

  1. Route53 레코드 삭제: EKS 재기동/축소 후 Route53 레코드가 사라짐
  2. Secrets Manager 초기화: terraform apply 시 DB/Redis 비밀번호 등이 덮어써짐

원인 1: external-dns policy: sync#

문제#

policy: sync  # 레코드 삭제도 허용

policy: sync는 k8s의 Ingress/Service가 삭제되면 해당 Route53 레코드도 삭제. EKS를 내리거나 축소하면 → Ingress 삭제 → external-dns가 Route53 레코드 삭제.

흐름#

EKS 종료/축소
  → Ingress 리소스 삭제
  → external-dns: "이 Ingress에 연결된 DNS 레코드 삭제해야지" (sync 정책)
  → Route53에서 argocd.playball.one, grafana.playball.one 등 삭제
  → 서비스 접근 불가

수정#

# staging/prod 양쪽
policy: upsert-only  # 레코드 생성/갱신만, 삭제 안 함

수정 파일#

파일브랜치
303-goormgb-k8s-helm/staging/values/infra/values-external-dns.yamldevelop, argocd-sync/staging
303-goormgb-k8s-helm/prod/values/infra/values-external-dns.yamldevelop, argocd-sync/prod

upsert-only vs sync#

syncupsert-only
레코드 생성OO
레코드 갱신OO
레코드 삭제O (위험)X
EKS 재기동레코드 삭제됨레코드 유지

참고#

  • upsert-only에서는 불필요한 레코드가 남을 수 있음 → 수동 정리 필요
  • 레코드 자동 삭제가 필요하면 --txt-owner-id로 ownership 관리 후 sync 사용 가능하지만, EKS가 자주 올리고 내리는 환경에서는 upsert-only가 안전

원인 2: Secrets Manager secret_version 덮어쓰기#

문제 (Prod)#

# prod/secrets.tf (수정 전)
resource "aws_secretsmanager_secret_version" "discord" {
  secret_string = jsonencode(var.common_secrets["prod/monitoring/discord-webhook-alerts"])
  # lifecycle ignore_changes 없음 → apply마다 tfvars 값으로 덮어씀
}

terraform apply할 때마다:

  1. var.common_secrets에 값이 있으면 → 그 값으로 덮어씀
  2. terraform.tfvars가 없거나 해당 키가 없으면 → 빈 값으로 덮어씀
  3. AWS Console에서 수동 변경한 값이 사라짐

Staging은 왜 괜찮았나#

# staging/secrets.tf (이미 안전)
resource "aws_secretsmanager_secret_version" "kafka" {
  secret_string = jsonencode({...})
  lifecycle { ignore_changes = [secret_string] }  # 이미 있었음
}

Staging은 ignore_changes = [secret_string]이 있어서 최초 생성 후 변경 무시. Prod에는 이게 없었음.

수정#

# prod/secrets.tf (수정 후)

# 1. secret 자체 삭제 방지
resource "aws_secretsmanager_secret" "this" {
  lifecycle {
    prevent_destroy = true   # 추가
    ignore_changes  = [description]
  }
}

# 2. 수동 설정 secret은 값 변경 무시
resource "aws_secretsmanager_secret_version" "discord" {
  secret_string = jsonencode(...)
  lifecycle { ignore_changes = [secret_string] }  # 추가
}

# 3. 인프라 연동 secret (RDS/Redis endpoint)은 ignore 안 함
#    → RDS/Redis 재생성 시 새 endpoint 자동 반영 필요
resource "aws_secretsmanager_secret_version" "ai_postgres" {
  secret_string = jsonencode({
    host = module.rds.address      # 동적
    password = module.rds.master_password  # 동적
  })
  # ignore_changes 없음 — 의도적
}

수정 파일#

파일변경
301-goormgb-terraform/environments/prod/secrets.tfprevent_destroy + ignore_changes 추가
301-goormgb-terraform/environments/prod/main.tfredis secret에 prevent_destroy 추가
301-goormgb-terraform/environments/staging/secrets.tfprevent_destroy 추가 (ignore_changes는 이미 있었음)

secret 보호 정책 요약#

Secret 종류prevent_destroyignore_changes이유
수동 설정 (Discord, OAuth, Mail 등)OOConsole에서 관리, terraform이 건드리면 안 됨
Kafka defaultsOO초기 설정 후 변경 없음
AI PostgreSQL (host, password)OXRDS 재생성 시 새 값 반영 필요
AI Redis (host, port)OXElastiCache 재생성 시 새 값 반영 필요
Redis (services/redis)OXElastiCache endpoint 동적

예방 조치#

  1. external-dns: upsert-only 정책 고정. sync로 바꾸지 말 것
  2. Secrets Manager: 새 secret 추가 시 반드시 prevent_destroy + 수동 값은 ignore_changes 포함
  3. terraform apply 전: terraform plan에서 destroy 또는 secret_string 변경이 있으면 apply 중단