IL-0246 Adyen · 2026-07-08 (게시일)kubernetesnetworking
Cilium 에이전트가 엔드포인트를 만들지 못해 빅데이터 쿠버네티스 노드에서 새 파드가 뜨지 못했다
Adyen 은 100개가 넘는 쿠버네티스 클러스터 모두에서 Cilium CNI 를 쓴다. 운영 빅데이터 클러스터는 Kubernetes 1.31.7 과 Cilium 1.16.5 로 돌았고, 노드는 물리 코어 64개와 RAM 512GB, 또는 물리 코어 128개와 RAM 2TB 였다. 500개가 넘는 HDFS datanode 에서 파일을 하나 받을 때마다 새 연결이 생겨, Trino 파드 하나가 붐비는 시간에 1분에 약 50,000개 연결을 만들었다. Spark 같은 배치 작업 파드는 1초에서 몇 시간까지 살다 사라졌다. 팀은 몇 달 전 512GB 노드의 연결 추적 고갈을 막으려고 Helm 차트에 cilium_bpf_map_dynamic_size_ratio: 0.0050 을 넣었고, 2TB 노드의 TCP 연결 추적 테이블 최대 크기는 기본값 800만의 두 배인 16,777,216 개가 됐다.
Cilium 은 엔드포인트를 만들 때 scrubIPsInConntrackTable 함수로 연결 추적 테이블 전체를 훑어, 그 파드의 IP 에 이전 연결의 흔적이 남지 않았는지 확인하고 지운다. 팀은 pprof 실행 추적에서 에이전트가 이 함수 안에서 nextKey() 와 lookup() BPF 시스템 콜을 쉼 없이 번갈아 부르는 것을 봤다. 35밀리초 구간에서 시스템 콜 14,916번을 세었고, 초당 약 213,000개 항목을 훑는 속도였다. 이 작업은 앞 키가 있어야 다음 키를 얻으므로 병렬화할 수 없었다. cilium bpf ct list global 로 본 테이블에는 약 700만 개 항목이 있었다. 대부분 이미 만료된 항목이었고, 몇 시간 전에 만료된 것도 있었다. 700만 개를 훑는 데 35초, 꽉 찬 1,600만 개를 훑는 데는 80초까지 걸릴 수 있어 CNI 타임아웃 90초에 가까웠다.
만료된 항목이 남은 까닭은 가비지 컬렉션 주기의 자동 조정이었다. Cilium 의 주기적 가비지 컬렉션은 한 번에 지운 항목이 5% 보다 적으면 주기를 1.5배로 늘리고 25% 보다 많으면 줄인다. 주기는 5분에서 시작해 최대 12시간까지 늘어난다. 1,600만 개 테이블에서 5% 는 800,000개가 넘는다. 연결이 적던 노드에서 주기가 12시간까지 늘어난 뒤 무거운 데이터 작업이 올라오면, 다음 가비지 컬렉션까지 만료된 항목이 쌓였다. 연결 추적 테이블에는 전역 mutex 가 있었다. 그래서 파드 50개가 한꺼번에 뜨면 하나씩 차례로 80초 동안 테이블을 훑었고, 뒤의 파드들은 기다리다 90초 타임아웃을 냈다. 타임아웃 뒤에도 작업은 멈추지 않았고, 삭제 요청과 kubelet 의 재시도가 더해져 대기열이 줄지 않았다. 팀은 conntrackGCInterval: 60s 로 주기를 고정했고, 오전 9:00 에 적용해 10:00 에 DaemonSet 배포를 마쳤다. 그 뒤 연결 추적 테이블 크기는 크게 줄어 안정됐고, 타임아웃 오류는 다시 나지 않았다. SLO 를 어기기 전에 해결했다. 빅데이터 환경은 결제 처리와 분리돼 있어 실시간 결제와 가맹점 거래에는 영향이 없었다.
- 심각도
- –
- 영향 범위
- New pods on affected nodes of Adyen's production big data Kubernetes clusters could not start; no SLO was breached and payment processing was not affected
- 영향 사용자
- –
- 지역
- –
- 감지까지
- –
- 복구까지
- –
증상
Adyen 의 운영 빅데이터 쿠버네티스 클러스터에서 새 파드가 네트워크 설정 단계에서 타임아웃으로 실패했다. 한 파드가 실패하면 같은 노드에 뜨려는 다른 파드도 모두 타임아웃을 냈다.
파드 이벤트에는 `Failed to create pod sandbox: rpc error: code = Unknown desc = failed to setup network for sandbox "0fecf4844d3f8f2df218f09d91f9698bb424e2166551952f46fd7638f4757cf2": plugin type="cilium-cni" failed (add): unable to create endpoint: Cilium API client timeout exceeded` 가 찍혔다.
영향받은 노드에서는 Cilium 엔드포인트를 지우고 더하는 DELETE /v1/endpoint 와 PUT /v1/endpoint 의 API 처리 시간도 늘었다. 16:20 부터 엔드포인트 호출 지연이 시간이 갈수록 선형으로 늘었고, 엔드포인트 생성과 삭제가 끝나지 않았다.
Cilium 에이전트 로그에는 관련된 타임아웃이 없었고, 디버그 로그를 켜도 오류가 보이지 않았다. 고장 난 노드에서 cilium endpoint list 는 타임아웃을 냈다. cilium endpoint log <ENDPOINT_ID> 는 동작했고 엔드포인트가 regenerating 단계에 멈춰 있다고 보여 줬다. kubectl get ciliumendpoint 는 여전히 regenerating 이라고 나왔지만, 엔드포인트의 쿠버네티스 매니페스트는 ready 였다. /var/run/cilium/state 에는 이름이 _next 로 끝나는 엔드포인트 폴더가 남아 있었다. 팀은 이것으로 문제가 kubelet 이나 CNI 플러그인이 아니라 Cilium 에이전트 안에 있다고 확인했다.
여러 파드가 한꺼번에 뜨면 문제가 커졌다. CNI 가 90초 뒤 타임아웃을 내도 에이전트는 작업을 멈추지 않았고, containerd 가 곧바로 DeleteEndpoint() 를 불러 삭제 요청까지 쌓였다. kubelet 이 60~90초 뒤 다시 시도할 때마다 생성과 삭제 요청이 대기열에 더해졌다. 파드 cilium-node-breaker-5 는 15:54:30 에 첫 생성을 시도했고, 15:56:00 에 타임아웃으로 삭제된 뒤 15:57:12, 15:59:57, 16:02:39 에 다시 생성을 시도했다.
원인
- 촉발
- 몇 달 전 Helm 차트에 cilium_bpf_map_dynamic_size_ratio: 0.0050 을 설정해, 2TB RAM 노드의 TCP 연결 추적 테이블 최대 크기가 기본값의 두 배인 16,777,216 개가 됐다. 이 큰 테이블이 연결 추적 가비지 컬렉션의 주기 자동 조정과 맞물려, 연결이 적던 노드에 무거운 데이터 작업이 올라오면 만료된 항목이 몇 시간 동안 쌓였다.
감지
–
조치
- Cilium 설정에 conntrackGCInterval: 60s 를 넣어 가비지 컬렉션 주기가 자동으로 늘어나지 않게 고정했다. 오전 9:00 에 적용해 10:00 에 DaemonSet 배포를 마쳤고, 연결 추적 테이블 크기와 엔드포인트 API 처리 시간이 정상으로 돌아왔다.
후속 조치
- 워크로드가 동적으로 바뀌는 Cilium 환경에서는 conntrackGCInterval: 60s 설정과 함께, 가비지 컬렉션 시간 cilium_datapath_conntrack_gc_duration_seconds 와 테이블 크기 cilium_datapath_conntrack_gc_entries 에 미리 알림을 건다. 이 장애 때 가비지 컬렉션 시간은 1초에서 80초로 뛰었고 테이블에는 대부분 만료된 항목 700만 개가 있었다.
교훈
규모에 맞춰 바꾼 값은 트래픽이 늘면서 몇 달 뒤에야 전체 영향을 드러낼 수 있다. 512GB 노드를 위해 바꾼 bpf_map_dynamic_size_ratio 는 처음 문제를 풀었지만, 2TB 노드에서 커진 테이블이 가비지 컬렉션 주기 자동 조정과 맞물렸다. 로그는 증상만 보여 줬고, 원인은 프로파일링과 추적으로 컨테이너 런타임, CNI 플러그인, Cilium 에이전트, 리눅스 커널까지 훑어야 보였다. 실제 숫자로 어림셈을 하자 전체 흐름을 이해하기 전에 원인을 짚을 수 있었다.
자동 조정 알고리즘에는 한계가 있어야 한다. Cilium 의 주기 자동 조정은 오래 연결이 적다가 파드 하나로 연결이 크게 늘어나는 노드나, 5% 가 아주 큰 수가 되는 큰 테이블을 고려하지 않았다. 최대 12시간이라는 주기도 이 작업에는 너무 길었다. 타임아웃은 호출한 쪽만 보호할 뿐 작업을 멈추지 않는다. 필요하면 작업을 취소하는 처리를 명시적으로 넣어야 한다. 연결 추적 상태는 핵심 운영 지표로 다뤄야 하고, 한가한 시간의 CPU 절약을 위해 바쁜 시간의 파드 생성 타임아웃을 감수해서는 안 된다.
출처
관련 사례
같은 원인 · 공통 Cilium
내 클러스터에도 있는지 확인하기
AI 도구와 스크립트용
- 이 사례의 JSON: https://incidentlib-api.benchlet.com/cases/IL-0246
- 가장 비슷한 사례들: https://incidentlib-api.benchlet.com/cases/IL-0246/similar
- MCP 서버(Streamable HTTP, 키 없음):
https://incidentlib-mcp.benchlet.com/mcp
API 는 키가 필요 없습니다. MCP 서버는 search_incidents, get_incident, list_incidents, get_topic, vocab 도구를 내놓습니다. 사용법은 API 와 MCP.