운영 중인 AWS EKS 환경에 Kafka 인프라를 추가하던 중, 갑자기 kubectl이 동작하지 않는 상황을 맞았습니다.
이번 글에서는 문제 발생 원인과 해결 과정, 그리고 얻은 교훈을 정리했습니다.
문제 상황
Kafka 서브넷을 추가한 뒤, kubectl이 더 이상 연결되지 않았습니다.
$ kubectl get nodes
Unable to connect to the server: dial tcp 10.0.11.83:443: i/o timeout
- 이전까지는 정상 작동
- DNS는 EKS Private IP로 정상 해석
- VPN도 연결 상태 양호
즉, VPN–DNS–보안그룹 모두 정상인데 트래픽이 도달하지 못하는 상황이었습니다.
(새로 생성한 kafka는 vpn에서 접속이 정상적으로 동작하고 있음)
진단 과정
1. EKS 엔드포인트 확인
aws eks describe-cluster --name my-company-eks-cluster \\
--query "cluster.resourcesVpcConfig.{EndpointPrivate:endpointPrivateAccess,EndpointPublic:endpointPublicAccess}"
- Private Access: ✅
- Public Access: ❌
즉, VPN을 통한 내부 접근만 가능하도록 설계된 상태였습니다.
2. 라우팅 테이블 확인
라우팅 테이블을 확인하니 충격적인 결과가 나왔습니다.
{
"DestinationCidrBlock": "0.0.0.0/0",
"NatGatewayId": "nat-05a2d77a7e1491248",
"State": "blackhole"
}
👉 NAT Gateway 교체 과정에서 존재하지 않는 NAT ID가 참조되어 라우트가 블랙홀 상태로 변해 있었습니다.
3. Terraform State 확인
추가로 Terraform state를 확인했을 때, EKS 서브넷이 라우팅 테이블에 연결되지 않은 사실이 드러났습니다.
$ terraform state list | grep association
aws_route_table_association.private_db[0]
aws_route_table_association.private_db[1]
aws_route_table_association.private_kafka[0]
aws_route_table_association.private_kafka[1]
aws_route_table_association.private_kafka[2]
# 👉 aws_route_table_association.private_eks 없음 ❌
즉, 보안그룹은 이미 설정되어 있었지만, NAT 교체 과정에서 블랙홀이 발생했고, 이어서 EKS Subnet Association이 빠져버리면서 최종적으로 접근 불가 상태가 된 것이었습니다.
해결 과정
- 올바른 NAT Gateway로 라우트 교체
- aws ec2 replace-route \\ --route-table-id rtb-0f99405ce458c40d1 \\ --destination-cidr-block 0.0.0.0/0 \\ --nat-gateway-id nat-0dcc0e3f01f8c3d17
- 콘솔에서 EKS 서브넷을 라우팅 테이블에 다시 연결
- Terraform과 실제 인프라 동기화
- terraform import aws_nat_gateway.nat nat-0dcc0e3f01f8c3d17
최종 결과
모든 조치를 적용한 뒤, kubectl 연결이 정상 복구되었습니다.
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
ip-10-0-11-97.ap-northeast-2.compute.internal Ready <none> 19d v1.33.3-eks-3abbec1
교훈
- NAT 교체 시 라우트 참조를 반드시 확인
- 블랙홀 라우트가 생기면 서브넷 단위로 장애가 확산됨.
- Subnet Association은 명시적으로 관리할 것
- Terraform state에서 누락되면 실제와 불일치 발생.
- 수동 변경 후 terraform import 필수.
- 보안그룹만으로는 충분하지 않다
- 이번 케이스는 보안그룹은 이미 정상이나, 라우팅·서브넷 Association 문제가 원인이었다.
마무리
이번 장애는 단순히 보안그룹 문제나 VPN 문제로 오해할 수 있었지만, 실제 원인은 NAT 교체 과정의 블랙홀과 Subnet Association 누락이었습니다.
결국 인프라의 작은 변경이 큰 장애로 이어질 수 있다는 점을 다시금 느꼈습니다.
👉 결론: “보안그룹만 믿지 말고, NAT–라우팅–Association까지 끝까지 확인하라”
📌 참고 문서
관련 케이스
- Subnet–RouteTable Association은 라우트(예: 0.0.0.0/0 → NAT GW)와 별개이며, 명시적으로 바꿀 수 있습니다. AWS 공식 문서도 “서브넷의 라우팅 테이블 연결은 교체(Replace)할 수 있고, 교체 시 기존 커넥션이 끊길 수 있다”고 명시합니다. AWS Documentation+1
- 명시적 Association이 없어지면 서브넷은 메인 RouteTable로 암묵 연결(implicit association)로 떨어집니다. 이는 AWS 문서/지식베이스에서도 일관되게 설명돼요. AWS Documentation+2AWS Documentation+2
- Terraform 관점: 콘솔에서 replace-route-table-association 같은 수동 변경을 하면, 다음 terraform apply 때 aws_route_table_association 리소스가 “이미 다른 연결이 있다(Resource.AlreadyAssociated)”는 에러로 흔히 꼬입니다. 실제 이슈 리포트도 다수 있어요. 즉, State와 실제가 어긋나는 Drift가 나면서 연결 상태가 바뀌어 보이거나(implicit로 떨어짐) 재연결 시도가 실패합니다. GitHub+2GitHub+2
- EKS는 Private Endpoint일 때 서브넷–RouteTable–NAT 구성이 특히 중요합니다. AWS 가이드에서도 “EKS용 서브넷은 NAT로 나가는 경로가 있는 RouteTable과 제대로 연계되어 있어야 한다”고 권장합니다. 연결이 다른 테이블로 넘어가거나 블랙홀 경로가 있으면 kubectl 타임아웃으로 이어지죠. Repost+1
정리하면
- NAT 라우트의 블랙홀 자체가 Association을 ‘자동 해제’ 하진 않습니다. (별도 속성)
- 하지만 수정 과정에서
- RouteTable을 새로 만들거나(리소스 재생성),
- replace-route-table-association 류의 수동 변경,
- 혹은 Terraform 코드/State 구조 변경
이 섞이면 명시적 Association이 다른 테이블로 바뀌거나(또는 사라져 메인으로 암묵 연결), 이후 apply에서 충돌/에러가 나며 결국 “명시적 Subnet 연결이 빠진 듯한” 현상이 나타납니다. AWS Documentation+2Terraform Registry+2
비슷한 사례/자료
- 콘솔/CLI로 Association을 바꾸고 나서 Terraform이 Resource.AlreadyAssociated로 꼬이는 케이스(수동 변경 → Drift). GitHub
- terraform apply가 간헐적으로 Association 충돌을 내는 최신 이슈. GitHub
- EKS 프라이빗 접근에서 RouteTable/NAT 요건 미충족으로 kubectl 타임아웃이 나는 케이스들. Stack Overflow+1
재발 방지 체크리스트(실무 팁)
- 적용 전: terraform plan으로 Drift 유무 확인 → 이상 시 terraform import로 동기화. HashiCorp | An IBM Company
- 수정 시: NAT GW 교체는 RouteTable 재생성이 수반되지 않도록 리소스 분리(예: aws_route를 분리 관리)하고, Association은 별도 리소스로 명시적 선언. Terraform 레지스트리 가이드 참고. Terraform Registry+1
- EKS용 서브넷: 해당 Subnet이 의도한 RouteTable( NAT 경로 포함 )에 명시적으로 연결돼 있는지 주기 점검. Repost
'CLOUD > AWS' 카테고리의 다른 글
| Spot 인스턴스를 안정적으로 쓰는 방법: ASG + Capacity-Optimized + Capacity Rebalancing 2편(단점, 무중단, 장점) (0) | 2025.09.22 |
|---|---|
| Spot 인스턴스 안정성 확보: ASG + Capacity-Optimized + Capacity Rebalancing 활용하기 (0) | 2025.09.22 |
| AWS Network Loadbalancer의 ALPN이란? (0) | 2025.09.09 |
| CORS “보여주기 vs 읽기”, 프리플라이트, S3·CloudFront 정리 (1) | 2025.08.28 |
| [AWS IAM] 사용자에게 S3 전체 버킷 조회 + 객체 Get/Put/Delete 권한 부여하기 (1) | 2025.08.26 |
댓글