BPF/eBPF
BPF(Berkeley Packet Filter)
1992년 버클리 대학교에서 처음 개발된 고성능 네트워크 패킷 필터링 기술이다. BPF의 핵심 개념은 패킷이 커널로 들어올 때 사용자 정의 필터를 적용하여, 특정 조건에 맞는 패킷만을 선택하는 방식이며, 이는 불필요한 패킷을 필터링하여 시스템 리소스를 절약하고, 네트워크 애플리케이션이 필요한 데이터만 처리할 수 있도록 지원한다.
주요 특징
- 효율성: BPF는 커널 내에서 직접 동작하여, 네트워크 패킷 필터링이 매우 빠릅니다. 이는 시스템 호출 수를 줄여주어 성능을 높이는 데 기여합니다.
- 유연성: BPF는 다양한 조건을 통해 네트워크 패킷을 필터링할 수 있으며, TCP/UDP 포트, IP 주소, 패킷 내용 등 여러 기준으로 필터링 설정이 가능합니다.
eBPF(extended Berkeley Packet Filter)
BPF에서 확장된 것으로 다양한 시스템 기능을 위한 범용 도구로 확장되었습니다. eBPF는 리눅스 커널 내에서 사용자 정의 코드를 실행할 수 있게 해 주며, 시스템 호출, 트레이싱, 네트워크, 보안 모니터링 등에 사용됩니다.
- 실시간 데이터 분석: eBPF는 커널 내에서 사용자 정의 프로그램을 실행하여 실시간으로 데이터를 수집하고 처리합니다. 이는 성능에 거의 영향을 미치지 않으면서 다양한 시스템 메트릭을 수집할 수 있게 해줍니다.
- 네트워크 패킷 필터링 및 트래픽 관리: 네트워크 트래픽을 필터링하거나 관리하는 데 사용할 수 있습니다. eBPF는 XDP(eXpress Data Path)와 결합하여 네트워크 트래픽의 성능을 최적화하는 데 도움을 줄 수 있습니다.
- 보안 모니터링 및 강화: eBPF는 실시간으로 특정 이벤트를 감시하거나 차단하는 데 사용될 수 있어 보안적인 측면에서도 유용합니다.
- 고성능 트레이싱 및 디버깅: bpftrace, bcc(BPF Compiler Collection) 등의 도구를 통해 eBPF 프로그램을 작성하고 실행함으로써, 시스템에서 발생하는 다양한 이벤트를 모니터링하고 분석할 수 있습니다.
Cilium
Cilium은 eBPF를 기반으로 하는 K8s의 CNI 입니다.
- eBPF 기반 고성능 네트워킹: Cilium은 eBPF를 통해 네트워크 트래픽을 효율적으로 처리하고 필터링합니다. 이 방식은 기존의 iptables나 커널 네트워킹 스택보다 성능이 우수하고, 더 세밀한 패킷 처리와 정책 적용이 가능합니다.
- L3/L4 및 L7 보안 정책: Cilium은 전통적인 L3/L4 레벨(IP 및 포트)뿐만 아니라, L7 레벨(HTTP, gRPC 등)에서도 정책을 설정할 수 있습니다. 이를 통해 애플리케이션 레벨의 보안 제어가 가능하며, 마이크로서비스 간의 세부적인 보안 요구 사항을 충족할 수 있습니다.
- 네트워크 트레이싱 및 가시성: Cilium은 트래픽 흐름과 정책 적용 상황을 시각화하고 분석하는 기능을 제공합니다. 이를 통해 네트워크 문제를 빠르게 진단하고 해결할 수 있으며, 정책 위반이나 예상치 못한 네트워크 트래픽을 실시간으로 모니터링할 수 있습니다.
- 마이크로서비스 지원 및 확장성: 마이크로서비스 간의 복잡한 트래픽 경로와 정책 요구 사항을 충족하는 데 특화되어 있어, 대규모 클라우드 네이티브 애플리케이션 환경에서 효율적인 운영이 가능합니다.
구성요소
Cilium 에이전트(Cilium Agent): Cilium 에이전트는 각 Kubernetes 노드에 배포되어 실행되는 핵심 컴포넌트로, 네트워크 트래픽을 제어하고 보안 정책을 적용하는 역할을 합니다. eBPF를 사용해 데이터 플레인에 직접 작동하며, 네트워크 패킷을 필터링하고 정책을 기반으로 통신을 허용하거나 차단합니다. 이 에이전트는 네트워크 정책을 실시간으로 적용하고, CNI(Container Network Interface) 플러그인 역할도 수행합니다.
Cilium CLI는 사용자가 Cilium을 설치하고 관리할 수 있는 커맨드라인 인터페이스 도구입니다. Cilium 네트워크 구성 상태를 확인하거나 정책을 적용하는 데 사용됩니다.
Cilium Operator는 Kubernetes의 컨트롤 플레인에서 실행되며, IP 주소 할당 및 관리, Cilium 네트워크 정책을 Kubernetes 정책으로 변환하는 작업을 수행합니다. 이는 특히 Kubernetes와의 원활한 통합을 위해 중요한 역할을 합니다.
Cilium Hubble: Hubble은 Cilium의 네트워크 트래픽 가시성과 모니터링 기능을 강화하는 컴포넌트입니다. 네트워크 흐름을 실시간으로 시각화하고 분석할 수 있어, 마이크로서비스 간 트래픽 및 정책 적용 상태를 파악하는 데 유용합니다. 또한, Hubble UI를 통해 그래픽 기반의 대시보드를 제공하여 네트워크 이벤트를 직관적으로 모니터링할 수 있습니다.
실습
Cilium 배포
helm repo add cilium https://helm.cilium.io/
helm repo update
배포
helm install cilium cilium/cilium --version 1.16.3 --namespace kube-system \
--set k8sServiceHost=192.168.10.10 --set k8sServicePort=6443 --set debug.enabled=true \
--set rollOutCiliumPods=true --set routingMode=native --set autoDirectNodeRoutes=true \
--set bpf.masquerade=true --set bpf.hostRouting=true --set endpointRoutes.enabled=true \
--set ipam.mode=kubernetes --set k8s.requireIPv4PodCIDR=true --set kubeProxyReplacement=true \
--set ipv4NativeRoutingCIDR=192.168.0.0/16 --set installNoConntrackIptablesRules=true \
--set hubble.ui.enabled=true --set hubble.relay.enabled=true --set prometheus.enabled=true --set operator.prometheus.enabled=true --set hubble.metrics.enableOpenMetrics=true \
--set hubble.metrics.enabled="{dns:query;ignoreAAAA,drop,tcp,flow,port-distribution,icmp,httpV2:exemplars=true;labelsContext=source_ip\,source_namespace\,source_workload\,destination_ip\,destination_namespace\,destination_workload\,traffic_direction}" \
--set operator.replicas=1
bypass-iptables-connection-tracking
helm upgrade cilium cilium/cilium --namespace kube-system --reuse-values --set installNoConntrackIptablesRules=true
확인
iptables -t raw -S | grep notrack
---
-A CILIUM_OUTPUT_raw -d 192.168.0.0/16 -m comment --comment "cilium: NOTRACK for pod traffic" -j CT --notrack
-A CILIUM_OUTPUT_raw -s 192.168.0.0/16 -m comment --comment "cilium: NOTRACK for pod traffic" -j CT --notrack
-A CILIUM_OUTPUT_raw -o lxc+ -m comment --comment "cilium: NOTRACK for proxy return traffic" -j CT --notrack
-A CILIUM_OUTPUT_raw -o cilium_host -m comment --comment "cilium: NOTRACK for proxy return traffic" -j CT --notrack
-A CILIUM_OUTPUT_raw -o lxc+ -m comment --comment "cilium: NOTRACK for L7 proxy upstream traffic" -j CT --notrack
-A CILIUM_OUTPUT_raw -o cilium_host -m comment --comment "cilium: NOTRACK for L7 proxy upstream traffic" -j CT --notrack
-A CILIUM_PRE_raw -d 192.168.0.0/16 -m comment --comment "cilium: NOTRACK for pod traffic" -j CT --notrack
-A CILIUM_PRE_raw -s 192.168.0.0/16 -m comment --comment "cilium: NOTRACK for pod traffic" -j CT --notrack
-A CILIUM_PRE_raw -m comment --comment "cilium: NOTRACK for proxy traffic" -j CT --notrack
CLI 설치
CILIUM_CLI_VERSION=$(curl -s https://raw.githubusercontent.com/cilium/cilium-cli/main/stable.txt)
CLI_ARCH=amd64
if [ "$(uname -m)" = "aarch64" ]; then CLI_ARCH=arm64; fi
curl -L --fail --remote-name-all https://github.com/cilium/cilium-cli/releases/download/${CILIUM_CLI_VERSION}/cilium-linux-${CLI_ARCH}.tar.gz{,.sha256sum}
sha256sum --check cilium-linux-${CLI_ARCH}.tar.gz.sha256sum
sudo tar xzvfC cilium-linux-${CLI_ARCH}.tar.gz /usr/local/bin
rm cilium-linux-${CLI_ARCH}.tar.gz{,.sha256sum}
확인
cilium status --wait
---
/¯¯\
/¯¯\__/¯¯\ Cilium: OK
\__/¯¯\__/ Operator: OK
/¯¯\__/¯¯\ Envoy DaemonSet: OK
\__/¯¯\__/ Hubble Relay: OK
\__/ ClusterMesh: disabled
DaemonSet cilium Desired: 3, Ready: 3/3, Available: 3/3
DaemonSet cilium-envoy Desired: 3, Ready: 3/3, Available: 3/3
Deployment cilium-operator Desired: 1, Ready: 1/1, Available: 1/1
Deployment hubble-relay Desired: 1, Ready: 1/1, Available: 1/1
Deployment hubble-ui Desired: 1, Ready: 1/1, Available: 1/1
Containers: cilium Running: 3
cilium-envoy Running: 3
cilium-operator Running: 1
hubble-relay Running: 1
hubble-ui Running: 1
Cluster Pods: 4/4 managed by Cilium
Helm chart version: 1.16.3
Image versions cilium quay.io/cilium/cilium:v1.16.3@sha256:62d2a09bbef840a46099ac4c69421c90f84f28d018d479749049011329aa7f28: 3
cilium-envoy quay.io/cilium/cilium-envoy:v1.29.9-1728346947-0d05e48bfbb8c4737ec40d5781d970a550ed2bbd@sha256:42614a44e508f70d03a04470df5f61e3cffd22462471a0be0544cf116f2c50ba: 3
cilium-operator quay.io/cilium/operator-generic:v1.16.3@sha256:6e2925ef47a1c76e183c48f95d4ce0d34a1e5e848252f910476c3e11ce1ec94b: 1
hubble-relay quay.io/cilium/hubble-relay:v1.16.3@sha256:feb60efd767e0e7863a94689f4a8db56a0acc7c1d2b307dee66422e3dc25a089: 1
hubble-ui quay.io/cilium/hubble-ui-backend:v0.13.1@sha256:0e0eed917653441fded4e7cdb096b7be6a3bddded5a2dd10812a27b1fc6ed95b: 1
hubble-ui quay.io/cilium/hubble-ui:v0.13.1@sha256:e2e9313eb7caf64b0061d9da0efbdad59c6c461f6ca1752768942bfeda0796c6: 1
cilium 데몬셋 파드 내에서 cilium 명령어로 상태 확인
export CILIUMPOD0=$(kubectl get -l k8s-app=cilium pods -n kube-system --field-selector spec.nodeName=k8s-s -o jsonpath='{.items[0].metadata.name}')
alias c0="kubectl exec -it $CILIUMPOD0 -n kube-system -c cilium-agent -- cilium"
c0 status --verbose
---
KVStore: Ok Disabled
Kubernetes: Ok 1.30 (v1.30.6) [linux/amd64]
Kubernetes APIs: ["EndpointSliceOrEndpoint", "cilium/v2::CiliumClusterwideNetworkPolicy", "cilium/v2::CiliumEndpoint", "cilium/v2::CiliumNetworkPolicy", "cilium/v2::CiliumNode", "cilium/v2alpha1::CiliumCIDRGroup", "core/v1::Namespace", "core/v1::Pods", "core/v1::Service", "networking.k8s.io/v1::NetworkPolicy"]
KubeProxyReplacement: True [eth0 10.0.2.15 fe80::a00:27ff:fe87:8e29, eth1 192.168.10.10 fe80::a00:27ff:fe86:5bc0 (Direct Routing)]
Host firewall: Disabled
SRv6: Disabled
CNI Chaining: none
CNI Config file: successfully wrote CNI configuration file to /host/etc/cni/net.d/05-cilium.conflist
Cilium: Ok 1.16.3 (v1.16.3-f2217191)
NodeMonitor: Listening for events on 2 CPUs with 64x4096 of shared memory
Cilium health daemon: Ok
IPAM: IPv4: 2/254 allocated from 172.16.0.0/24,
Allocated addresses:
172.16.0.216 (router)
172.16.0.243 (health)
IPv4 BIG TCP: Disabled
IPv6 BIG TCP: Disabled
BandwidthManager: Disabled
Routing: Network: Native Host: BPF
Attach Mode: Legacy TC
Device Mode: veth
Masquerading: BPF [eth0, eth1] 192.168.0.0/16 [IPv4: Enabled, IPv6: Disabled]
Clock Source for BPF: ktime
Controller Status: 20/20 healthy
Name Last s
...
Native Routing 확인 : # 192.168.0.0/16 대역은 IP Masq 없이 라우팅
c0 status | grep KubeProxyReplacement
---
KubeProxyReplacement: True [eth0 10.0.2.15 fe80::a00:27ff:fe87:8e29, eth1 192.168.10.10 fe80::a00:27ff:fe86:5bc0 (Direct Routing)]
enableIPv4Masquerade=true(기본값) , bpf.masquerade=true 확인
cilium config view | egrep 'enable-ipv4-masquerade|enable-bpf-masquerade'
---
enable-bpf-masquerade true
enable-ipv4-masquerade true
c0 status --verbose | grep Masquerading
---
Masquerading: BPF [eth0, eth1] 192.168.0.0/16 [IPv4: Enabled, IPv6: Disabled]
eBPF-based ip-masq-agent
helm upgrade cilium cilium/cilium --namespace kube-system --reuse-values --set ipMasqAgent.enabled=true
확인
cilium config view | grep -i masq
---
enable-bpf-masquerade true
enable-ip-masq-agent true
enable-ipv4-masquerade true
enable-ipv6-masquerade true
enable-masquerade-to-route-source false
c0 status --verbose | grep Masquerading
---
Masquerading: BPF (ip-masq-agent) [eth0, eth1] 192.168.0.0/16 [IPv4: Enabled, IPv6: Disabled]
k get cm -n kube-system cilium-config -o yaml | grep ip-masq
enable-ip-masq-agent: "true"

변수 지정
export CILIUMPOD0=$(kubectl get -l k8s-app=cilium pods -n kube-system --field-selector spec.nodeName=k8s-s -o jsonpath='{.items[0].metadata.name}')
export CILIUMPOD1=$(kubectl get -l k8s-app=cilium pods -n kube-system --field-selector spec.nodeName=k8s-w1 -o jsonpath='{.items[0].metadata.name}')
export CILIUMPOD2=$(kubectl get -l k8s-app=cilium pods -n kube-system --field-selector spec.nodeName=k8s-w2 -o jsonpath='{.items[0].metadata.name}')
단축키(alias) 지정
alias c0="kubectl exec -it $CILIUMPOD0 -n kube-system -c cilium-agent -- cilium"
alias c1="kubectl exec -it $CILIUMPOD1 -n kube-system -c cilium-agent -- cilium"
alias c2="kubectl exec -it $CILIUMPOD2 -n kube-system -c cilium-agent -- cilium"
alias c0bpf="kubectl exec -it $CILIUMPOD0 -n kube-system -c cilium-agent -- bpftool"
alias c1bpf="kubectl exec -it $CILIUMPOD1 -n kube-system -c cilium-agent -- bpftool"
alias c2bpf="kubectl exec -it $CILIUMPOD2 -n kube-system -c cilium-agent -- bpftool"
Hubble UI 접속
k patch -n kube-system svc hubble-ui -p '{"spec": {"type": "NodePort"}}'
HubbleUiNodePort=$(kubectl get svc -n kube-system hubble-ui -o jsonpath={.spec.ports[0].nodePort})
echo -e "Hubble UI URL = http://$(curl -s ipinfo.io/ip):$HubbleUiNodePort"
hubble cli 설치
HUBBLE_VERSION=$(curl -s https://raw.githubusercontent.com/cilium/hubble/master/stable.txt)
HUBBLE_ARCH=amd64
if [ "$(uname -m)" = "aarch64" ]; then HUBBLE_ARCH=arm64; fi
curl -L --fail --remote-name-all https://github.com/cilium/hubble/releases/download/$HUBBLE_VERSION/hubble-linux-${HUBBLE_ARCH}.tar.gz{,.sha256sum}
sha256sum --check hubble-linux-${HUBBLE_ARCH}.tar.gz.sha256sum
sudo tar xzvfC hubble-linux-${HUBBLE_ARCH}.tar.gz /usr/local/bin
rm hubble-linux-${HUBBLE_ARCH}.tar.gz{,.sha256sum}
cilium hubble port-forward &

노드간 파드 통신 확인
POD 배포
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
name: netpod
labels:
app: netpod
spec:
nodeName: k8s-s
containers:
- name: netshoot-pod
image: nicolaka/netshoot
command: ["tail"]
args: ["-f", "/dev/null"]
terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Pod
metadata:
name: webpod1
labels:
app: webpod
spec:
nodeName: k8s-w1
containers:
- name: container
image: traefik/whoami
terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Pod
metadata:
name: webpod2
labels:
app: webpod
spec:
nodeName: k8s-w2
containers:
- name: container
image: traefik/whoami
terminationGracePeriodSeconds: 0
EOF
배포 확인
c0 status --verbose | grep Allocated -A5
---
Allocated addresses:
172.16.0.105 (default/netpod)
172.16.0.216 (router)
172.16.0.243 (health)
IPv4 BIG TCP: Disabled
IPv6 BIG TCP: Disabled
c1 status --verbose | grep Allocated -A5
---
Allocated addresses:
172.16.1.107 (router)
172.16.1.158 (default/webpod1)
172.16.1.16 (health)
172.16.1.227 (kube-system/hubble-ui-59bb4cb67b-bj8qq [restored])
172.16.1.33 (kube-system/coredns-55cb58b774-b6vlm [restored])
c2 status --verbose | grep Allocated -A5
---
Allocated addresses:
172.16.2.113 (router)
172.16.2.207 (default/webpod2)
172.16.2.58 (health)
172.16.2.90 (kube-system/coredns-55cb58b774-hkxpm [restored])
IPv4 BIG TCP: Disabled
c0 endpoint list
---
ENDPOINT POLICY (ingress) POLICY (egress) IDENTITY LABELS (source:key[=value]) IPv6 IPv4 STATUS
ENFORCEMENT ENFORCEMENT
1127 Disabled Disabled 4 reserved:health 172.16.0.243 ready
3385 Disabled Disabled 15360 k8s:app=netpod 172.16.0.105 ready
k8s:io.cilium.k8s.namespace.labels.kubernetes.io/metadata.name=default
k8s:io.cilium.k8s.policy.cluster=default
k8s:io.cilium.k8s.policy.serviceaccount=default
k8s:io.kubernetes.pod.namespace=default
4018 Disabled Disabled 1 k8s:node-role.kubernetes.io/control-plane ready
k8s:node.kubernetes.io/exclude-from-external-load-balancers
reserved:host
c0 bpf endpoint list
---
IP ADDRESS LOCAL ENDPOINT INFO
172.16.0.243:0 id=1127 sec_id=4 flags=0x0000 ifindex=9 mac=F2:C5:A6:B0:5B:79 nodemac=AE:FC:13:0B:6D:C0
172.16.0.216:0 (localhost)
192.168.10.10:0 (localhost)
10.0.2.15:0 (localhost)
172.16.0.105:0 id=3385 sec_id=15360 flags=0x0000 ifindex=11 mac=F2:58:A2:C8:85:09 nodemac=A6:76:C0:12:7C:06
c0 map get cilium_lxc
---
Key Value State Error
172.16.0.243:0 id=1127 sec_id=4 flags=0x0000 ifindex=9 mac=F2:C5:A6:B0:5B:79 nodemac=AE:FC:13:0B:6D:C0 sync
172.16.0.105:0 id=3385 sec_id=15360 flags=0x0000 ifindex=11 mac=F2:58:A2:C8:85:09 nodemac=A6:76:C0:12:7C:06 sync
테스트 POD 변수지정
NETPODIP=$(kubectl get pods netpod -o jsonpath='{.status.podIP}')
WEBPOD1IP=$(kubectl get pods webpod1 -o jsonpath='{.status.podIP}')
WEBPOD2IP=$(kubectl get pods webpod2 -o jsonpath='{.status.podIP}')
alias p0="kubectl exec -it netpod -- "
alias p1="kubectl exec -it webpod1 -- "
alias p2="kubectl exec -it webpod2 -- "
확인
p0 ip -c -4 addr
---
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
10: eth0@if11: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000 link-netnsid 0
inet 172.16.0.105/32 scope global eth0
valid_lft forever preferred_lft forever
p0 route -n
---
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 172.16.0.216 0.0.0.0 UG 0 0 0 eth0
172.16.0.216 0.0.0.0 255.255.255.255 UH 0 0 0 eth0
p0 ping -c 1 $WEBPOD1IP && p0 ping -c 1 $WEBPOD2IP
---
PING 172.16.1.158 (172.16.1.158) 56(84) bytes of data.
64 bytes from 172.16.1.158: icmp_seq=1 ttl=62 time=0.741 ms
--- 172.16.1.158 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.741/0.741/0.741/0.000 ms
PING 172.16.2.207 (172.16.2.207) 56(84) bytes of data.
64 bytes from 172.16.2.207: icmp_seq=1 ttl=62 time=0.935 ms
--- 172.16.2.207 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.935/0.935/0.935/0.000 ms
p0 curl -s $WEBPOD1IP && p0 curl -s $WEBPOD2IP
---
Hostname: webpod1
IP: 127.0.0.1
IP: ::1
IP: 172.16.1.158
IP: fe80::38a0:30ff:fe33:1df
RemoteAddr: 172.16.0.105:55234
GET / HTTP/1.1
Host: 172.16.1.158
User-Agent: curl/8.7.1
Accept: */*
Hostname: webpod2
IP: 127.0.0.1
IP: ::1
IP: 172.16.2.207
IP: fe80::a414:5dff:fe93:5223
RemoteAddr: 172.16.0.105:57394
GET / HTTP/1.1
Host: 172.16.2.207
User-Agent: curl/8.7.1
Accept: */*
p0 ping -c 1 8.8.8.8 && p0 curl -s wttr.in/seoul
---
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=61 time=33.2 ms
--- 8.8.8.8 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 33.183/33.183/33.183/0.000 ms
command terminated with exit code 6
p0 ip -c neigh
---
172.16.0.216 dev eth0 lladdr a6:76:c0:12:7c:06 REACHABLE
