LabHub

CCA — 실리움 인증 어소시에이트 · eBPF 와 Cilium 아키텍처 · 이론

패킷은 어디서 붙잡히고 어디로 튕겨 나가나

LabHub 에서 이어서 보기

한 줄 요약

Cilium 은 쿠버네티스의 의미론(서비스, 라벨, 정책)을 커널 안 자료구조로 직접 내려보냅니다. 그래서 서비스가 늘어도 룩업 비용이 늘지 않지만, 전부 빨라지는 것은 아닙니다.

왜 이게 필요했나

kube-proxy 의 iptables 모드에서 서비스 요청 하나는 KUBE-SERVICES 체인을 훑고, KUBE-SVC-* 에서 확률 분기를 거쳐, KUBE-SEP-* 에서 DNAT 됩니다. 규칙 수는 서비스 수와 백엔드 수에 비례해 늘어나고, 커널은 매 패킷마다 그 목록을 순회에 가깝게 지나갑니다.

더 아픈 쪽은 갱신 비용입니다. iptables 는 규칙 하나를 바꿀 때도 사실상 테이블 전체를 다시 씁니다. 배포가 잦은 클러스터에서 이것은 CPU 스파이크와 규칙 반영 지연으로 나타납니다. 서비스가 수천 개인 환경에서 "파드는 떴는데 아직 트래픽이 안 온다"는 몇 초짜리 공백이 여기서 나옵니다.

eBPF 는 두 축을 모두 상수 시간으로 바꿉니다. 룩업은 해시맵 조회라 서비스 수와 무관하고, 갱신은 맵 엔트리 단위 증분이라 다른 엔트리를 건드리지 않습니다.

어떻게 동작하나

외부에서 NodePort 로 들어온 패킷의 여정입니다.

NIC 수신  -> [XDP 훅]            드라이버 수준. sk_buff 할당 전이라 가장 빠름  -> [tc ingress 훅]     서비스 변환(lb4_services -> lb4_backends), conntrack,                         ipcache 룩업으로 목적지 아이덴티티 확인  -> bpf_redirect_peer   호스트 veth 에서 파드 네임스페이스 안 피어로 직행  -> [파드 lxc 인터페이스] 정책 평가: (소스 아이덴티티, 포트, 프로토콜, 방향)  -> 애플리케이션 소켓

세 가지를 짚어야 합니다. 첫째, XDP 는 메모리 할당 전 단계라 DDoS 필터링과 NodePort 가속에 쓰이지만 지원 NIC 드라이버가 필요합니다. 둘째, 데이터패스의 본체는 tc 훅이고 서비스 변환·conntrack·정책 평가가 전부 여기서 일어납니다. 셋째, bpf_redirect_peer 는 veth 쌍을 통과할 때 생기는 소프트IRQ 재스케줄링 한 사이클을 건너뛰어 네임스페이스 경계를 한 번에 넘습니다.

정책은 IP 가 아니라 아이덴티티로 평가됩니다. 파드 라벨 집합에 숫자 하나가 대응되고, 그 숫자가 커널 맵의 키가 됩니다. 시험에 그대로 나오는 예약 아이덴티티는 외워 두는 편이 낫습니다.

| 번호 | 이름 | 의미 |
| --- | --- | --- |
| 0 | unknown | 알 수 없는 소스 |
| 1 | host | 로컬 노드 자신 |
| 2 | world | 클러스터 외부 전부 |
| 3 | unmanaged | Cilium 이 관리하지 않는 엔드포인트 |
| 4 | health | 헬스 체크 엔드포인트 |
| 5 | init | 초기화 중인 엔드포인트 |
| 6 | remote-node | 다른 노드들 |
| 7 | kube-apiserver | API 서버 |
| 8 | ingress | 인그레스 |

역할 분담도 정리해 둡니다. Cilium Agent 는 DaemonSet 으로 노드마다 떠서 eBPF 프로그램 로드, 엔드포인트 관리, 정책 적용, conntrack 을 맡습니다. Cilium Operator 는 클러스터 하나에 떠서 클러스터 범위 IPAM, CRD 가비지 컬렉션, 노드 디스커버리를 맡습니다. Agent 가 노드 데이터패스를 직접 만지므로 DaemonSet 이어야 하는 것이고, Operator 는 클러스터 전역 상태를 다루므로 하나면 됩니다.

마지막으로 정직한 반대급부입니다. eBPF 로 바꾸면 전부 빨라진다는 말은 사실이 아닙니다.

"어떤 기능을 켜면 어떤 비용이 드는가"를 아는 것이 운영의 핵심입니다.

현장에서 만나는 모습

저자는 7노드 홈랩(cp-1/2/3 + gpu-a/b/c/d)을 쿠버네티스 v1.34.10, containerd 1.7.27, 커널 6.14, Cilium 1.20.1 로 운영합니다. 이 클러스터는 kubeadm init --skip-phases=addon/kube-proxykube-proxy 를 처음부터 설치하지 않고 세웠습니다. 설치 후 삭제와는 다릅니다. 한 번이라도 돈 kube-proxy 는 노드에 KUBE-SERVICES / KUBE-SVC-* / KUBE-SEP-* 체인을 새겨 놓고, DaemonSet 을 지워도 그 규칙은 남아 eBPF 데이터패스와 충돌합니다.

주장이 아니라 측정으로 확인한 결과입니다.

kube-proxy 파드 수: 0노드의 iptables KUBE- 체인 개수: 0

그리고 서비스는 커널 맵 안에 이렇게 들어 있었습니다.

SERVICE ADDRESS             BACKEND ADDRESS (REVNAT_ID) (SLOT)10.96.0.10:53/TCP (1)       10.244.0.150:53/TCP (4) (1)10.96.0.1:443/TCP (1)       10.0.0.120:6443/TCP (1) (1)

cilium statusKubeProxyReplacement: True, Modules Health: OK 92 / Degraded 0 을 보고했고, 노드 상태·컨트롤 플레인·CNI·CoreDNS·스케줄링·서비스 DNS 를 포함한 기본 검증 14개 항목이 14/14 통과했습니다. 이 숫자들이 중요한 이유는 단순합니다. eBPF 데이터패스가 동작한다는 증거는 문서가 아니라 체인 개수와 맵 덤프입니다.

다음 퀴즈에서 확인할 것

이 모듈은 개념만 다룹니다. 다음 모듈에서 아이덴티티 모델과 kube-proxy 대체를 다루면서, 위에서 본 값들을 Helm values 파일로 직접 써 보고 그 상태를 검증하는 스크립트까지 만듭니다.