본 내용은 gasida님의 Cilium 스터디를 진행 후 정리한 내용입니다.
꾸준히 업데이트 하면서 작성됩니다
Cilium CNI란
Cilium은 eBPF (Berkeley Packet Filter)를 기반으로 파드네트워크 환경과 보안을 제공하는 CNI Plugin 입니다.
- Cilium eBPF는 추가적인 App 이나 설정변경 없이 리눅스 커널을 자유롭게 프로그래밍하여 동작
- 기본 리눅스 네트워크 스택 : 복잡하고 변경에 시간이 걸리고 레이어를 건너 뛰기 어려움
iptables의 단점
iptables 의 규칙 리스트 : Chain
- 단일 트랜잭션으로 모든 규칙 업데이트
- 하나의 규칙 변경 -> 전체 규칙 세트 재구성
- 연결리스트로 구현된 규칙 체인, 모든 연산은 O(n)
- 규칙 추가, 삭제, 조회 ,매칭 등의 모든 연산은 규칙 수에 비례하는 시간 복잡도를 가집니다
- 접근제어목록 (ACLs) 의 표준 구현은 순차적 규칙 목록
- iptables가 구현하는 접근 제어 목록의 일반적인 방식은 규칙들을 순차적으로 나열
- 패킷 당 규칙을 위에서 아래로 순서대로 검사 -> O(n)
- IP 및 포트 매칭 기반, L7 프로토콜에 대한 인지 부족
- iptables는 패킷의 주로 ip와 포트를 기반으로 적용
- L7 수준의 트래픽 제어에 한계
- 새로운 IP 또는 포트 매칭 시 규칙 추가 및 체인 변경 필요
- 새로운 IP 및 포트가 추가 또는 변경될 때마다 해당 변경사항을 위해 새로운 iptables 규칙을 추가 및 기존 체인 수정
- 쿠버네티스 자원 소모
- 각 서비스와 네트워크 정책은 iptables 규칙으로 변환되면 이 과정에서 시스템 자원을 많이 소모
iptables와 cilium 비교
https://blog.naver.com/kangdorr/222593265958
[Kubernetes/Networking] eBPF Basic
Introduction 첫 블로그는 요즘 화두인 Kubnetes의 eBPF에 대해서 얘기하고자 합니다. 처음 작성이라 ...
blog.naver.com
쿠버네티스 클러스터 내부 및 외부에서 Application 접근을 위해 Endpoint가 필요하고,
이러한 Endpoint를 제공하는 것은 쿠버네티스의 Service 오브젝트입니다.
kube-proxy를 통해 해당 서비스 엔드포인트로 전달된 요청을 타겟 파드에 라우팅을 합니다.
Service가 생성되면 해당 서비스에 대한 타켓 백엔드의 분산을 위한 iptables Chain 및 규칙을 생성하고 배포하는 것을
kube-proxy가 담당합니다.
iptables
iptables(Userspace)는 Netfilter (Kernel)과 함께 동작
Packet을 처리하는 과정에서 Netfilter Hook이 트리거 되고 iptables Chain의 규칙을 평가합니다.
https://gnobaaaaar.tistory.com/88
kube-proxy
서로 다른 노드에서 파드간 통신쿠버네티스에서 서로 다른 노드 간의 통신에는 일반적으로 L3 라우팅이 사용됩니다.또한 서로 다른 노드 간의 통신을 위해 물리 네트워크는 브릿지가 어떤 형태
gnobaaaaar.tistory.com

동작순서
NF_IP_PRE_ROUTING : 패킷이 커널에 도착하면 먼저 검사되는 훅 (DNAT 수행가능)
Route : 목적지가 로컬인지 포워딩인지 판단
(로컬)NF_IP_LOCAL_IN
NF_IP_LOCAL_OUT : 로컬어플리케이션에서 전송한 패킷
NF_IP_POST_ROUTING : 송신 전 마지막 훅 (SNAT 수행가능)

eBPF
BPF는 Kernel에 Sandbox 형태로 설치되어 Packet을 필터링, 즉 Packet에 대한 제어가 가능합니다.
BPF의 예시로는 tcpdump 필터링이 있습니다

BPF를 확장한 eBPF가 나왔으며 eBPF는 커널에 내장되어 시스템 변경없이 단순히 시스템 호출을 통해 요청을 처리합니다.

왼쪽은 User-Space-Networking (iptables) 와 커널을 거쳐 불필요한 오버헤드가 발생하지만
eBPF는 커널에 내장되어 있어 커널 레벨에 대한 패킷을 필터링 처리가 가능합니다.
Socket-based Loadbalancing
kube-proxy / iptables 기반
- 사용자가 Service IP (ClusterIP or NodePort) 로 요청
- kube-proxy(iptables or ipvs)는 요청 패킷의 목적지 IP를 DNAT하여 실제 백엔드 Pod IP 변경
- Reverse Proxy (ex. kube-proxy in userspace) 혹은 커널 레벨 NAT 테이블에서 트래픽 라우팅
- 패킷이 Pod에 도달하고, 응답이 다시 사용자에게 갑니다
- 응답은 SNAT 되어 원래 요청한 클라이언트에게 돌아가도록 연결 유지.
- 단, SNAT을 하지 않으면 클라이언트가 직접 Pod IP를 보게 됨.
Socket-based Loadbalancing
eBPF를 사용하는 Socket-based Loadbalancing의 경우 서비스를 요청한 파드에서 부하분산 대상 목적지 파드로
바로 DNAT되어 Direct Routing을 수행합니다.
-> 커널 소켓 수준(bpf_sock)에서 해당 서비스는 어떤 파드로 보내는지 결정됩니다.
-> 유저 공간에서 connect() 호출을 하면 eBPF가 개입하여 백엔드 파드로 연결합니다.
tcpdump로 파드내에서 패킷을 봐도 처음부터 목적지 IP는 백엔드 파드IP 입니다. (서비스 IP가 등장하지 않습니다)
Host(Node)에 구성되는 eBPF가 Pod 또는 Container 수준의 Tracing이 가능한 이유는
Host는 모든 cgroup을 관리할 수 있고
eBPF는 cgroup-bpf을 cgroup에 연결하여 속한 네트워크 트래픽 등 이벤트를 추적합니다.
Cilium에서 파드의 eth0(veth) 인터페이스는 Node의 cilium_host(veth) 인터페이스와 Pair로 구성됩니다.
일반적으로 Host Side veth에는 Proxy_arp가 활성화 되어있어
파드에서 외부로 가는 모든 ARP요청을 응답하는데, Cilium 기반에서는 Proxy_arp가 설정되어있지않습니다.
-> 대신 eBPF가 동작합니다
eBPF 기반 패킷 처리 : XDP와 TC
XDP (eXpress Data Path)

- XDP는 리눅스 커널 내에서 네트워크 인터페이스 카드(NIC) 드라이버 바로 위에서 동작하는 고성능 패킷 처리 기술
- 패킷이 커널 네트워크 스택에 진입하기 전에 가장 빠른 단계에서 필터링, 드롭, 리다이렉트 등의 작업을 수행
TC (Traffic Control)
- TC (Traffic Control) Ingress/Egress ⇒ 현재는 TCX 사용
- TC는 리눅스 커널 네트워크 스택 내에서 동작하는 트래픽 제어 시스템
- XDP에 비해 상대적으로 처리 지연이 있으나 복잡한 정책 적용과 세밀한 트래픽 관리가 가능
- Network Interface 에 tc ingress hook 에서 BPF programs 실행
- 각 파드에 연결된 veth 인터페이스(lxc)에 연결
Cilium은 eBPF 기반으로 TC에 프로그램을 붙여, 커널 네트워크 스택 단계에서 패킷을 직접 처리하고 제어합니다.
XDP 모드도 지원됩니다.
네트워크 모드
터널 모드 (VXLAN, GENEVE)
- 파드 네트워크 트래픽을 오버레이 터널로 캡슐화 해서 전달
- 패킷을 VXLAN 또는 Geneve 프로토콜로 감싸서 노드간 네트워크 연결
- VXLAN(UDP 8472), Geneve(UDP 6081) 인터페이스를 설정
네이티브 라우팅 모드 지원
- 노드 간 터널링 없이 실제 IP 라우팅 기반 통신 처리
- BGP 또는 Cloud 라우팅을 통한 노드/파드 간 통신 경로가 있다고 전제
https://docs.cilium.io/en/stable/network/concepts/routing

IPAM (IP Address Management)
https://docs.cilium.io/en/stable/network/concepts/ipam/#ip-address-management-ipam
- IPAM mode
- Kubernetes Host Scope
- Kube Controller Manager 에 의해 노드에 할당되어 있는 PodCIDR → 실습 환경에서 사용
- Cluster Scope (Default)
- Cilium (CiliumNode CRD) 을 통해 관리하는 Pod CIDRs
- Kubernetes Host Scope
kube-proxy replacement
iptables를 사용하는 일부 기능 이슈가 있으므로 확인 필요
Cilium 구성요소

Cilium Operator : K8S 클러스터에 대한 한 번씩 처리해야 하는 작업을 관리
Cilium Agent : 데몬셋으로 실행, K8S API 설정으로 부터 '네트워크 설정, 네트워크 정책, 서비스 부하분산, 모니터링' 등을 수행하며, eBPF 프로그램을 관리
Cilium Client (CLI) : Cilium 커멘드툴, eBPF maps 에 직접 접속하여 상태를 확인
Hubble : 네트워크와 보안 모니터링 플랫폼 역할을 하여, 'Server, Relay, Client, Graphical UI' 로 구성
Data Store : Cilium Agent 간의 상태를 저장하고 전파하는 데이터 저장소, 2가지 종류 중 선택(K8S CRDs, Key-Value Store)

Life of a Packet



통신 확인
cilium 설치 이후 진행
https://gnobaaaaar.tistory.com/94
1주차 - cilium 설치
더보기본 내용은 gasida님의 Cilium 스터디를 진행 후 정리한 내용입니다.꾸준히 업데이트 하면서 작성됩니다 https://docs.cilium.io/en/stable/operations/system_requirements/ 요구사항- AMD64 또는 AArch64 CPU 아키텍
gnobaaaaar.tistory.com
노드 간 파드 -> 파드 통신


# 엔드포인트 정보 확인
# kubectl get pod -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
curl-pod 1/1 Running 1 (133m ago) 23h 172.20.2.195 k8s-ctr <none> <none>
webpod-7fb5cc765-czp7w 1/1 Running 0 113m 172.20.1.144 k8s-w1 <none> <none>
webpod-7fb5cc765-xc7w4 1/1 Running 0 113m 172.20.0.118 k8s-w2 <none> <none>
# kubectl get svc,ep webpod
Warning: v1 Endpoints is deprecated in v1.33+; use discovery.k8s.io/v1 EndpointSlice
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/webpod ClusterIP 10.96.139.167 <none> 80/TCP 23h
NAME ENDPOINTS AGE
endpoints/webpod 172.20.0.118:80,172.20.1.144:80 23h
#
WEBPOD1IP=172.20.1.144
# BPF maps : 목적지 파드와 통신 시 어느곳으로 보내야 될지 확인 가능합니다
c0 map get cilium_ipcache
c0 map get cilium_ipcache | grep $WEBPOD1IP

# curl-pod 의 LXC 변수 지정
# LXC= Pod의 veth pair 중 하나 반대쪽은 컨테이너 안의 eth0
LXC=<k8s-ctr의 가장 나중에 lxc 이름>
# Node’s eBPF programs
## eBPF 네트워크 프로그램을 보여주는 명령
# net show는 netfilter hook이나 tc (traffic control) hook 등에 설치된 eBPF 프로그램 목록을 출력
c0bpf net show
c0bpf net show | grep $LXC
# 출력 결과
# Cilium이 특정 veth 인터페이스(Pod와 연결된 쪽)에 설치한 eBPF 프로그램에 대한 정보
# cil_from_container : Cilium에서 컨테이너로부터 나가는 트래픽에 대한 eBPF 프로그램
# cil_to_container : 컨테이너로 가는 트래픽에 대한 eBPF 프로그램
lxcdc6ee5c2b59c(9) tcx/ingress cil_from_container prog_id 1176 link_id 20
lxcdc6ee5c2b59c(9) tcx/egress cil_to_container prog_id 1171 link_id 22
# Cilium이 Pod 트래픽을 어떻게 처리하고 있는지에 대한 낮은 수준의 네트워크 가시성을 제공

## eBPF 프로그램이 어떤 역할을 하며, 어떤 map과 연관되어 있는지를 보여줍니다
c0bpf prog show id <출력된 prog id 입력>
# c0bpf prog show id 1176
1176: sched_cls name cil_from_container tag 41989045bb171bee gpl
loaded_at 2025-07-19T12:07:15+0000 uid 0
xlated 752B jited 784B memlock 4096B map_ids 217,218,33
btf_id 485
# c0bpf prog show id 1171
1171: sched_cls name cil_to_container tag 0b3125767ba1861c gpl
loaded_at 2025-07-19T12:07:15+0000 uid 0
xlated 1448B jited 1144B memlock 4096B map_ids 217,33,218
btf_id 480
217,33,218 map list 를 확인해봅니다
c0bpf map list

다른 노드 간 파드 -> 파드 통신 확인
# cilium routing mode: native
# vagrant ssh k8s-w1 접근 후 아래 명령어 실행
ngrep -tW byline -d eth1 '' 'tcp port 80'
# k8s-ctr에서 WEBPOD1 로 curl 요청 (w1 노드)
kubectl exec -it curl-pod -- curl $WEBPOD1IP

node1 에서 eth1(호스트 인터페이스)에서 패킷을 떠도 출발지와 목적지의 IP가 명시됩니다.

통신이 서버 Pod가 있는 노드의 NIC (eth1 등) 에서 그대로 캡처
-> Encapsulation 없이 Native Routing으로 직접 이동 중인 것을 확인가능합니다.
다른 노드 간 파드 -> 서비스 통신 확인

기존의 ClusterIP를 iptables/ipvs 를 통한 실제 pod IP로 DNAT 수행이 후 응답시 다시 SNAT 수행 방식이 아닌 eBPF 방식
1. 앱이 connect() 시스템콜을 이용하여 소켓을 연결
2. Socket Operations eBPF 프로그램이 개입
목적지 주소가 서비스 주소(10.10.8.55)이면 소켓의 목적지 주소를 바로 백엔드 주소(10.0.0.31)로 설정
-> 커널이 소켓을 생성할 때 목적지 주소를 변경
-> 커널은 처음부터 10.0.0.31 (ClusterIP) 로 연결된 소켓을 사용
해당 소켓을 통해 보내는 모든 패킷의 목적지 주소는 이미 10.0.0.31로 설정되어 있기 때문에 DNAT 변환 및 역변환 과정이 필요없어집니다.
: Socket operations : BPF socket operations program 은 root cgroup 에 연결되며 TCP event(ESTABLISHED) 에서 실행
-> 전체 노드를 포함하는 root-level cgroup에 attach / 연결이 성공할 때만 수행
Socket send/recv :
송수신마다 검사/제어/필터링할 수 있는 eBPF hook
The socket send/recv hook 은 TCP socket 의 모든 송수신 작업에서 실행, hook 에서 검사/삭제/리다이렉션 가능합니다.
파드 네임스페이스에서 Socket-Based LoadBalancing 기법
https://velog.io/@haruband/K8SCilium-Socket-Based-LoadBalancing-%EA%B8%B0%EB%B2%95
[K8S/Cilium] Socket-Based LoadBalancing 기법
Cilium 에서 ClusterIP 서비스로 통신할때 로드밸런싱이 이루어지는 과정은 아래 그림과 같다. 왼쪽 그림은 네트워크 기반 로드밸런싱 방식이고, 오른쪽 그림은 소켓 기반 로드밸런싱 방식이다. 간
velog.io

- iptables가 아닌 eBPF를 사용해 소켓 수준에서 로드밸런싱 수행
- pod0 내부의 어플리케이션이 connect(10.10.8.55: 서비스IP)를 이용하여 연결을 시도
- eBPF 프로그램인 bpf_sock[connect4]가 connect() 시스테 호출을 가로챕니다
- eBPF는 로드밸런싱 정책에 따라 10.0.0.32로 소켓의 목적지 주소를 변경 (커널 내 소켓에서 이루어짐)
- 패킷은 node0의 veth0을 거쳐 veth1으로, 그리고 pod1으로 직접 전달
- 별도의 DNAT나 DNAT 역변환이 필요 없습니다
connect() 와 sendto() 소켓 함수에 연결된 프로그램(connect4, sendmsg4)에서는
소켓의 목적지 주소를 백엔드 주소와 포트로 변환하고, cilium_lb4_backends 맵에 백엔드 주소와 포트를 등록
이후 recvmsg() 소켓 함수에 연결된 프로그램(recvmsg4)에서는
cilium_lb4_reverse_nat 맵을 이용해서 목적지 주소와 포트를 다시 서비스 주소와 포트로 변환함
실습
# curl 호출
kubectl exec -it curl-pod -- curl webpod
# 신규 터미널 : 파드에서 SVC(ClusterIP) 접속 시 tcpdump 로 확인 : ClusterIP가 소켓 레벨에서 이미 Endpoint 로 변경되었음을 확인!
kubectl exec curl-pod -- tcpdump -enni any -q
06:19:43.214116 eth0 Out ifindex 17 0e:0e:1f:0b:b4:70 172.20.2.70.33950 > 172.20.1.25.80: tcp 0
06:19:43.215346 eth0 In ifindex 17 6a:20:d8:21:a8:1e 172.20.1.25.80 > 172.20.2.70.33950: tcp 0
# syacall 호출 확인
kubectl exec curl-pod -- strace -c curl -s webpod
# 특정 시스템콜 이벤트 필터링 : -e
## connect 로 출력되는 10.96.165.83 는 webpod Service 의 ClusterIP
kubectl exec curl-pod -- strace -e trace=connect curl -s webpod
---
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl exec curl-pod -- strace -e trace=getsockname curl -s webpod
getsockname(4, {sa_family=AF_INET, sin_port=htons(58107), sin_addr=inet_addr("172.20.2.13")}, [128 => 16]) = 0
getsockname(5, {sa_family=AF_INET, sin_port=htons(43565), sin_addr=inet_addr("172.20.2.13")}, [16]) = 0
getsockname(4, {sa_family=AF_INET, sin_port=htons(38994), sin_addr=inet_addr("172.20.2.13")}, [128 => 16]) = 0
getsockname(4, {sa_family=AF_INET, sin_port=htons(38994), sin_addr=inet_addr("172.20.2.13")}, [128 => 16]) = 0
getsockname(4, {sa_family=AF_INET, sin_port=htons(38994), sin_addr=inet_addr("172.20.2.13")}, [128 => 16]) = 0
Hostname: webpod-7fb5cc765-xc7w4
IP: 127.0.0.1
IP: ::1
IP: 172.20.0.118
IP: fe80::acd9:f9ff:fee8:b388
RemoteAddr: 172.20.2.13:38994
GET / HTTP/1.1
Host: webpod
User-Agent: curl/8.14.1
Accept: */*
+++ exited with 0 +++
# strace 로 IP 변환 확인이 어려운 이유 by ChatGPT
1. Socket LB는 커널 BPF 레벨에서 처리
- Cilium의 Socket LB (sock_ops / sk_msg / sockmap) 기능은 유저스페이스가 아닌 커널의 BPF 프로그램에서 소켓 수준에서 백엔드로 리디렉션합니다. - strace는 시스템 콜 수준(ex: connect, sendto, recvmsg)만 추적하며, 커널 내부 동작 (BPF redirect) 은 보이지 않습니다.
2. strace에서 보이는 정보는 리디렉션 이후 상태일 뿐
- connect() 시 service IP로 요청했지만, 실제 커널에서는 이미 BPF 프로그램이 backend로 소켓을 연결했기 때문에, - 유저 공간에서는 connect(backend-ip)처럼 보이지 않습니다.
이슈들
소켓 기반 로드밸런싱 사용 시 Istio(EnvoyProxy)와 같은 사이드카 우회 문제
[K8S] Cilium Socket-based LoadBalancing 에 의한 Istio Envoy 우회 문제 분석
오늘은 Cilium 에서 소켓 기반 로드밸런싱을 이용할 때 발생하는 EnvoyProxy 사이드카 우회 문제에 대해 살펴보고자 한다. 이는 현재 모든 서비스 메쉬(Istio, Linkerd, ...)에서 서비스 IP 기반으로 동작하
velog.io

- 서비스 컨테이너에서 전송하는 모든 패킷은 Netfilter 에서 강제로 프록시(envoy) 컨테이너로 전달
- 정책은 헤드리스와 같이 특이한 경우를 제외하고는 모두 서비스 IP 를 기반
Cilium 에서 소켓 기반 로드밸런싱을 이용할 경우 2번 단계에서 이미 서비스 IP 가 백엔드 IP 로 변환되었기 때문에
당연히 서비스 IP 기반으로 동작하는 프록시 컨테이너는 제대로 작동할 수가 없다
- HTTP 가 아닌 TCP 에서 발생
- Envoy 의 HTTP 필터가 HTTP 패킷의 Host 헤더를 이용해서 필터링을 수행하기 때문
해결 방안 : 파드 네임스페이스에서는 소켓 기반 로드밸런싱을 사용하지 않는다 → 호스트 네임스페이스만 사용하게 설정
VERSION=1.11.2
helm upgrade cilium cilium/cilium --version $VERSION --namespace kube-system --reuse-values \
--set hostServices.hostNamespaceOnly=true
kubectl -n kube-system rollout restart ds/cilium
# 설정
VERSION=1.11.2
helm upgrade cilium cilium/cilium --version $VERSION --namespace kube-system --reuse-values \
--set hostServices.hostNamespaceOnly=true
kubectl -n kube-system rollout restart ds/cilium
cilium config view | grep bpf-lb-sock-hostns-only
bpf-lb-sock-hostns-only true
# 확인
# 지속적으로 접속 트래픽 발생
while true; do kubectl exec netpod -- curl -s $SVCIP | grep Hostname;echo "-----";sleep 1;done
# 파드에서 SVC(ClusterIP) 접속 시 tcpdump 로 확인 >> 파드 내부 캡쳐인데, SVC(10.108.12.195) 트래픽이 보인다
kubectl exec netpod -- tcpdump -enni any -q
Service ClusterIP로 NFS and SMB mounts(Longhorn, Portworx, and Robin) 사용하는 경우
https://docs.cilium.io/en/stable/network/kubernetes/kubeproxy-free/#limitations
NFS(Network File System)와 SMB(Server Message Block)와 같은 파일 공유 프로토콜을 Kubernetes Service ClusterIP를 통해 마운트하려고 할 때 문제가 발생할 수 있습니다
NFS 및 SMB와 같은 프로토콜은 일반적으로 TCP/IP 기반으로 동작하며, 클라이언트가 서버에 연결하고 데이터를 주고받는 과정에서
소켓 수준의 정보(예: 연결된 소켓의 로컬/원격 주소)를 내부적으로 사용
-> Cilium Socket-LB가 동작할 때 이슈가 발생

'devops > cilium' 카테고리의 다른 글
| 2주차 - Monitoring & Metrics (3) | 2025.07.26 |
|---|---|
| 2주차 - Hubble (3) | 2025.07.26 |
| 1주차 - cilium 설치 (0) | 2025.07.19 |
| 1주차 - flannel CNI (0) | 2025.07.14 |
| 1주차 - 실습 환경 구성 (1) | 2025.07.14 |