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 가속**: 네트워크 드라이버 수준에서 초고속 패킷 처리
현재 단락 (1/364)
Cilium의 eBPF 데이터패스는 리눅스 커널 내에서 패킷을 처리하는 고성능 네트워크 파이프라인입니다. 이 글에서는 패킷이 Pod에서 송수신될 때 거치는 전체 과정을 상세히 분석...