운영사에서 DEV와 STG 환경에서만 노드를 Spot 인스턴스로 띄울 수 있게 해달라는 요청이 왔다. 회수 2분 전 통지를 EventBridge로 받아 새 노드를 띄우고, Pod를 옮기고, Spot 재고가 없으면 On-Demand로 대신 띄워 달라는 내용이었다. 하나의 서비스를 여러 고객사가 각자 쓰도록 바꾸는 멀티 테넌트 SaaS 전환 프로젝트에서, 관리자 화면의 “티어”에 이 옵션을 붙이는 일이었다.
Karpenter가 대부분을 해 준다. NodePool의 capacity type에 spot과 on-demand를 함께 넣으면 Spot을 먼저 시도하고, interruption queue를 연결하면 중단 통지를 받은 노드에 taint를 걸고 drain한 뒤 대체 노드를 띄운다. 코드에도 뼈대는 거의 다 있었고 설계는 그날 바로 확정됐다. 그런데 고칠 파일을 하나씩 다시 읽다가 함정이 네 개 나왔고, 이 기능은 결국 내가 구현하지 않고 운영사 운영팀용 가이드로 넘겼다. 함정 중 둘이 중단 이벤트 처리 쪽에 있었다.
중단 이벤트 규칙은 spot_termiation.tf에서 eventbridge/bus 모듈을 호출해 만든다. termination의 오타인데 실제 파일 이름이 이렇고, 규칙 이름에도 같은 오타가 들어가서 콘솔에서 termination으로 찾으면 안 나온다. 오타보다 큰 문제는 그 아래에 있었다. eventbridge/bus 모듈은 use_default_bus 기본값이 false다. false면 bus_name으로 커스텀 이벤트 버스를 새로 만들고 규칙과 타깃을 전부 그 버스에 붙인다.
# eventbridge/bus/main.tf
resource "aws_cloudwatch_event_bus" "this" {
count = local.use_default_bus ? 0 : 1 # 기본값 false → 커스텀 버스 생성
name = local.bus_name
}
resource "aws_cloudwatch_event_rule" "this" {
...
event_bus_name = local.use_default_bus ? "default" : aws_cloudwatch_event_bus.this[0].name
}
EC2 Spot 중단 경고와 AWS Health 이벤트는 AWS가 default 이벤트 버스로만 보낸다. 이벤트 패턴이 맞아도 규칙이 붙은 버스가 다르면 이벤트는 한 건도 들어오지 않는다. 이대로 켜면 apply는 깔끔하게 성공하는데 SQS는 늘 비어 있고, 노드는 아무 준비 없이 2분 뒤 그냥 사라진다. 고치는 건 한 줄이다.
module "spot_termination" {
source = "../../eventbridge/bus"
count = local.enabled_spot_termination ? 1 : 0
bus_name = "${local.cluster_name}-spot-termiation" # 규칙 이름 접두어로만 쓰인다
use_default_bus = true # 추가 필수
...
}
이 모듈은 애초에 켜져 있지도 않았다. Karpenter 모듈의 enabled_spot_termination은 기본값이 false이고, 02-compute 스택의 호출부는 이 값을 넘기지 않았다. dev 앱플레인의 Karpenter(v1.6.0)에는 INTERRUPTION_QUEUE 환경변수가 없었다. 두 문제는 서로를 가린다. 플래그만 켜면 리소스는 다 생기는데 이벤트가 오지 않고, 버스만 고치면 리소스가 아예 없다. 둘 다 고쳐야 처음으로 동작한다. 그래서 가이드에는 규칙이 어느 버스에 붙었는지 직접 보는 확인을 필수로 넣었다.
CLUSTER=example-dev-eks
# EventBusName 열이 default여야 한다
aws events list-rules --name-prefix "${CLUSTER}-spot-termiation" \
--query 'Rules[].[Name,EventBusName,State]' --output table
나머지 둘은 티어의 인스턴스 유형을 바꾸면 관리형 노드그룹이 통째로 교체된다는 것(instance_types가 ForceNew)과, 화면의 ‘인스턴스 자동 확장’ 체크박스 값을 백엔드도 Terraform도 읽지 않았다는 것이다.
설계를 확정한 직후 이 기능은 우리가 직접 작업하지 않기로 협의가 됐다. 코드는 건드리지 않았고 클러스터는 조회만 한 채로, 2026-08-27에 인수인계받을 운영사 운영팀에 상세 가이드를 넘겼다. 위의 함정들은 설계만 보고는 안 보이고 파일을 하나씩 열어야 나오는 것들이라 가이드 첫 장 요약에도 다시 올렸다.
9월 초에 같은 코드를 다시 열었을 때는 이 기능이 다른 모양으로 들어가 있었다. 티어 토글 대신 Purpose 태그가 DEV/STG인 앱플레인이면 Karpenter 확장분을 전부 Spot 우선으로 띄우는 방식이었고, spot_termiation.tf에는 use_default_bus = true가 들어가 있었다.
다음에 비슷한 모듈을 보면 규칙이 어느 버스에 붙는지부터 확인할 생각이다.