eBPF로 쿠버네티스 네트워크 관찰하기: Cilium과 Hubble로 트래픽을 커널 수준에서 들여다보는 법

커널 수준에서 패킷을 들여다보는 것이 왜 중요한가
쿠버네티스 클러스터를 운영하다 보면 "이 파드 간 통신이 왜 실패하는가"라는 질문 앞에서 막막함을 느낄 때가 있습니다. kubectl logs에는 아무런 에러가 없고, kubectl describe pod를 봐도 이상이 없습니다. 그런데 A 서비스가 B 서비스를 호출하면 간헐적으로 연결이 끊깁니다. 우리 팀은 수백 개의 파드가 뜨고 지는 대용량 클러스터에서 정확히 이 상황을 마주했습니다. 결국 원인은 iptables NAT 테이블의 conntrack 엔트리가 고갈되면서 발생한 패킷 드롭이었는데, 이것을 찾아내는 데 기존 모니터링 스택으로는 수 시간이 걸렸습니다.
eBPF 기반의 Cilium과 Hubble이 이 문제를 어떻게 바꾸는지 이 글에서 정리합니다. 쿠버네티스 오토스케일링 전략에 대해서는 HPA·VPA·KEDA 운영 가이드를, OpenTelemetry 기반 관측성 설계에 대해서는 OpenTelemetry 운영 관측성 설계를 함께 참고하시면 좋습니다.
1. eBPF란 무엇인가
eBPF는 "extended Berkeley Packet Filter"의 약자입니다. 공식 사이트 ebpf.io는 eBPF를 다음과 같이 정의합니다. "eBPF is a revolutionary technology with origins in the Linux kernel that can run sandboxed programs in a privileged context such as the operating system kernel."
동작 방식: 사용자 공간에서 C로 작성한 소스를 LLVM/Clang이 eBPF 바이트코드로 컴파일합니다. 이 바이트코드를 bpf() 시스템 콜로 커널에 전달하면, 커널 내 검증기(verifier)가 안전성을 먼저 확인합니다. 검증을 통과한 프로그램은 JIT 컴파일러가 네이티브 기계어로 변환해 해당 훅 포인트에 부착합니다.
검증기가 보장하는 안전성은 세 가지입니다. 프로그램이 무한 루프에 빠지지 않는다는 것, 허용되지 않은 커널 메모리 영역에 접근하지 않는다는 것, 그리고 특정 커널 함수만 호출한다는 것입니다.
2. 기존 iptables 한계와 eBPF의 차이
| 항목 | iptables/kube-proxy | eBPF/Cilium |
|---|---|---|
| 서비스 라우팅 복잡도 | O(n) — 순차 탐색 | O(1) — eBPF map 해시 조회 |
| 네트워크 정책 구현 | iptables 규칙 체인 | eBPF 프로그램 직접 적용 |
| L7 가시성 | 불가 | eBPF 파서로 HTTP/gRPC 헤더 추출 |
| conntrack 의존도 | 높음 | DSR 모드 등에서 conntrack 우회 가능 |
| 규칙 업데이트 방식 | 전체 iptables 체인 재작성 | eBPF map 원자적 업데이트 |
iptables 기반에서 또 다른 문제는 conntrack 테이블입니다. NAT를 위해 모든 연결을 conntrack에 기록하는데, 클러스터 내 파드 간 동서 트래픽이 많으면 conntrack 테이블이 포화될 수 있습니다.
3. eBPF 프로그램 유형
**XDP (eXpress Data Path)**는 네트워크 드라이버 수준에서 동작합니다. 패킷이 커널 네트워크 스택에 진입하기 전에 실행됩니다. DDoS 방어, 부하 분산, 패킷 포워딩에 적합합니다.
**TC (Traffic Control)**는 리눅스 tc 서브시스템의 훅 포인트입니다. 인그레스와 이그레스 양방향을 처리할 수 있고, 패킷 수정이 가능합니다. Cilium의 주요 데이터 패스는 TC 레벨 eBPF 프로그램으로 구현되어 있습니다.
kprobe와 kretprobe는 커널 함수의 진입점과 반환점에 부착합니다.
uprobe와 uretprobe는 사용자 공간 함수에 부착합니다. Hubble의 L7 가시성 일부가 이 방식을 활용합니다.
tracepoint는 커널 개발자가 명시적으로 설치한 안정적인 훅 포인트입니다.
4. Cilium 아키텍처
cilium-agent는 각 노드에 DaemonSet으로 배포되는 핵심 컴포넌트입니다. 쿠버네티스 API 서버를 watch하면서 파드, 서비스, 네트워크 정책 변경을 감지하고, 해당 노드의 eBPF 프로그램과 map을 업데이트합니다.
cilium-operator는 클러스터 전역 작업을 담당합니다. IPAM, CiliumNetworkPolicy CRD 관리, 노드 간 라우팅 정보 동기화가 여기에 속합니다.
Hubble은 Cilium 위에서 동작하는 네트워크 관측성 레이어입니다. cilium-agent의 eBPF 프로그램이 캡처한 이벤트를 실시간으로 집계하며, gRPC 스트리밍으로 제공합니다.
eBPF map은 cilium-agent가 관리하는 커널 내 데이터 구조입니다. cilium map list, cilium map get 명령으로 현재 map 상태를 직접 조회할 수 있습니다.
5. Hubble UI로 서비스 맵 시각화
Hubble은 eBPF 이벤트 스트림에서 HTTP 메서드, URL, 상태 코드, gRPC 메서드명, DNS 쿼리, 패킷 드롭 이유까지 추출해 실시간으로 집계합니다.
# 특정 네임스페이스의 모든 흐름 관찰
hubble observe --namespace production --follow
# HTTP 레이어 필터: 5xx 에러만 추려서 확인
hubble observe \
--namespace production \
--protocol http \
--http-status-code 500,502,503,504 \
--follow
# 특정 파드 간 통신만 필터링
hubble observe \
--from-pod production/order-service \
--to-pod production/payment-service \
--follow
# 패킷 드롭 이유 확인
hubble observe \
--namespace production \
--verdict DROPPED \
--follow \
-o json | jq '.flow | {source: .source.pod_name, destination: .destination.pod_name, drop_reason: .drop_reason_desc}'
drop_reason_desc 필드는 eBPF 프로그램이 패킷을 드롭한 이유를 문자열로 반환합니다. POLICY_DENIED는 네트워크 정책에 의한 차단, CT_MISSING_ENTRY는 conntrack 엔트리 없음 등 구체적인 이유를 확인할 수 있습니다.
6. 네트워크 정책 강화
쿠버네티스의 표준 NetworkPolicy는 L3/L4 레벨에서 인그레스와 이그레스를 제어합니다. Cilium은 CiliumNetworkPolicy CRD를 통해 L7 레벨 정책까지 확장합니다. (Cilium Security Policy)
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: payment-service-l7-policy
namespace: production
spec:
endpointSelector:
matchLabels:
app: payment-service
ingress:
- fromEndpoints:
- matchLabels:
app: order-service
toPorts:
- ports:
- port: "8080"
protocol: TCP
rules:
http:
- method: "POST"
path: "/v1/charge"
- method: "GET"
path: "/v1/charge/[0-9]+"
- fromEndpoints:
- matchLabels:
app: billing-service
toPorts:
- ports:
- port: "8080"
protocol: TCP
rules:
http:
- method: "GET"
path: "/v1/invoice"
이 정책이 적용되면 order-service에서 payment-service의 /v1/admin으로 접근 시도가 발생하면 Hubble에 즉시 POLICY_DENIED 이벤트로 기록됩니다.
7. L7 가시성: HTTP·gRPC 메타데이터
eBPF만으로는 패킷 내용을 파싱하기 어렵습니다. Cilium은 이를 위해 파드의 트래픽을 인라인 Envoy 프록시를 통과시키는 방식을 선택합니다.
| 필드 | HTTP | gRPC |
|---|---|---|
| 메서드 | GET, POST, PUT, DELETE | 서비스명/메서드명 |
| URL / 경로 | 전체 경로 | /package.Service/Method |
| 상태 코드 | 200, 404, 500 | gRPC status code |
| 응답 시간 | 요청/응답 쌍 매칭 | 요청/응답 쌍 매칭 |
이 메타데이터가 있으면 외부 서비스 메시(Istio, Linkerd)를 도입하지 않고도 서비스 간 L7 RED 지표(Rate, Error, Duration)를 Prometheus에서 볼 수 있습니다.
8. Cilium 설치와 kube-proxy 대체
helm repo add cilium https://helm.cilium.io/
helm repo update
helm install cilium cilium/cilium \
--version 1.15.4 \
--namespace kube-system \
--set kubeProxyReplacement=true \
--set k8sServiceHost="${API_SERVER_HOST}" \
--set k8sServicePort="${API_SERVER_PORT}" \
--set hubble.relay.enabled=true \
--set hubble.ui.enabled=true \
--set hubble.metrics.enabled="{dns,drop,tcp,flow,port-distribution,icmp,http}" \
--set prometheus.enabled=true \
--set operator.prometheus.enabled=true \
--set bpf.masquerade=true
cilium status --wait
kubectl -n kube-system delete daemonset kube-proxy
kubectl -n kube-system delete configmap kube-proxy
cilium hubble port-forward &
hubble status
hubble observe --follow
kubeProxyReplacement=true 설정이 핵심입니다. 이 옵션이 활성화되면 Cilium이 kube-proxy의 역할인 ClusterIP, NodePort, LoadBalancer 서비스 라우팅을 모두 eBPF로 처리합니다.
9. 퍼포먼스 특성과 벤치마크 관점
서비스 수가 증가할 때 kube-proxy(iptables 모드)와 Cilium의 차이가 가장 두드러집니다. 서비스 1만 개 규모에서 iptables는 규칙 업데이트마다 수 초가 걸리는 반면, Cilium의 eBPF map 업데이트는 원자적이며 밀리초 수준에서 완료됩니다.
L7 가시성 활성화 시에는 추가 오버헤드를 고려해야 합니다. L7 정책이 적용된 파드는 Envoy 프록시가 인라인으로 삽입되므로 추가 CPU와 메모리가 소요됩니다.
10. 제한사항과 커널 버전 요구사항
| 기능 | 최소 커널 버전 |
|---|---|
| 기본 eBPF 데이터 패스 | 4.19 |
| BPF Host Routing | 5.10 |
| eBPF Socket LB (kube-proxy 완전 대체) | 5.8 |
| Bandwidth Manager | 5.1 |
| WireGuard 투명 암호화 | 5.6 |
AWS EKS, GKE, AKS 등 관리형 쿠버네티스 서비스는 기본 노드 AMI가 커널 5.10 이상을 사용하는 경우가 많아 대부분의 기능을 사용할 수 있습니다.
Hubble의 이벤트 버퍼 오버플로우도 실무에서 마주칩니다. 트래픽이 매우 많은 클러스터에서는 버퍼가 빠르게 채워져 이벤트가 드롭될 수 있습니다.
결론
- 커널 버전 확인: 모든 노드에서
uname -r로 커널 버전을 확인하고, 사용하려는 Cilium 기능에 필요한 최소 버전을 충족하는지 검증한다. - 단계적 전환: kube-proxy를 즉시 제거하지 않고, Cilium을 먼저 배포해 기존 네트워크 정책이 정상 동작하는지 확인합니다.
- Hubble 관측성 우선 활용: L7 정책 집행 전에 먼저 가시성 모드로 Hubble을 운영합니다.
- L7 가시성 범위 제한: 가시성이 필요한 핵심 서비스부터 우선 적용합니다.
- eBPF map 상태 모니터링:
cilium map list와 Prometheus 메트릭으로 map 업데이트 오류와 용량 한계를 확인합니다.