- Authors

- Name
- Youngju Kim
- @fjvbn20031
Cilium eBPF 데이터패스 심층 분석: 패킷 처리 파이프라인
개요
Cilium의 eBPF 데이터패스는 리눅스 커널 내에서 패킷을 처리하는 고성능 네트워크 파이프라인입니다. 이 글에서는 패킷이 Pod에서 송수신될 때 거치는 전체 과정을 상세히 분석합니다.
1. 패킷 처리 흐름 개요
1.1 인그레스 패킷 흐름 (외부 -> Pod)
외부 네트워크
|
v
[물리 NIC] eth0
|
v
[XDP 프로그램] (선택적)
- NodePort 가속
- DDoS 방어
- 사전 필터링
|
v
[tc ingress: from-netdev]
- 소스 Identity 조회 (ipcache)
- 터널 디캡슐화 (VXLAN/Geneve)
- NodePort DNAT
|
v
[라우팅 결정]
- 로컬 Pod? -> cilium_host
- 원격 노드? -> 터널 또는 직접 라우팅
|
v
[cilium_host: to-host]
- 호스트 방화벽 정책 확인
|
v
[tc egress: to-container] (lxc*)
- 목적지 Identity 확인
- 인그레스 정책 확인
- L3/L4 필터링
- conntrack 항목 생성/업데이트
- L7 리디렉션 (필요시 Envoy로)
|
v
[Pod 네트워크 네임스페이스]
1.2 이그레스 패킷 흐름 (Pod -> 외부)
[Pod 네트워크 네임스페이스]
|
v
[tc ingress: from-container] (lxc*)
- 소스 엔드포인트 식별
- 이그레스 정책 확인
- 서비스 DNAT (kube-proxy 대체)
- conntrack 룩업/생성
- L7 리디렉션 (필요시)
|
v
[라우팅 결정]
- 같은 노드 Pod? -> 직접 전달
- 원격 Pod? -> 터널 또는 직접 라우팅
- 외부? -> SNAT (masquerade)
|
v
[tc egress: to-netdev] (eth0)
- SNAT 적용
- 터널 캡슐화 (VXLAN/Geneve)
|
v
[물리 NIC] eth0
|
v
외부 네트워크
2. BPF 프로그램 상세
2.1 from-container (Pod 이그레스)
Pod에서 나가는 패킷을 처리하는 첫 번째 BPF 프로그램입니다.
// 개념적 from-container 처리 흐름 (단순화)
// 실제 코드: bpf/bpf_lxc.c - handle_xgress()
int from_container(struct __sk_buff *skb) {
// 1. 패킷 파싱 (L2/L3/L4 헤더)
// 2. 소스 엔드포인트 식별
// 3. 이그레스 정책 확인
// - Identity 기반 L3/L4 정책
// - CIDR 정책
// - L7 정책 -> Envoy 리디렉션
// 4. 서비스 로드 밸런싱
// - ClusterIP DNAT
// - NodePort DNAT
// 5. conntrack 처리
// 6. 라우팅 결정
// - 같은 노드: tail call to-container
// - 원격 노드: 터널 캡슐화 또는 직접 라우팅
// - 외부: SNAT
return TC_ACT_OK; // or TC_ACT_SHOT (drop)
}
주요 처리 단계:
| 단계 | 설명 | BPF 맵 사용 |
|---|---|---|
| 패킷 파싱 | L2/L3/L4 헤더 추출 | - |
| 엔드포인트 식별 | 소스 Pod 확인 | cilium_lxc |
| 정책 확인 | 이그레스 정책 매칭 | cilium_policy |
| 서비스 LB | ClusterIP/NodePort DNAT | cilium_lb4_services |
| conntrack | 연결 상태 추적 | cilium_ct4_global |
| 라우팅 | 목적지 노드 결정 | cilium_ipcache, cilium_tunnel_map |
2.2 to-container (Pod 인그레스)
Pod으로 들어오는 패킷을 처리하는 BPF 프로그램입니다.
// 개념적 to-container 처리 흐름
// 실제 코드: bpf/bpf_lxc.c - handle_ingress()
int to_container(struct __sk_buff *skb) {
// 1. 패킷 파싱
// 2. 소스 Identity 조회
// - ipcache에서 소스 IP -> Identity 매핑
// 3. 인그레스 정책 확인
// - Identity 기반 L3/L4 정책
// - L7 정책 -> Envoy 리디렉션
// 4. conntrack 업데이트
// 5. reverse NAT (응답 패킷의 경우)
// 6. Pod으로 전달
return TC_ACT_OK;
}
2.3 from-overlay / to-overlay
오버레이 네트워크(VXLAN/Geneve)를 통한 패킷 처리입니다.
VXLAN 수신 흐름:
[물리 NIC] -> [VXLAN 디캡슐화] -> [from-overlay BPF]
- 내부 Identity 추출 (Geneve TLV 또는 소스 IP 기반)
- 목적지 엔드포인트 조회
- 정책 확인
- 대상 Pod으로 전달
VXLAN 송신 흐름:
[from-container BPF] -> [라우팅: 원격 노드] -> [to-overlay BPF]
- Identity 정보를 터널 헤더에 포함
- VXLAN/Geneve 캡슐화
- 물리 NIC으로 전달
3. 커넥션 트래킹 구현
3.1 conntrack 구조
Cilium은 자체 eBPF 기반 커넥션 트래킹을 구현합니다.
// conntrack 키 구조 (개념적)
struct ct_key {
__u32 src_ip;
__u32 dst_ip;
__u16 src_port;
__u16 dst_port;
__u8 protocol; // TCP, UDP, ICMP
__u8 direction; // ingress, egress
};
// conntrack 값 구조
struct ct_entry {
__u64 rx_packets;
__u64 rx_bytes;
__u64 tx_packets;
__u64 tx_bytes;
__u32 lifetime;
__u16 rev_nat_index; // reverse NAT 인덱스
__u16 src_sec_id; // 소스 Identity
__u32 flags;
};
3.2 conntrack 상태 머신
TCP 연결 상태 추적:
SYN -> [NEW] -> SYN-ACK -> [ESTABLISHED] -> FIN -> [CLOSING] -> [CLOSED]
상태별 타임아웃:
- NEW: 60초
- ESTABLISHED: 6시간 (TCP), 60초 (UDP)
- CLOSING: 10초
3.3 conntrack 활용
# conntrack 테이블 조회
cilium bpf ct list global
# TCP 연결 예시 출력:
# TCP IN 10.244.1.5:34567 -> 10.96.0.1:443
# Expires: 21590s Identity: 48291
# RxPackets: 142 RxBytes: 15234
# TxPackets: 138 TxBytes: 12890
# Flags: rx+tx established
# conntrack 항목 수 확인
cilium bpf ct list global | wc -l
# 특정 IP 관련 conntrack 필터링
cilium bpf ct list global | grep "10.244.1.5"
3.4 conntrack GC (가비지 컬렉션)
만료된 conntrack 항목은 주기적으로 정리됩니다.
Agent 내부 GC 루프:
1. BPF 맵의 모든 항목 순회
2. 타임아웃 만료 항목 식별
3. 만료 항목 삭제
4. 관련 NAT 항목도 함께 삭제
5. 메트릭 업데이트
GC 주기: 설정 가능 (기본 약 12초)
4. NAT 엔진
4.1 SNAT (Source NAT / Masquerade)
Pod에서 클러스터 외부로 나가는 트래픽의 소스 IP를 노드 IP로 변환합니다.
SNAT 처리 흐름:
Pod (10.244.1.5:34567) -> 외부 (8.8.8.8:443)
|
v
[from-container BPF]
- 목적지가 클러스터 외부인지 확인
- SNAT 필요 여부 판단
|
v
[SNAT 엔진]
- 소스 IP를 노드 IP로 변환 (10.244.1.5 -> 192.168.1.100)
- 소스 포트를 에페메럴 포트로 변환
- NAT 매핑을 BPF 맵에 저장
|
v
노드 IP (192.168.1.100:50123) -> 외부 (8.8.8.8:443)
응답 패킷 (Reverse SNAT):
외부 (8.8.8.8:443) -> 노드 IP (192.168.1.100:50123)
|
v
[to-netdev BPF]
- NAT 매핑에서 원래 소스 조회
- 목적지를 원래 Pod IP/포트로 복원
|
v
외부 (8.8.8.8:443) -> Pod (10.244.1.5:34567)
4.2 DNAT (서비스 로드 밸런싱)
쿠버네티스 서비스 IP를 실제 백엔드 Pod IP로 변환합니다.
서비스 DNAT 흐름:
클라이언트 (10.244.1.5) -> Service (10.96.0.10:80)
|
v
[from-container BPF: 서비스 LB]
- cilium_lb4_services에서 서비스 조회
- 백엔드 선택 (라운드로빈, Maglev 등)
- 목적지 IP/포트를 백엔드로 변환
|
v
클라이언트 (10.244.1.5) -> Backend Pod (10.244.2.10:8080)
4.3 Maglev 일관된 해싱
Maglev는 Google이 개발한 일관된 해싱 알고리즘으로, Cilium에서 서비스 로드 밸런싱에 사용됩니다.
Maglev 해싱 과정:
1. 룩업 테이블 생성 (65537 엔트리)
- 각 백엔드가 테이블의 여러 위치에 매핑
- 백엔드 수에 관계없이 균일한 분포
2. 패킷 해시 계산
- 5-tuple 해시: src_ip + dst_ip + src_port + dst_port + protocol
- 해시 값으로 테이블 인덱스 결정
3. 백엔드 선택
- table[hash % table_size] = backend
백엔드 변경 시 영향:
- 1개 백엔드 추가/제거 -> 약 1/N 연결만 재매핑
- 기존 연결 대부분 유지
# Maglev 설정 확인
cilium config | grep maglev
# 서비스 백엔드 확인
cilium bpf lb list
# 서비스별 Maglev 룩업 테이블
cilium bpf lb maglev list
4.4 서비스 타입별 처리
ClusterIP:
- from-container에서 DNAT
- 소켓 수준 LB (bpf_sock) 또는 tc 수준
NodePort:
- XDP 또는 tc ingress (from-netdev)에서 DNAT
- DSR 모드: 응답 패킷 직접 전달
- SNAT 모드: 원본 노드를 통해 응답
LoadBalancer:
- NodePort와 동일한 처리 + ExternalIP 매핑
- LB-IPAM에 의해 IP 할당
ExternalTrafficPolicy: Local
- 로컬 백엔드가 있는 경우만 처리
- 클라이언트 소스 IP 보존
5. DSR (Direct Server Return) 모드
5.1 DSR 동작 원리
DSR 모드에서는 응답 패킷이 원래 요청을 수신한 노드를 거치지 않고 직접 클라이언트에게 전달됩니다.
DSR 흐름:
1. 클라이언트 -> 노드A (NodePort)
[노드A: DNAT + DSR 옵션 설정]
- 목적지를 백엔드 Pod(노드B)로 변환
- IP 옵션에 원래 서비스 IP/포트를 인코딩
2. 노드A -> 노드B (백엔드 Pod)
[노드B: DSR 옵션 처리]
- IP 옵션에서 원래 서비스 IP/포트 복원
- conntrack에 reverse NAT 정보 저장
3. 노드B (백엔드 Pod) -> 클라이언트
[노드B: reverse NAT]
- 소스 IP를 서비스 IP로 변환
- 노드A를 거치지 않고 직접 응답
일반 모드 (SNAT):
클라이언트 -> 노드A -> 노드B -> 노드A -> 클라이언트
(2홉 추가)
DSR 모드:
클라이언트 -> 노드A -> 노드B -> 클라이언트
(응답은 직접)
5.2 DSR 구현 방식
IPv4 DSR:
- IP Options 또는 Geneve TLV를 통해 원래 주소 전달
- 추가 패킷 오버헤드 최소화
IPv6 DSR:
- Segment Routing Header (SRH) 또는 Extension Header 사용
- IPv6 네이티브 지원
6. eBPF 테일 콜: 프로그램 체이닝
6.1 테일 콜 메커니즘
eBPF 프로그램에는 명령어 수 제한이 있으므로, 복잡한 처리를 여러 프로그램으로 분리합니다.
테일 콜 체인 예시:
[from-container]
|
tail_call -> [IPv4 정책 확인]
|
tail_call -> [서비스 LB]
|
tail_call -> [NAT 처리]
|
tail_call -> [전달]
6.2 테일 콜 맵
// 테일 콜 맵 구조 (개념적)
// BPF_MAP_TYPE_PROG_ARRAY
struct bpf_map tail_call_map = {
.type = BPF_MAP_TYPE_PROG_ARRAY,
.max_entries = 64,
// 인덱스 -> BPF 프로그램 FD 매핑
};
// 테일 콜 호출
// tail_call(skb, &tail_call_map, CILIUM_CALL_IPV4_FROM_LXC);
6.3 Cilium의 주요 테일 콜 포인트
CILIUM_CALL_IPV4_FROM_LXC = 0 // IPv4 from-container 진입
CILIUM_CALL_IPV4_CT_INGRESS = 4 // IPv4 conntrack 인그레스
CILIUM_CALL_IPV4_CT_EGRESS = 5 // IPv4 conntrack 이그레스
CILIUM_CALL_IPV4_NODEPORT_NAT = 13 // NodePort NAT
CILIUM_CALL_IPV4_NODEPORT_DSR = 14 // NodePort DSR
CILIUM_CALL_IPV4_ENCAP = 15 // 터널 캡슐화
CILIUM_CALL_SEND_ICMP_UNREACH = 18 // ICMP Unreachable 전송
CILIUM_CALL_SRV6_ENCAP = 23 // SRv6 캡슐화
7. 소켓 수준 로드 밸런싱
7.1 소켓 LB 개요
소켓 수준 로드 밸런싱은 iptables나 tc 수준의 NAT를 우회하여 connect() 시스템 콜 시점에 서비스 IP를 백엔드 IP로 직접 변환합니다.
전통적 방식 (iptables/tc):
connect(서비스IP) -> [커널 네트워크 스택] -> [NAT] -> [conntrack] -> 백엔드
소켓 LB 방식:
connect(서비스IP) -> [BPF sock_ops] -> connect(백엔드IP)
- NAT 불필요
- conntrack 항목 불필요
- 네트워크 스택 오버헤드 제거
7.2 BPF 소켓 프로그램 타입
cgroup/connect4: connect() 가로채기 (IPv4)
cgroup/connect6: connect() 가로채기 (IPv6)
cgroup/sendmsg4: UDP sendmsg() 가로채기 (IPv4)
cgroup/sendmsg6: UDP sendmsg() 가로채기 (IPv6)
cgroup/recvmsg4: UDP recvmsg() 가로채기 (IPv4)
cgroup/recvmsg6: UDP recvmsg() 가로채기 (IPv6)
cgroup/getpeername4: getpeername() 가로채기 (IPv4)
cgroup/getpeername6: getpeername() 가로채기 (IPv6)
7.3 소켓 LB의 장점
성능 비교:
tc 수준 LB:
- 패킷마다 NAT 수행
- conntrack 항목 필요
- 추가 CPU 사이클
소켓 수준 LB:
- 연결당 한 번만 변환
- conntrack 불필요
- 최소 CPU 오버헤드
- 원본 서비스 IP 보존 (getpeername)
7.4 소켓 LB 제한사항
소켓 LB가 적용되지 않는 경우:
- 외부에서 NodePort로 들어오는 트래픽
- hostNetwork Pod의 트래픽
- 특정 kube-proxy 호환성 모드
- L7 정책이 적용된 서비스 (Envoy 리디렉션 필요)
이런 경우 tc 수준에서 fallback 처리됨
8. 패킷 드롭과 모니터링
8.1 드롭 이유 코드
Cilium은 패킷 드롭 시 상세한 이유 코드를 제공합니다.
# 드롭된 패킷 모니터링
cilium monitor --type drop
# 출력 예시:
# xx drop (Policy denied) flow ...
# xx drop (Invalid source ip) flow ...
# xx drop (CT: Map insertion failed) flow ...
주요 드롭 이유:
| 코드 | 설명 |
|---|---|
| Policy denied | 정책에 의해 거부됨 |
| Invalid source ip | 소스 IP가 유효하지 않음 |
| CT: Map insertion failed | conntrack 맵 삽입 실패 |
| No mapping for NAT | NAT 매핑 없음 |
| Unknown L3 target | L3 대상을 알 수 없음 |
| Stale or unroutable IP | 오래되거나 라우팅 불가능한 IP |
| Authentication required | mTLS 인증 필요 |
| Service backend not found | 서비스 백엔드 없음 |
8.2 패킷 트레이싱
# 실시간 패킷 트레이스
cilium monitor --type trace
# 특정 엔드포인트의 트래픽만 모니터링
cilium monitor --type trace --from-endpoint 1234
# 정책 verdict 모니터링
cilium monitor --type policy-verdict
# 디버그 수준 모니터링
cilium monitor --type debug
9. 성능 최적화 기법
9.1 XDP 가속
XDP 모드별 성능:
- XDP native (드라이버 내장): 최고 성능
- XDP generic (소프트웨어): 호환성 우선
- XDP offload (NIC 하드웨어): 특정 NIC만 지원
XDP 활용 사례:
- NodePort 가속
- LoadBalancer 가속
- DDoS 방어 (패킷 드롭)
- 프리필터링
9.2 BIG TCP
BIG TCP 동작:
- GRO(Generic Receive Offload)로 수신 패킷을 64KB+ 크기로 병합
- 내부적으로 대용량 패킷으로 처리하여 패킷당 오버헤드 감소
- GSO(Generic Segmentation Offload)로 송신 시 분할
- MTU 제약 없이 내부 처리 최적화
9.3 eBPF 프로그램 최적화
컴파일 시간 최적화:
- 사용하지 않는 기능은 컴파일에서 제외
- 정책이 없는 엔드포인트: 최소한의 BPF 코드
- IPv4 전용 환경: IPv6 코드 제거
런타임 최적화:
- BPF 맵 프리페칭
- 인라인 함수 활용
- 불필요한 조건 분기 제거
- JIT 컴파일로 네이티브 코드 실행
10. 데이터패스 디버깅
10.1 BPF 프로그램 상태 확인
# 모든 BPF 프로그램 목록
bpftool prog list
# 특정 인터페이스의 BPF 프로그램
tc filter show dev lxc1234 ingress
tc filter show dev lxc1234 egress
# BPF 프로그램 통계
bpftool prog show id 42 --json
# BPF 맵 통계
bpftool map show id 10 --json
10.2 성능 메트릭
# 데이터패스 관련 메트릭
cilium metrics list | grep datapath
# 주요 메트릭:
# cilium_datapath_conntrack_gc_runs_total
# cilium_datapath_conntrack_gc_entries
# cilium_bpf_map_ops_total
# cilium_drop_count_total
# cilium_forward_count_total
정리
Cilium의 eBPF 데이터패스는 다음과 같은 핵심 설계로 고성능을 달성합니다.
- 커널 내 처리: 모든 패킷 처리가 커널 공간에서 이루어져 사용자 공간 전환 오버헤드 제거
- 테일 콜 체이닝: 복잡한 로직을 여러 BPF 프로그램으로 분리하여 모듈성 확보
- 자체 conntrack: 리눅스 netfilter conntrack 대신 BPF 맵 기반 고성능 커넥션 트래킹
- 소켓 수준 LB: NAT 없이 서비스 로드 밸런싱으로 최소 오버헤드
- DSR 모드: 불필요한 네트워크 홉 제거로 응답 지연 시간 감소
- XDP 가속: 네트워크 드라이버 수준에서 초고속 패킷 처리