Skip to content
Published on

Cilium 데이터패스 아키텍처 — kube-proxy 없는 클러스터의 내부

Authors

들어가며

쿠버네티스 클러스터에서 서비스 요청 하나가 파드에 도달하기까지, 전통적인 구성에서는 iptables 체인 수천 개를 순회합니다. 서비스가 5천 개를 넘는 클러스터라면 규칙 갱신 한 번에 수 초가 걸리고, 커널은 매 패킷마다 선형 탐색에 가까운 비용을 지불합니다. Cilium은 이 경로를 eBPF 프로그램과 해시 맵으로 대체해, 서비스 수와 무관하게 O(1)에 가까운 룩업으로 패킷을 처리합니다.

Cilium은 2023년 10월 CNCF 졸업(Graduated) 프로젝트가 되었고, 이후 관리형 쿠버네티스 진영의 표준 CNI로 빠르게 자리 잡았습니다. GKE Dataplane V2는 Cilium 기반이고, AKS의 Azure CNI Powered by Cilium, EKS에서도 Cilium을 직접 설치해 kube-proxy를 제거하는 구성이 흔해졌습니다. 이 글에서는 Cilium의 eBPF 데이터패스를 패킷의 여정을 따라 해부하고, kube-proxy replacement·identity 보안 모델·라우팅 모드·IPAM·설치와 검증·트러블슈팅까지 운영자가 알아야 할 내부를 정리합니다.

Cilium의 위치 — CNI 그 이상

Cilium은 표면적으로는 CNI 플러그인이지만, 실제로 담당하는 영역은 훨씬 넓습니다.

영역기능대체 대상
파드 네트워킹IP 할당, 파드 간 라우팅기존 CNI(flannel, calico 등)
서비스 로드밸런싱ClusterIP, NodePort, LoadBalancerkube-proxy
네트워크 정책L3/L4/L7 정책, DNS 정책NetworkPolicy + 별도 프록시
관측성플로우 가시성(Hubble)별도 모니터링 에이전트
멀티클러스터ClusterMesh서비스 메시 일부 기능
암호화WireGuard, IPsec별도 오버레이 솔루션

이 모든 기능의 공통 기반이 eBPF입니다. eBPF는 커널을 재컴파일하거나 모듈을 적재하지 않고도, 검증기(verifier)를 통과한 프로그램을 커널 내 훅 포인트에서 실행할 수 있게 해주는 기술입니다. 네트워킹 관점에서 중요한 훅은 XDP(드라이버 수준)와 tc(트래픽 컨트롤, L2/L3 진입·진출 지점)입니다.

eBPF 데이터패스 해부 — 패킷의 여정

외부에서 NodePort로 들어온 패킷이 파드에 도달하기까지의 여정을 따라가 보겠습니다.

[외부 클라이언트]
      |
      v
+---------------------------------------------------------------+
| 노드 (Linux 커널)                                              |
|                                                               |
|  NIC 수신                                                     |
|   |                                                           |
|   v                                                           |
|  [XDP 훅] ──────── bpf_xdp.o                                  |
|   |  - NodePort 가속, DDoS 필터링, LB (드라이버 레벨)           |
|   |  - 여기서 DROP/TX(되반사)/PASS 결정                        |
|   v                                                           |
|  [tc ingress 훅] ── bpf_host.o (cil_from_netdev)              |
|   |  - 서비스 변환: ClusterIP/NodePort -> 백엔드 파드 IP        |
|   |    (cilium_lb4_services_v2, cilium_lb4_backends 맵 룩업)   |
|   |  - conntrack 기록 (cilium_ct4_global 맵)                  |
|   |  - ipcache 룩업: 목적지 IP -> identity                     |
|   v                                                           |
|  라우팅/리다이렉트 (bpf_redirect_peer 로 veth 건너뜀)            |
|   |                                                           |
|   v                                                           |
|  [파드 lxc 인터페이스] ── bpf_lxc.o (cil_to_container)          |
|   |  - 정책 평가: src identity + dst port/proto 매칭           |
|   |    (cilium_policy_v2 per-endpoint 맵)                     |
|   v                                                           |
|  [파드 네트워크 네임스페이스 -> 애플리케이션 소켓]                |
+---------------------------------------------------------------+

핵심 포인트를 짚어 보면:

  1. XDP는 NIC 드라이버가 패킷을 sk_buff로 변환하기 전에 실행됩니다. 메모리 할당 전 단계라 가장 빠르고, Cilium은 NodePort/LoadBalancer 가속(loadBalancer.acceleration=native)과 L4LB 독립 모드에서 활용합니다.
  2. tc 훅이 Cilium 데이터패스의 본체입니다. 서비스 변환, conntrack, 정책 평가, 터널 캡슐화가 모두 여기서 일어납니다.
  3. bpf_redirect_peer는 호스트 측 veth에서 파드 네임스페이스 안의 피어 디바이스로 패킷을 직접 전달해, 소프트IRQ 재스케줄링 없이 네임스페이스 경계를 한 번에 넘습니다.
  4. 정책 평가는 IP가 아니라 identity(뒤에서 설명)를 기준으로 일어납니다.

iptables 경로와의 비교

같은 NodePort 요청이 kube-proxy(iptables 모드) 클러스터에서 처리되는 경로입니다.

NIC -> netfilter PREROUTING
        -> KUBE-SERVICES 체인 (서비스 수만큼 규칙 선형 순회)
        -> KUBE-SVC-XXXX (확률 기반 DNAT 분기, 백엔드 수만큼 규칙)
        -> KUBE-SEP-XXXX (DNAT 실행)
      -> conntrack (nf_conntrack 테이블)
      -> FORWARD 체인 -> CNI 브리지/라우팅 -> veth -> 파드
항목iptables (kube-proxy)eBPF (Cilium)
서비스 룩업규칙 체인 선형 순회해시 맵 O(1)
규칙 갱신전체 테이블 재작성(서비스 많을수록 느림)맵 엔트리 단위 증분 갱신
백엔드 선택statistic 모듈 확률 분기랜덤 또는 Maglev 일관 해싱
conntracknf_conntrack(전역, 락 경합)BPF 맵 기반 자체 CT
정책 표현IP/CIDR 기반identity 기반(라벨 의미 보존)
L7 인지불가Envoy 연동으로 가능

서비스가 수백 개 수준이면 체감 차이가 작지만, 수천 개 이상으로 가면 iptables의 갱신 지연과 첫 패킷 레이턴시가 운영 문제로 드러납니다. eBPF 데이터패스는 이 두 축(룩업 비용, 갱신 비용)을 모두 상수 시간으로 만듭니다.

kube-proxy replacement의 원리

Cilium의 kube-proxy replacement(KPR)는 쿠버네티스 서비스 추상화를 eBPF 맵 두 계층으로 구현합니다.

서비스 룩업 흐름 (단순화)

  목적지 10.96.0.10:443 (ClusterIP)
        |
        v
  cilium_lb4_services_v2  맵
    key:   (IP, port, scope)
    value: (backend count, rev_nat index, flags)
        |
        v
  cilium_lb4_backends_v3  맵
    key:   backend id
    value: (pod IP, port, state)
        |
        v
  DNAT 수행 + cilium_lb4_reverse_nat 에 역변환 기록

에이전트는 쿠버네티스 API에서 Service/EndpointSlice를 watch하고, 변경분만 맵에 반영합니다. 데이터 평면은 커널 안에서 닫히므로, 에이전트가 잠시 죽어도 기존 연결과 기존 서비스 변환은 계속 동작합니다.

DSR과 Maglev

NodePort 트래픽이 백엔드가 없는 노드로 들어오면 다른 노드로 한 번 더 홉이 발생합니다. 기본 SNAT 모드에서는 응답이 다시 진입 노드를 거쳐 돌아가지만, DSR(Direct Server Return) 모드에서는 백엔드 파드가 클라이언트에게 직접 응답해 응답 경로의 홉과 대역폭 소모를 줄이고 클라이언트 소스 IP도 보존됩니다.

Maglev 일관 해싱은 구글이 발표한 L4 로드밸런서 알고리즘으로, 백엔드 증감 시에도 기존 플로우 대부분이 같은 백엔드에 매핑되도록 합니다. 여러 노드가 ECMP로 같은 VIP 트래픽을 받는 구성에서 노드 간 백엔드 선택이 일관되어, 노드 장애 시 연결 깨짐을 최소화합니다.

# helm values - KPR + DSR + Maglev 예시
kubeProxyReplacement: true
loadBalancer:
  mode: dsr
  algorithm: maglev
  acceleration: native     # XDP 가속 (지원 NIC 한정)
maglev:
  tableSize: 16381         # 백엔드 수 x 100 보다 큰 소수 권장

DSR은 네이티브 라우팅 모드에서 가장 자연스럽게 동작하며, 터널 모드와 조합할 때는 Geneve DSR 같은 별도 옵션이 필요하다는 점을 기억해 두시기 바랍니다.

identity 기반 보안 모델

Cilium 보안 모델의 핵심은 "IP가 아닌 identity로 판단한다"입니다.

파드 라벨                          identity (숫자)
---------------------------       ----------------
app=frontend, env=prod      --->  identity 51234
app=backend,  env=prod      --->  identity 60917
(예약) host                 --->  1
(예약) world                --->  2
(예약) remote-node          --->  6

ipcache 맵: IP/CIDR -> identity
  10.0.1.23/32 -> 51234
  10.0.2.40/32 -> 60917
  0.0.0.0/0    -> 2 (world)

동작 순서는 다음과 같습니다.

  1. 파드가 생성되면 에이전트가 보안 관련 라벨 집합을 추출하고, 동일 라벨 집합에 동일한 숫자 identity를 할당합니다(kvstore 또는 CRD 기반 전역 합의).
  2. 모든 노드의 cilium_ipcache 맵에 해당 파드 IP와 identity 매핑이 전파됩니다.
  3. 패킷 송신 시 소스 identity가 메타데이터(터널 헤더 또는 패킷 마크)로 운반되거나, 수신 측에서 ipcache 룩업으로 복원됩니다.
  4. 수신 엔드포인트의 정책 맵은 "identity 51234가 TCP 8080으로 접근 가능한가"를 O(1)로 판정합니다.

이 모델의 장점은 정책 의미가 IP 변동과 무관하게 유지된다는 것입니다. 파드가 재스케줄되어 IP가 바뀌어도 라벨이 같으면 identity가 같고, 정책 맵은 수정할 필요가 없습니다. ipcache 한 곳만 갱신되면 됩니다.

# identity와 ipcache 직접 확인
kubectl -n kube-system exec ds/cilium -- cilium identity list
kubectl -n kube-system exec ds/cilium -- cilium bpf ipcache list
kubectl -n kube-system exec ds/cilium -- cilium endpoint list

터널링 vs 네이티브 라우팅

파드 간 패킷이 노드 경계를 넘는 방법은 크게 두 가지입니다.

터널 모드 (VXLAN/Geneve)                네이티브 라우팅
+--------+   캡슐화   +--------+        +--------+  일반 IP  +--------+
| node A | =========> | node B |        | node A | --------> | node B |
+--------+ UDP 8472   +--------+        +--------+ 라우터/BGP +--------+
 파드 패킷을 외부 헤더로 감쌈              네트워크가 PodCIDR 경로를 알아야 함
기준VXLANGeneve네이티브 라우팅(BGP/클라우드)
네트워크 요구사항노드 간 UDP 8472 허용만노드 간 UDP 6081 허용만언더레이가 PodCIDR 라우팅 가능해야
오버헤드약 50바이트 캡슐화약 50바이트 이상(가변 옵션)없음
MTU 영향있음(MTU 축소 필요)있음없음
DSR 친화성제한적DSR 옵션 운반에 유리가장 자연스러움
운영 난이도낮음낮음BGP 또는 클라우드 라우팅 이해 필요
적합 환경언더레이 통제 불가 환경메타데이터 확장이 필요한 경우온프레미스 BGP, ENI 등 클라우드 네이티브

선택 기준을 거칠게 요약하면:

  • 언더레이 네트워크를 통제할 수 없다면(사내 공용망, 단순 L3 환경) → 터널 모드로 시작
  • 온프레미스에서 ToR 스위치와 BGP 피어링이 가능하다면 → 네이티브 라우팅 + BGP Control Plane
  • AWS/Azure에서 ENI/Azure IPAM을 쓴다면 → 네이티브 라우팅이 기본 전제
# 네이티브 라우팅 + BGP 예시 (helm values)
routingMode: native
ipv4NativeRoutingCIDR: 10.0.0.0/8
autoDirectNodeRoutes: true     # 같은 L2일 때 노드 간 직접 경로 설치
bgpControlPlane:
  enabled: true                # CiliumBGPPeeringPolicy/ClusterConfig 사용

IPAM 모드

파드 IP를 어디서 어떻게 할당하느냐도 데이터패스 설계의 일부입니다.

IPAM 모드할당 주체특징
cluster-pool (기본)Cilium operatorCiliumNode CRD로 노드별 PodCIDR 분배, 유연한 풀 크기
kuberneteskube-controller-managerNode.spec.podCIDR 사용, 기존 클러스터 관행과 호환
eniAWS ENI파드에 VPC 네이티브 IP 부여, 라우팅 모드는 native
azureAzure IPAMAzure CNI Powered by Cilium의 기반
multi-poolCilium operator네임스페이스/노드별로 다른 풀 사용 가능

cluster-pool 모드에서 풀 크기를 처음에 작게 잡으면 노드 증설 시 CIDR 고갈로 파드 스케줄링이 막힐 수 있으니, clusterPoolIPv4PodCIDRList는 미래 노드 수까지 감안해 넉넉히 잡아야 합니다.

설치와 업그레이드 실전

커널 요구사항

기능최소 커널
기본 데이터패스4.19 이상(실질 권장 5.4 이상)
WireGuard 암호화5.6 이상
XDP 가속NIC 드라이버 지원 + 5.x 권장
BIG TCP, netkit 등 최신 기능6.x 계열

2026년 현재 주요 배포판(RHEL 9, Ubuntu 22.04/24.04)은 모두 5.14 이상이므로 일반적으로 문제가 없지만, 오래된 온프레미스 노드는 반드시 uname -r로 확인해야 합니다.

helm 설치 예시

helm repo add cilium https://helm.cilium.io/
helm repo update

# kube-proxy 없이 클러스터를 만들었거나 제거한 상태를 가정
helm install cilium cilium/cilium \
  --version 1.18.5 \
  --namespace kube-system \
  -f values.yaml
# values.yaml — kube-proxy 대체 + Hubble 활성화 기본형
kubeProxyReplacement: true
k8sServiceHost: api.mycluster.internal   # KPR 시 API 서버 직접 지정 필수
k8sServicePort: 6443

routingMode: tunnel
tunnelProtocol: vxlan

ipam:
  mode: cluster-pool
  operator:
    clusterPoolIPv4PodCIDRList:
      - 10.128.0.0/12
    clusterPoolIPv4MaskSize: 24

hubble:
  enabled: true
  relay:
    enabled: true
  ui:
    enabled: true

operator:
  replicas: 2

prometheus:
  enabled: true

k8sServiceHost를 지정하는 이유가 중요합니다. kube-proxy가 없으면 ClusterIP 변환을 Cilium이 담당하는데, Cilium 에이전트 자신이 뜨기 전에는 kubernetes.default ClusterIP를 풀 수 없습니다. 닭과 달걀 문제를 피하려고 API 서버 실주소를 직접 알려주는 것입니다.

cilium-cli 활용

# 설치 상태 종합 점검
cilium status --wait

# 클러스터 전반 연결성 테스트 (테스트 파드 자동 배포)
cilium connectivity test

# 설정 확인
cilium config view | grep -i kube-proxy

업그레이드 절차

# 1) 릴리스 노트의 업그레이드 가이드 필수 확인 (마이너 버전 건너뛰기 금지)
# 2) pre-flight 검사로 새 이미지 사전 풀링 및 CRD 호환 확인
helm install cilium-preflight cilium/cilium --version 1.18.5 \
  --namespace kube-system \
  --set preflight.enabled=true \
  --set agent=false --set operator.enabled=false

# 3) preflight 정상 확인 후 본 업그레이드
helm upgrade cilium cilium/cilium --version 1.18.5 \
  --namespace kube-system -f values.yaml

# 4) 롤링 재시작 상태 관찰
kubectl -n kube-system rollout status ds/cilium

업그레이드 시 에이전트 파드가 재시작되어도 eBPF 프로그램과 맵은 커널에 남아 있으므로 기존 트래픽은 끊기지 않는 것이 정상입니다. 다만 맵 레이아웃이 바뀌는 메이저 변경에서는 일시적 재생성이 일어날 수 있어 릴리스 노트 확인이 필수입니다.

kube-proxy 대체 검증

KPR이 실제로 동작하는지 반드시 눈으로 확인해야 합니다.

# 1) KPR 모드 확인 - "True" 표시 확인
kubectl -n kube-system exec ds/cilium -- cilium status --verbose | grep -A3 KubeProxyReplacement

# 2) 서비스가 eBPF 맵에 들어왔는지 확인
kubectl -n kube-system exec ds/cilium -- cilium service list
kubectl -n kube-system exec ds/cilium -- cilium bpf lb list

# 3) kube-proxy가 정말 없는지, iptables 흔적 확인
kubectl -n kube-system get ds kube-proxy 2>&1 || echo "kube-proxy 없음 - 정상"
iptables-save | grep -c KUBE-SVC || echo "KUBE-SVC 체인 없음 - 정상"

# 4) 실제 연결 테스트
kubectl run probe --image=curlimages/curl --rm -it --restart=Never -- \
  curl -s -o /dev/null -w "%{http_code}\n" http://my-service.default.svc.cluster.local

특히 기존 클러스터에서 kube-proxy를 제거하며 전환하는 경우, 남아 있는 iptables 규칙이 eBPF 경로와 충돌할 수 있으므로 kube-proxy DaemonSet 삭제 후 노드의 잔여 KUBE-* 체인을 정리(노드 재부팅 또는 iptables 플러시)하는 절차를 계획에 포함해야 합니다.

성능 관점 — 왜 빨라지는가

벤치마크 수치는 환경 의존성이 크므로, 구조적으로 어디서 이득이 나는지를 이해하는 것이 중요합니다.

  1. 룩업 복잡도: iptables는 규칙 수에 비례한 순회, eBPF는 해시 맵 상수 시간. 서비스/정책 수가 늘수록 격차가 커집니다.
  2. 경로 길이 단축: bpf_redirect_peer로 veth 쌍 통과 시 소프트IRQ 한 사이클을 절약하고, 호스트 라우팅 모드에서는 상위 netfilter 스택 진입 자체를 건너뜁니다.
  3. 갱신 비용: 배포가 잦은 클러스터에서 iptables 전체 재작성은 CPU 스파이크와 규칙 적용 지연을 만들지만, 맵 증분 갱신은 거의 무비용입니다.
  4. XDP: 드라이버 수준 처리로 sk_buff 할당 전에 LB/드롭을 결정해, NodePort 패킷 처리량을 큰 폭으로 끌어올립니다.

반대로 비용도 있습니다. L7 정책을 켜면 해당 트래픽은 사용자 공간 Envoy를 경유하므로 레이턴시가 추가되고, 터널 모드는 캡슐화 오버헤드와 MTU 축소를 동반합니다. "전부 빨라진다"가 아니라 "어떤 기능을 켜면 어떤 비용이 드는지"를 파악하고 선택하는 것이 운영의 핵심입니다.

트러블슈팅 기초

# 실시간 데이터패스 이벤트 관찰 (드롭 사유 포함)
kubectl -n kube-system exec ds/cilium -- cilium monitor --type drop
kubectl -n kube-system exec ds/cilium -- cilium monitor --type policy-verdict

# 특정 엔드포인트 상세 (정책 상태, identity 확인)
kubectl -n kube-system exec ds/cilium -- cilium endpoint list
kubectl -n kube-system exec ds/cilium -- cilium endpoint get 1234

# conntrack/NAT 맵 직접 조회
kubectl -n kube-system exec ds/cilium -- cilium bpf ct list global | head
kubectl -n kube-system exec ds/cilium -- cilium bpf nat list | head

# 종합 진단 번들 수집 (이슈 리포트 첨부용)
cilium sysdump
# 또는 노드 단위로
kubectl -n kube-system exec ds/cilium -- cilium-bugtool

cilium monitor의 드롭 사유 코드가 1차 단서입니다. 자주 보는 사유는 다음과 같습니다.

드롭 사유흔한 원인
Policy denied정책 미허용 — identity와 포트 재확인
CT: Map insertion failedconntrack 맵 가득 참 — bpf-ct-global-tcp-max 조정
Unsupported L3 protocol비IP 트래픽 — 의도 여부 확인
Stale or unroutable IPipcache 불일치 — 에이전트/노드 간 동기화 점검
Missed tail call프로그램 로드 불일치 — 에이전트 재시작, 버전 혼재 확인

운영 주의점

  • 버전 호환: Cilium 마이너 버전은 순차 업그레이드만 지원합니다. 1.16 → 1.18처럼 건너뛰면 안 됩니다. 쿠버네티스 버전 지원 매트릭스도 함께 확인하세요.
  • 정책 마이그레이션: Calico 등에서 이관할 때 기존 NetworkPolicy는 대부분 그대로 동작하지만, identity 모델 차이로 동작이 미묘하게 달라질 수 있는 부분(특히 ipBlock과 노드 IP 취급)을 스테이징에서 먼저 검증해야 합니다.
  • 시스템 예약 identity: host, remote-node, world, kube-apiserver 같은 예약 identity의 의미를 이해하지 못하면 노드 발신 트래픽이나 헬스체크가 정책에 막히는 사고가 납니다.
  • 리소스 한도: 대규모 클러스터에서는 BPF 맵 크기(ct, ipcache, lb 맵)의 기본값을 점검하고, 에이전트 메모리 요청을 그에 맞게 조정해야 합니다.
  • 관리형 환경 제약: GKE Dataplane V2, AKS Cilium 모드에서는 helm 값 일부가 클라우드 측에서 고정됩니다. 직접 설치와 같은 자유도를 기대하면 안 됩니다.

도입 체크리스트

  • 노드 커널 버전이 5.4 이상인가(WireGuard 필요 시 5.6 이상)
  • 라우팅 모드 결정(터널 vs 네이티브)과 그 근거를 문서화했는가
  • PodCIDR 풀이 향후 3년 노드 증설을 감당하는가
  • kubeProxyReplacement 시 k8sServiceHost/Port를 지정했는가
  • 터널 모드라면 노드 간 UDP 8472(VXLAN) 또는 6081(Geneve)이 열려 있는가
  • MTU 계산(캡슐화/암호화 오버헤드 반영)을 마쳤는가
  • cilium connectivity test가 전 항목 통과하는가
  • kube-proxy 제거 후 잔여 iptables 체인 정리 절차가 있는가
  • cilium status, BPF 맵 사용률, 드롭 카운트를 모니터링에 연결했는가
  • 업그레이드 시 preflight 절차가 런북에 포함되어 있는가

마치며

Cilium의 본질은 "쿠버네티스 네트워킹의 의미론(서비스, 라벨, 정책)을 커널 안 자료구조로 직접 내려보낸 것"입니다. iptables 시대에는 라벨이라는 의미가 IP 규칙으로 번역되며 소실됐지만, eBPF 데이터패스에서는 identity라는 형태로 커널까지 보존됩니다. 이 구조를 이해하면 kube-proxy replacement, 정책 평가, 트러블슈팅이 모두 하나의 그림으로 연결됩니다. 다음 글에서는 이 위에 올라가는 네트워크 정책(L3~L7, DNS)을 실전 YAML 중심으로 다루겠습니다.

참고 자료