IL-0006 Neon · 2025-05-16kubernetesawsnetworking
Neon 은 스토리지와 컴퓨트를 나눈 서버리스 Postgres 를 제공한다. 컴퓨트는 하나하나가 쿠버네티스 파드 하나 안에서 가벼운 VM 으로 돈다. us-east-1 클러스터는 하루 최대 약 6천 개의 컴퓨트를 돌리고, 분당 약 500개의 새 파드가 뜨며 비슷한 속도로 유휴 데이터베이스가 종료된다. 당시 AWS VPC CNI 는 v1.18.6 이었고 기본 설정인 WARM_ENI_TARGET=1 에 WARM_IP_TARGET 은 지정하지 않은 상태였다. 이 리전의 서브넷 세 개는 각각 /20 이라 전체 IP 가 12,273개였고, 다른 리전에는 24k 개가 있다.
5월 16일 유휴 컴퓨트를 종료하는 주기 작업이 실패해 활성 컴퓨트가 약 8.1천 개로 늘었다. 기본 설정에서 VPC CNI 의 IP 관리 데몬인 ipamd 는 노드마다 ENI 하나 분량의 여유 IP 를 유지하려 하고, IP 는 ENI 단위로만 반납한다. m6id.metal 노드는 ENI 하나에 IPv4 49개를 쓴다. ipamd 는 ENI 를 Go 해시맵에 담아 두는데, Go 해시맵은 순회 순서가 무작위라서 파드도 여러 ENI 에 무작위로 배정된다. 파드가 계속 들어오는 환경에서는 ENI 마다 파드가 적어도 하나씩 남아, 한번 붙은 ENI 의 IP 가 서브넷으로 돌아가지 않았다. 그 결과 IP 약 3.4천 개가 CPU 와 메모리가 꽉 찬 노드에 묶였고, 서브넷이 바닥나 새 컴퓨트 파드를 시작할 수 없었다.
팀은 노드에 묶인 IP 를 풀려고 WARM_IP_TARGET=1 을 설정했다. 문서를 잘못 읽어 기본값이 5 라고 믿었고, 이를 1 로 줄인다고 생각했다. 실제로는 이 값을 지정하지 않으면 동작이 전혀 다른 WARM_ENI_TARGET 로직을 쓴다. WARM_IP_TARGET=1 은 IP 수천 개를 서브넷으로 돌려보내 파드가 다시 뜨게 했다. 그러나 배정이 풀린 IP 는 30초 동안 cooldown 상태가 되어 다시 쓸 수 없고, cooldown 중인 IP 도 여유 IP 로 셈한다. 그래서 파드가 하나 지워질 때마다 그 노드에서 30초 동안 파드 시작이 막혔다. 2분 타임아웃 뒤 컴퓨트 생성을 재시도하는 컨트롤 플레인 동작이 문제를 더 키웠다. 마지막 수단으로 WARM_IP_TARGET, MINIMUM_IP_TARGET, WARM_ENI_TARGET 을 모두 0 으로 바꾸자 ipamd 가 WARM_ENI_TARGET 로직으로 돌아가 IP 를 ENI 하나에 들어가는 만큼 한꺼번에 할당했다. 노드당 평균 할당 IP 가 약 75개에서 약 250개로 늘었고 파드 시작 성공률이 정상으로 돌아왔다.
5월 19일 팀은 문서만으로는 WARM_IP_TARGET=0 의 동작이 분명하지 않다고 보고 후속 조치로 WARM_IP_TARGET=1 을 다시 배포했다. 같은 파드 시작 실패가 바로 나타났고 0 으로 되돌렸지만 오류는 계속됐다. 세 값이 모두 0 이면 ipamd 는 노드의 모든 IP 가 파드에 배정되었고 지난 30초 동안 지워진 파드가 없을 때만 IP 를 더 할당한다. 파드 교체가 잦아 이 조건이 채워지지 않았다. 팀은 미리 만들어 두는 컴퓨트 풀을 늘려 사용자 영향을 줄였다. 몇 시간 뒤 컨트롤 플레인이 무거운 Postgres 쿼리 때문에 86초 동안 컴퓨트를 종료하지 못했다. 그동안 cooldown 중인 IP 가 사라져 클러스터 전체에서 IP 가 할당됐고 오류가 거의 0 으로 떨어졌다.
2025년 5월 16일, Neon 의 AWS us-east-1 클러스터에서 새 파드가 스케줄링은 되지만 IP 할당 오류를 내며 시작하지 못했다. 새 컴퓨트 파드를 시작할 수 없어 유휴 데이터베이스를 깨울 수도 없었다.
그 직전 유휴 컴퓨트를 종료하는 컨트롤 플레인 주기 작업이 실패했다. 종료는 멈추고 생성은 계속되어 활성 컴퓨트가 몇 분 만에 약 5천 개에서 약 8.1천 개로 늘었다. 파드가 CPU 와 메모리를 다 쓰자 cluster-autoscaler 가 노드를 추가했고, 노드가 늘면서 IP 할당 오류가 나타났다. 세 가용 영역 가운데 두 곳의 VPC 서브넷에서 IP 가 바닥났다.
당시 팀은 이 상황을 이해하지 못했다. 클러스터는 1만 컴퓨트까지 시험했고 서브넷 IP 는 모두 12,273개였는데, 컴퓨트 약 8천 개에서 IP 가 바닥났다. 파드에 배정된 IP 는 약 8.8천 개로 71% 였지만, 세 서브넷 IP 의 99% 인 약 12,200개가 이미 노드에 할당되어 있었다. 배정되지 않은 IP 가 넉넉해 보였지만, 그 IP 는 CPU 와 메모리가 꽉 찬 옛 노드에 묶여 있었다.
WARM_IP_TARGET=1 로 바꾸자 파드가 다시 떴지만, 컨트롤 플레인이 유휴 컴퓨트를 종료하기 시작하자 파드 시작 성공률이 크게 떨어졌다. VPC 서브넷 지표에는 할당되지 않은 IP 가 수천 개 있었는데도 새 파드 대부분이 IP 배정 문제로 시작하지 못했다. 문서에는 WARM_IP_TARGET 을 쓰면 EC2 API 요청이 속도 제한에 걸릴 수 있다고 적혀 있었지만, 노드마다 분당 시작하는 파드가 한두 개뿐이라 그럴 것 같지 않았다. aws-node DaemonSet 을 재시작할 때마다 잠깐 나아졌다. 5월 19일에는 WARM_IP_TARGET=1 을 다시 배포하자 IP 배정이 바로 실패했고, 0 으로 되돌린 뒤에도 파드 시작의 약 10% 가 몇 시간 동안 실패했다.
원인 용량 한계
노드가 추가되면서 새 파드가 스케줄링은 되지만 시작하지 못하고 IP 할당 오류를 내는 것이 보였다. 5월 19일에는 WARM_IP_TARGET=1 배포 직후 IP 배정 실패와 파드 시작 성공률 하락이 바로 나타났다.
AWS CNI 기본 설정에서는 노드마다 적어도 ENI 한두 개 분량의 IP 가 더 잡히고, IP 는 ENI 가 통째로 비어야만 반납된다. 그래서 파드에 배정된 IP 수만 보고 서브넷 여유를 판단하면 안 된다. 이번에는 IP 의 71% 만 파드에 배정된 상태에서 서브넷이 바닥났다.
WARM_IP_TARGET 은 지정하지 않을 때, 0 일 때, 1 일 때 동작이 서로 크게 다르다. cooldown 중인 IP 도 여유 IP 로 세기 때문에 WARM_IP_TARGET=1 은 파드가 지워질 때마다 그 노드의 파드 시작을 30초 동안 막을 수 있다. 문서를 잘못 읽고 바꾼 설정도, 원래대로 돌린 설정도 예상과 다르게 동작했고, 되돌리는 것만으로는 망가진 상태가 회복되지 않았다.
서브넷 크기는 부하 시험에서 정한 상한인 1만 컴퓨트를 넘지 않는다는 가정에 맞춰 잡혀 있었다. 서비스가 예상보다 빨리 커지면서 이 가정이 맞지 않게 됐다.
https://incidentlib-mcp.benchlet.com/mcpAPI 는 키가 필요 없습니다. MCP 서버는 search_incidents, get_incident, list_incidents, get_topic, vocab 도구를 내놓습니다. 사용법은 API 와 MCP.