가시다님의 KANS [3기] 스터디 내용을 정리한 포스트 입니다.
Cilium의 가장 큰 특징 중 하나는 기존의 네트워크 인터페이스를 사용하는 대신에 eBPF라는 기술을 사용한다는 점입니다.
eBPF에 대해 잘 설명한 포스팅을 공유합니다. eBPF link
eBPF는 리눅스 커널에서 네트워크 트래픽을 효율적으로 모니터링하고 필터링할 수 있는 강력한 기술입니다. eBPF는 원래 네트워크 패킷을 필터링하기 위한 도구로 개발되었지만, 지금은 네트워크뿐만 아니라 시스템 모니터링, 보안 등 다양한 분야에서 활용되고 있습니다.
리눅스 네트워크 스택의 단점은 복잡하고, 변경에 시간이 걸리고, 레이어를 건너뛰기 어렵다는 단점이 있습니다. IPTables와 비교할 때 eBPF의 장점은 여러 측면에서 IPTables의 단점을 보완하거나 해결합니다.

IPTables와 비교할 때 eBPF의 장점은 여러 측면에서 IPTables의 단점을 보완하거나 해결합니다. 여기서는 eBPF의 관점에서 IPTables의 단점을 설명하겠습니다.

성능 저하
• IPTables의 문제점: IPTables는 패킷 필터링을 위해 리눅스 네트워크 스택을 거칩니다. 네트워크 스택은 계층 구조를 가지고 있어, 각 패킷이 여러 단계의 처리를 거치기 때문에 처리 지연이 발생할 수 있습니다. 또한, IPTables 규칙이 많아질수록 패킷 처리 성능이 급격히 저하됩니다.
• eBPF의 개선: eBPF는 커널 내에서 직접 실행되므로 네트워크 스택을 거치지 않고, 특정한 트래픽 필터링이나 처리를 매우 효율적으로 수행할 수 있습니다. 이로 인해 패킷 처리 속도가 크게 향상되고, 대규모 트래픽 처리에도 성능이 저하되지 않습니다.
규모가 커질수록 관리 복잡성 증가
• IPTables의 문제점: IPTables는 많은 규칙이 쌓이면 규칙이 일일이 순차적으로 적용되기 때문에 확장성에 한계가 있습니다. 대규모 클러스터나 복잡한 정책을 적용할 때 규칙 관리가 어렵고, 규칙 업데이트 시 성능 저하가 발생할 수 있습니다.
• eBPF의 개선: eBPF는 프로그램 형태로 동작하며, 규칙을 효율적으로 적용하고 확장성이 뛰어납니다. 커널에 동적으로 로드되고, 트래픽 필터링이 IPTables처럼 규칙 기반으로 처리되지 않아 대규모 환경에서도 성능이 유지됩니다. 또한 정교한 로깅과 트레이싱이 가능해 네트워크 정책의 모니터링과 디버깅이 용이합니다.
유연성 부족
• IPTables의 문제점: IPTables는 주로 L3/L4 계층에서 패킷 필터링을 수행하며, 애플리케이션 레벨(L7)의 트래픽을 필터링하거나 세밀한 제어를 하기가 어렵습니다. 복잡한 네트워크 정책을 구현하려면 제약이 많습니다.
• eBPF의 개선: eBPF는 L3/L4뿐만 아니라 L7 레벨에서 트래픽을 처리할 수 있어, 애플리케이션 레벨의 트래픽 제어도 가능합니다. 이로 인해 HTTP 요청 필터링, TLS 트래픽 제어, 서비스 메시 통합 등의 고급 기능을 쉽게 구현할 수 있습니다.
규칙 업데이트의 비효율성
• IPTables의 문제점: IPTables의 규칙을 수정하거나 추가할 때, 전체 규칙 테이블을 다시 로드해야 하므로 업데이트가 느리고 비효율적입니다. 특히 대규모 시스템에서는 규칙 업데이트 시 트래픽 중단이나 성능 저하가 발생할 수 있습니다.
• eBPF의 개선: eBPF는 동적으로 커널에 로드되며, 프로그램 자체가 실시간으로 적용됩니다. 규칙을 추가하거나 변경할 때 기존의 모든 규칙을 재적용할 필요가 없으며, 중단 없이 실시간 업데이트가 가능하여 운영 환경에서 유연하게 대응할 수 있습니다.
가시성 부족
• IPTables의 문제점: IPTables는 패킷이 어떤 규칙에 의해 처리되었는지, 왜 차단되었는지에 대한 상세한 정보 제공이 부족합니다. 네트워크 트래픽에 대한 가시성이 떨어지며, 이를 모니터링하기 위해 추가 도구가 필요합니다.
• eBPF의 개선: eBPF는 정교한 트레이싱과 모니터링 기능을 제공합니다. 패킷이 어떻게 처리되고 있는지, 어떤 경로를 거치는지 등을 실시간으로 파악할 수 있어 문제를 신속하게 발견하고 해결할 수 있습니다. Cilium과 같은 도구는 이를 바탕으로 세밀한 네트워크 분석을 제공합니다.
BPF 는 커널에 삽입하여 패킷을 통제(필터링) 할 수 있으며, 다양한 영역에서 Hook 을 통해서 접근 할 수 있습니다.



BPF(1992년) 를 확장해서 eBPF가 (2014년, Alexei Starovoitov) 가 나왔고, eBPF 를 다양한 영역 (보안, 추적, 네트워킹, 모니터링)에서 활용하기 시작하였습니다.



XDP(eXpress Data Path)**는 리눅스 커널의 네트워크 패킷 처리를 위한 고성능 기능으로, eBPF를 기반으로 동작합니다. XDP는 네트워크 인터페이스 카드(NIC) 수준에서 패킷을 필터링하고 처리할 수 있어, 매우 빠른 속도로 패킷을 처리할 수 있다는 장점이 있습니다.

고속 패킷 처리
• XDP는 커널 네트워크 스택에 진입하기 전에 패킷을 처리하기 때문에, 기존의 네트워크 경로에서 발생하는 오버헤드를 피할 수 있습니다. 패킷을 커널 공간으로 전달하기 전에, 네트워크 드라이버 단계에서 바로 처리하므로 매우 짧은 대기 시간과 높은 처리량을 제공합니다.

eBPF 기반 동작
• XDP는 eBPF 프로그램을 사용하여 패킷을 처리합니다. eBPF 프로그램은 커널에 안전하게 로드되어 특정 규칙에 따라 패킷을 처리하거나 필터링할 수 있습니다. XDP의 모든 로직은 사용자 정의 가능하며, 이를 통해 고유한 네트워크 필터링 규칙을 만들 수 있습니다.
네트워크 성능 최적화
• XDP는 기존의 리눅스 네트워크 스택과 비교해 훨씬 적은 리소스를 사용하면서도, 대량의 트래픽을 처리할 수 있습니다. 패킷을 커널에서 처리하지 않고 네트워크 카드에서 바로 처리할 수 있어, 패킷 처리 속도가 크게 향상됩니다. 특히, DDoS 공격과 같은 대량의 패킷을 빠르게 드롭할 수 있는 능력은 네트워크 방어에 매우 유용합니다.

유연한 배포
• XDP는 다양한 네트워크 환경에 쉽게 배포될 수 있습니다. 이를 통해 네트워크 장비와 애플리케이션의 성능을 크게 향상시킬 수 있으며, DDoS 방어, 방화벽, 네트워크 트래픽 분석과 같은 다양한 사용 사례에 적합합니다.
패킷 처리 경로

XDP는 크게 세 가지 패킷 처리 경로를 제공합니다:
XDP_DROP: 패킷을 즉시 드롭합니다. 이는 DDoS 공격 방어와 같이 불필요한 트래픽을 빠르게 차단하는 데 유용합니다.
XDP_PASS: 패킷을 커널 네트워크 스택으로 전달하여 일반적인 네트워크 처리를 계속합니다.
XDP_TX: 패킷을 수정한 후 바로 네트워크 인터페이스를 통해 다시 전송합니다.
XDP_REDIRECT: 패킷을 다른 네트워크 인터페이스나 사용자 공간 프로그램으로 리디렉션합니다.
Cilium은 클라우드 네이티브 환경에서 고성능 네트워킹과 보안 정책을 제공하기 위해 설계된 오픈소스 CNI(Container Network Interface)입니다. 특히, Kubernetes와 같은 컨테이너 오케스트레이션 플랫폼에서 컨테이너 간의 네트워크 통신을 효율적으로 처리하고, 네트워크 보안 정책을 설정하는 데 주로 사용됩니다. Cilium은 eBPF(Extended Berkeley Packet Filter)를 핵심 기술로 사용하여 기존의 IP 테이블 기반 네트워크 솔루션보다 더 높은 성능과 유연성을 제공합니다.
• 서비스 메시를 관리하고, L3/L4뿐만 아니라 L7 레벨에서의 정책도 제공
• 네트워크 정책을 통해 애플리케이션 간의 트래픽을 제어
• 로드 밸런싱과 네트워크 가시성 제공
Cilium은 eBPF를 사용하여 네트워크 트래픽을 처리하고 정책을 적용합니다. 전통적인 CNI는 IP 테이블을 사용하여 네트워크 정책을 처리했지만, eBPF를 사용하면 성능이 훨씬 더 좋아지고, 커널 레벨에서 세밀하게 트래픽을 관리할 수 있습니다.



Cilium Agent
• Cilium 에이전트는 각 Kubernetes 노드에서 실행되며, 네트워크 정책 관리와 eBPF 코드 로드를 담당합니다. 각 노드에서 네트워크 트래픽을 분석하고 처리하며, Kubernetes API 서버와 통신하여 정책 적용을 관리합니다.
Cilium CLI
• Cilium CLI는 Cilium을 관리하기 위한 명령어 도구입니다. 이를 통해 네트워크 정책 설정, 네트워크 상태 확인, 트래픽 모니터링 등을 쉽게 할 수 있습니다.
Cilium Operator
• Kubernetes 클러스터 내에서 네트워크 상태를 유지하고, 네트워크 정책을 적용하며, Cilium 에이전트를 관리하는 역할을 합니다. 특히 IP 할당과 네트워크 플러그인 간의 통합을 담당합니다.
eBPF Programs
• eBPF 프로그램은 커널 내에서 네트워크 트래픽을 실시간으로 처리하며, 보안 정책을 적용하거나 트래픽을 라우팅하는 데 사용됩니다. Cilium은 eBPF 프로그램을 통해 고성능 패킷 처리와 정교한 트래픽 제어를 실현합니다.
Hubble : 네트워크와 보안 모니터링 플랫폼 역할을 하여, 'Server, Relay, Client, Graphical UI' 로 구성되어 있다.
Data Store : Cilium Agent 간의 상태를 저장하고 전파하는 데이터 저장소, 2가지 종류 중 선택(K8S CRDs, Key-Value Store)



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
## 주요 파라미터 설명
--set debug.enabled=true # cilium 파드에 로그 레벨을 debug 설정
--set autoDirectNodeRoutes=true # 동일 대역 내의 노드들 끼리는 상대 노드의 podCIDR 대역의 라우팅이 자동으로 설정
--set endpointRoutes.enabled=true # 호스트에 endpoint(파드)별 개별 라우팅 설정
--set hubble.relay.enabled=true --set hubble.ui.enabled=true # hubble 활성화
--set ipam.mode=kubernetes --set k8s.requireIPv4PodCIDR=true # k8s IPAM 활용
--set kubeProxyReplacement=true # kube-proxy 없이 (최대한) 대처할수 있수 있게
--set ipv4NativeRoutingCIDR=192.168.0.0/16 # 해당 대역과 통신 시 IP Masq 하지 않음, 보통 사내망 대역을 지정
--set operator.replicas=1 # cilium-operator 파드 기본 1개
--set enableIPv4Masquerade=true --set bpf.masquerade=true # 파드를 위한 Masquerade , 추가로 Masquerade 을 BPF 로 처리 >> enableIPv4Masquerade=true 인 상태에서 추가로 bpf.masquerade=true 적용이 가능

# Cilium 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 config view

# 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
...
KubeProxyReplacement: True [ens5 192.168.10.10 fe80::57:abff:fee3:da8d (Direct Routing)]
...
IPAM: IPv4: 2/254 allocated from 172.16.0.0/24,
Allocated addresses:
172.16.0.159 (router)
172.16.0.171 (health)
...
Routing: Network: Native Host: BPF
...
Device Mode: veth
Masquerading: BPF [ens5] 192.168.0.0/16 [IPv4: Enabled, IPv6: Disabled]
...
Proxy Status: OK, ip 172.16.0.159, 0 redirects active on ports 10000-20000, Envoy: external
...
KubeProxyReplacement Details:
Status: True
Socket LB: Enabled
Socket LB Tracing: Enabled
Socket LB Coverage: Full
Devices: ens5 192.168.10.10 fe80::57:abff:fee3:da8d (Direct Routing)
Mode: SNAT
Backend Selection: Random
Session Affinity: Enabled
Graceful Termination: Enabled
NAT46/64 Support: Disabled
XDP Acceleration: Disabled
Services:
- ClusterIP: Enabled
- NodePort: Enabled (Range: 30000-32767)
- LoadBalancer: Enabled
- externalIPs: Enabled
- HostPort: Enabled
BPF Maps: dynamic sizing: on (ratio: 0.002500)
...
# Native Routing 확인 : # 192.168.0.0/16 대역은 IP Masq 없이 라우팅
c0 status | grep KubeProxyReplacement
KubeProxyReplacement: True [ens5 192.168.10.10 fe80::50:46ff:fe62:3505 (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 [ens5] 192.168.0.0/16 [IPv4: Enabled, IPv6: Disabled]
# Configure the eBPF-based ip-masq-agent
# https://docs.cilium.io/en/stable/network/concepts/masquerading/
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
...
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 | grep Masquerading
Masquerading: BPF (ip-masq-agent) [ens5] 192.168.0.0/16 [IPv4: Enabled, IPv6: Disabled]
kubectl get cm -n kube-system cilium-config -o yaml | grep ip-masq
enable-ip-masq-agent: "true"
$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 UI URL = http://43.201.15.39:32578

# cilium 파드 확인
kubectl get pod -n kube-system -l k8s-app=cilium -owide
# cilium 파드 재시작
kubectl -n kube-system rollout restart ds/cilium
혹은
kubectl delete pod -n kube-system -l k8s-app=cilium
# cilium 설정 정보 확인
cilium config view
# cilium 파드의 cilium 상태 확인
c0 status --verbose
# cilium 엔드포인트 확인
kubectl get ciliumendpoints -A
c0 endpoint list
c0 bpf endpoint list
c0 map get cilium_lxc
c0 ip list
# Manage the IPCache mappings for IP/CIDR <-> Identity
c0 bpf ipcache list
# Service/NAT List 확인
c0 service list
c0 bpf lb list
c0 bpf lb list --revnat
c0 bpf nat list
# List all open BPF maps
c0 map list
c0 map list --verbose
# List contents of a policy BPF map : Dump all policy maps
c0 bpf policy get --all
c0 bpf policy get --all -n
# cilium monitor
c0 monitor -v
c0 monitor -v --type l7


# 네트워크 인터페이스 정보 확인
ip -br -c link
ip -br -c addr

--------------------------------------------
# cilium_net 과 cilium_host 는 veth peer 관계이며, cilium_host 는 파드의 GW IP 주소로 지정되며 32bit 이다
ip -c addr show cilium_net ; ip -c addr show cilium_host
3: cilium_net@cilium_host: <BROADCAST,MULTICAST,NOARP,UP,LOWER_UP> mtu 9001 qdisc noqueue state UP group default qlen 1000
link/ether 5e:0d:9c:77:1c:d3 brd ff:ff:ff:ff:ff:ff
inet6 fe80::5c0d:9cff:fe77:1cd3/64 scope link
valid_lft forever preferred_lft forever
4: cilium_host@cilium_net: <BROADCAST,MULTICAST,NOARP,UP,LOWER_UP> mtu 9001 qdisc noqueue state UP group default qlen 1000
link/ether 8a:97:09:3e:4b:e4 brd ff:ff:ff:ff:ff:ff
inet 172.16.0.229/32 scope global cilium_host
valid_lft forever preferred_lft forever
inet6 fe80::8897:9ff:fe3e:4be4/64 scope link
valid_lft forever preferred_lft forever
...
# proxy arp 는 disable(0) 상태이며, 파드와 연결된 lxc 도 모두 0 이다
# 파드의 32bit ip의 gw 가 각각 연결된 veth 인터페이스의 mac 으로 cilium_host 의 IP/MAC 응답을 처리한다, 어떻게 동작이 되는걸까요? >> eBPF program!!!
cat /proc/sys/net/ipv4/conf/cilium_net/proxy_arp
0
cat /proc/sys/net/ipv4/conf/cilium_host/proxy_arp
0
# lxc_health 인터페이스는 veth 로 cilium(NET NS 0, 호스트와 다름)과 veth pair 이다 - 링크
# cilium 인터페이스에 파드 IP가 할당되어 있으며, cilium-health-responder 로 동작한다
lsns -t net
NS TYPE NPROCS PID USER NETNSID NSFS COMMAND
4026531840 net 147 1 root unassigned /sbin/init
4026532326 net 1 13559 root 0 cilium-health-responder --listen 4240 --pidfile /var/run/cilium/state/health-endpoint.pid

Cilium Hubble은 Cilium과 함께 제공되는 네트워크 관측 및 모니터링 도구로, 클라우드 네이티브 환경에서 네트워크 트래픽 가시성을 제공하는데요. Hubble은 eBPF를 활용해 실시간으로 네트워크 통신을 추적하고, Kubernetes 클러스터 내부의 컨테이너 간 네트워크 흐름을 시각화하는 강력한 도구입니다. 이를 통해 네트워크 상태, 보안 정책의 적용 여부, 트래픽 흐름 등을 쉽게 확인하고 문제를 신속하게 해결할 수 있습니다.

실시간 네트워크 가시성
• Hubble은 Kubernetes 클러스터에서 발생하는 모든 네트워크 트래픽을 실시간으로 관찰할 수 있게 해줍니다. 컨테이너 간의 패킷 흐름, 서비스 간 통신, 보안 정책 적용 여부 등을 실시간으로 추적하여 네트워크 상태를 명확히 파악할 수 있습니다.
• 각 네트워크 연결 상태, 통신 지연 시간, 트래픽 흐름 경로 등을 시각적으로 확인할 수 있어 문제 진단과 성능 최적화에 매우 유용합니다.
세밀한 네트워크 및 보안 모니터링
• Hubble은 L3/L4뿐만 아니라 L7(Application Layer) 트래픽까지 추적하여, HTTP, gRPC, Kafka 등 애플리케이션 레벨의 네트워크 통신을 모니터링할 수 있습니다. 이를 통해 특정 애플리케이션의 트래픽 상태를 분석하거나, 세밀한 보안 정책이 제대로 적용되고 있는지 확인할 수 있습니다.
• 또한, Hubble은 네트워크 보안 정책의 로그를 제공하여, 정책에 의해 차단된 트래픽과 허용된 트래픽을 명확히 확인할 수 있습니다.
다양한 가시성 도구
• Hubble은 여러 도구를 제공하여 네트워크를 시각적으로 분석할 수 있습니다.
• Hubble CLI: 명령줄 도구를 통해 실시간으로 네트워크 트래픽을 추적하고, 보안 정책의 적용 결과를 확인할 수 있습니다.
• Hubble UI: 웹 기반 대시보드를 제공하여 네트워크 상태를 직관적으로 시각화합니다. 이를 통해 각 포드 간의 통신 경로, 트래픽 상태, 정책 적용 상태 등을 한눈에 파악할 수 있습니다.
• Hubble Relay: 여러 노드에서 수집된 트래픽 정보를 중앙에서 집계하고, 그 데이터를 Hubble UI와 같은 시각화 도구에서 사용 가능하게 합니다.
네트워크 성능 분석
• Hubble은 네트워크 지연 시간, 패킷 손실률, 대역폭 사용량 등의 성능 메트릭을 실시간으로 추적하여 네트워크 성능을 분석합니다. 이를 통해 병목 현상이 발생하는 지점을 신속히 찾아내고, 네트워크 성능을 최적화할 수 있습니다.
• 또한, 서비스 간 통신에서 발생하는 문제점을 실시간으로 모니터링하여 애플리케이션의 성능 저하나 네트워크 장애를 신속히 파악할 수 있습니다.
강력한 보안 및 감사 기능
• Hubble은 Kubernetes 네트워크 정책의 적용 상태를 세밀하게 모니터링하고, 네트워크 트래픽에 대한 정책 감사 기능을 제공합니다. 이를 통해 특정 네트워크 흐름이 정책에 의해 허용되었는지, 차단되었는지 기록하여 보안 사고 발생 시 빠르게 대처할 수 있습니다.
• 감사 기록은 네트워크 보안 상태를 관리하고, 보안 규정을 준수하는 데 중요한 역할을 합니다.
배포 및 확장성
• Hubble은 Kubernetes 클러스터에 손쉽게 배포할 수 있으며, Cilium과 긴밀히 통합되어 클러스터의 규모와 관계없이 확장성 있는 네트워크 가시성을 제공합니다. 여러 노드에 걸친 트래픽을 중앙에서 수집하고 시각화하는 기능을 통해 대규모 환경에서도 일관된 네트워크 모니터링이 가능합니다.
Hubble UI
• 사용자 친화적인 웹 대시보드로, 클러스터에서 발생하는 트래픽을 시각적으로 확인할 수 있습니다. 각 애플리케이션 또는 서비스 간의 트래픽 흐름을 그래프 형태로 보여주며, 세부 트래픽 정보와 정책 적용 상태를 실시간으로 확인할 수 있습니다.
Hubble CLI
• 명령줄 인터페이스를 통해 Hubble의 다양한 기능을 사용할 수 있습니다. 실시간으로 네트워크 흐름을 모니터링하거나 특정 트래픽을 필터링하여 분석할 수 있습니다. 이는 자동화된 스크립트와도 연동이 가능하여, 운영 환경에서 유연하게 활용할 수 있습니다.
Hubble Relay

• 분산된 Hubble 에이전트에서 수집된 네트워크 정보를 중앙에서 집계하고 전달하는 역할을 합니다. 이를 통해 대규모 클러스터에서 확장성을 유지하며, 각 노드의 트래픽 정보를 효율적으로 관리할 수 있습니다.
Hubble Metrics

• 네트워크 상태와 성능 지표를 Prometheus와 같은 모니터링 시스템과 연동하여 메트릭 데이터를 수집하고 시각화할 수 있습니다. 이 데이터를 바탕으로 네트워크 성능을 최적화하거나 경고를 설정할 수 있습니다.
# 확인
cilium status
# UI 파드 정보 확인
kubectl get pod -n kube-system -l k8s-app=hubble-ui -o wide
# Hubble UI 웹 접속
kubectl 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"
## Service NodePort 생성 후 아래 정보 확인!
iptables -t nat -S
-P PREROUTING ACCEPT
-P INPUT ACCEPT
-P OUTPUT ACCEPT
-P POSTROUTING ACCEPT
-N CILIUM_OUTPUT_nat
-N CILIUM_POST_nat
-N CILIUM_PRE_nat
-N KUBE-KUBELET-CANARY
-A PREROUTING -m comment --comment "cilium-feeder: CILIUM_PRE_nat" -j CILIUM_PRE_nat
-A OUTPUT -m comment --comment "cilium-feeder: CILIUM_OUTPUT_nat" -j CILIUM_OUTPUT_nat
-A POSTROUTING -m comment --comment "cilium-feeder: CILIUM_POST_nat" -j CILIUM_POST_nat
conntrack -L
conntrack -L |grep -v 2379
tcp 6 0 CLOSE src=127.0.0.1 dst=127.0.0.1 sport=39450 dport=10259 src=127.0.0.1 dst=127.0.0.1 sport=10259 dport=39450 [ASSURED] mark=0 use=1
tcp 6 49 TIME_WAIT src=127.0.0.1 dst=127.0.0.1 sport=51554 dport=9879 src=127.0.0.1 dst=127.0.0.1 sport=9879 dport=51554 [ASSURED] mark=0 use=1
tcp 6 43 TIME_WAIT src=127.0.0.1 dst=127.0.0.1 sport=47728 dport=2381 src=127.0.0.1 dst=127.0.0.1 sport=2381 dport=47728 [ASSURED] mark=0 use=1
tcp 6 23 TIME_WAIT src=127.0.0.1 dst=127.0.0.1 sport=54738 dport=2381 src=127.0.0.1 dst=127.0.0.1 sport=2381 dport=54738 [ASSURED] mark=0 use=1
tcp 6 79 TIME_WAIT src=127.0.0.1 dst=127.0.0.1 sport=48616 dport=9879 src=127.0.0.1 dst=127.0.0.1 sport=9879 dport=48616 [ASSURED] mark=0 use=1
tcp 6 63 TIME_WAIT src=127.0.0.1 dst=127.0.0.1 sport=41626 dport=2381 src=127.0.0.1 dst=127.0.0.1 sport=2381 dport=41626 [ASSURED] mark=0 use=1
tcp 6 105 TIME_WAIT src=127.0.0.1 dst=127.0.0.1 sport=34416 dport=9878 src=127.0.0.1 dst=127.0.0.1 sport=9878 dport=34416 [ASSURED] mark=0 use=1
tcp 6 3 TIME_WAIT src=127.0.0.1 dst=127.0.0.1 sport=49398 dport=2381 src=127.0.0.1 dst=127.0.0.1 sport=2381 dport=49398 [ASSURED] mark=0 use=1
tcp 6 0 CLOSE src=127.0.0.1 dst=127.0.0.1 sport=60384 dport=10257 src=127.0.0.1 dst=127.0.0.1 sport=10257 dport=60384 [ASSURED] mark=0 use=1
tcp 6 83 TIME_WAIT src=127.0.0.1 dst=127.0.0.1 sport=56854 dport=2381 src=127.0.0.1 dst=127.0.0.1 sport=2381 dport=56854 [ASSURED] mark=0 use=1
tcp 6 45 TIME_WAIT src=127.0.0.1 dst=127.0.0.1 sport=48828 dport=9878 src=127.0.0.1 dst=127.0.0.1 sport=9878 dport=48828 [ASSURED] mark=0 use=1
tcp 6 73 TIME_WAIT src=127.0.0.1 dst=127.0.0.1 sport=38080 dport=2381 src=127.0.0.1 dst=127.0.0.1 sport=2381 dport=38080 [ASSURED] mark=0 use=1
tcp 6 75 TIME_WAIT src=127.0.0.1 dst=127.0.0.1 sport=51712 dport=9878 src=127.0.0.1 dst=127.0.0.1 sport=9878 dport=51712 [ASSURED] mark=0 use=1
tcp 6 53 TIME_WAIT src=127.0.0.1 dst=127.0.0.1 sport=52296 dport=2381 src=127.0.0.1 dst=127.0.0.1 sport=2381 dport=52296 [ASSURED] mark=0 use=1
tcp 6 79 TIME_WAIT src=127.0.0.1 dst=127.0.0.1 sport=48606 dport=9879 src=127.0.0.1 dst=127.0.0.1 sport=9879 dport=48606 [ASSURED] mark=0 use=1
tcp 6 93 TIME_WAIT src=127.0.0.1 dst=127.0.0.1 sport=34048 dport=2381 src=127.0.0.1 dst=127.0.0.1 sport=2381 dport=34048 [ASSURED] mark=0 use=1
tcp 6 19 TIME_WAIT src=127.0.0.1 dst=127.0.0.1 sport=56934 dport=9879 src=127.0.0.1 dst=127.0.0.1 sport=9879 dport=56934 [ASSURED] mark=0 use=1
tcp 6 49 TIME_WAIT src=127.0.0.1 dst=127.0.0.1 sport=51566 dport=9879 src=127.0.0.1 dst=127.0.0.1 sport=9879 dport=51566 [ASSURED] mark=0 use=1
tcp 6 33 TIME_WAIT src=127.0.0.1 dst=127.0.0.1 sport=47774 dport=2381 src=127.0.0.1 dst=127.0.0.1 sport=2381 dport=47774 [ASSURED] mark=0 use=1
tcp 6 113 TIME_WAIT src=127.0.0.1 dst=127.0.0.1 sport=59006 dport=2381 src=127.0.0.1 dst=127.0.0.1 sport=2381 dport=59006 [ASSURED] mark=0 use=1
tcp 6 109 TIME_WAIT src=127.0.0.1 dst=127.0.0.1 sport=35320 dport=9879 src=127.0.0.1 dst=127.0.0.1 sport=9879 dport=35320 [ASSURED] mark=0 use=1
tcp 6 49 TIME_WAIT src=127.0.0.1 dst=127.0.0.1 sport=43828 dport=33225 src=127.0.0.1 dst=127.0.0.1 sport=33225 dport=43828 [ASSURED] mark=0 use=1
tcp 6 15 TIME_WAIT src=127.0.0.1 dst=127.0.0.1 sport=36790 dport=9878 src=127.0.0.1 dst=127.0.0.1 sport=9878 dport=36790 [ASSURED] mark=0 use=1
tcp 6 109 TIME_WAIT src=127.0.0.1 dst=127.0.0.1 sport=35328 dport=9879 src=127.0.0.1 dst=127.0.0.1 sport=9879 dport=35328 [ASSURED] mark=0 use=1
conntrack v1.4.6 (conntrack-tools): 104 flow entries have been shown.
tcp 6 13 TIME_WAIT src=127.0.0.1 dst=127.0.0.1 sport=41580 dport=2381 src=127.0.0.1 dst=127.0.0.1 sport=2381 dport=41580 [ASSURED] mark=0 use=1
tcp 6 49 TIME_WAIT src=127.0.0.1 dst=127.0.0.1 sport=43824 dport=33225 src=127.0.0.1 dst=127.0.0.1 sport=33225 dport=43824 [ASSURED] mark=0 use=1
tcp 6 105 TIME_WAIT src=127.0.0.1 dst=127.0.0.1 sport=34426 dport=9878 src=127.0.0.1 dst=127.0.0.1 sport=9878 dport=34426 [ASSURED] mark=0 use=1
tcp 6 45 TIME_WAIT src=127.0.0.1 dst=127.0.0.1 sport=48830 dport=9878 src=127.0.0.1 dst=127.0.0.1 sport=9878 dport=48830 [ASSURED] mark=0 use=2
tcp 6 75 TIME_WAIT src=127.0.0.1 dst=127.0.0.1 sport=51728 dport=9878 src=127.0.0.1 dst=127.0.0.1 sport=9878 dport=51728 [ASSURED] mark=0 use=1
tcp 6 19 TIME_WAIT src=127.0.0.1 dst=127.0.0.1 sport=56948 dport=9879 src=127.0.0.1 dst=127.0.0.1 sport=9879 dport=56948 [ASSURED] mark=0 use=1
tcp 6 103 TIME_WAIT src=127.0.0.1 dst=127.0.0.1 sport=43656 dport=2381 src=127.0.0.1 dst=127.0.0.1 sport=2381 dport=43656 [ASSURED] mark=0 use=1
tcp 6 15 TIME_WAIT src=127.0.0.1 dst=127.0.0.1 sport=36788 dport=9878 src=127.0.0.1 dst=127.0.0.1 sport=9878 dport=36788 [ASSURED] mark=0 use=1
# Install Hubble Client
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}
# Hubble API Access : localhost TCP 4245 Relay 를 통해 접근, observe 를 통해서 flow 쿼리 확인 가능!
cilium hubble port-forward &
[1] 50952
# CLI 로 Hubble API 상태 확인
hubble status
Healthcheck (via localhost:4245): Ok
Current/Max Flows: 12,285/12,285 (100.00%)
Flows/s: 38.55
Connected Nodes: 3/
# query the flow API and look for flows
hubble observe
# hubble observe --pod netpod
# hubble observe --namespace galaxy --http-method POST --http-path /v1/request-landing
# hubble observe --pod deathstar --protocol http
# hubble observe --pod deathstar --verdict DROPPED


# k8s-s에서 아래 명령어 수행
$ cilium connectivity test
웹브라우저에서 Hubble UI 접속 후 cilium-test-1 네임스페이스 선택

Endpoint to Endpoint

Egress from Endpoint

Ingress to Endpoint

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
kubectl get pod -o wide
c0 status --verbose | grep Allocated -A5
c1 status --verbose | grep Allocated -A5
c2 status --verbose | grep Allocated -A5

$kubectl get ciliumendpoints
NAME SECURITY IDENTITY ENDPOINT STATE IPV4 IPV6
netpod 4167 ready 172.16.0.235
webpod1 51289 ready 172.16.1.47
webpod2 51289 ready 172.16.2.104
$kubectl get ciliumendpoints -A
NAMESPACE NAME SECURITY IDENTITY ENDPOINT STATE IPV4 IPV6
default netpod 4167 ready 172.16.0.235
default webpod1 51289 ready 172.16.1.47
default webpod2 51289 ready 172.16.2.104
kube-system coredns-55cb58b774-75qkq 56456 ready 172.16.2.184
kube-system coredns-55cb58b774-kkr6r 56456 ready 172.16.2.194
kube-system hubble-relay-88f7f89d4-wjrb5 20484 ready 172.16.2.65
kube-system hubble-ui-59bb4cb67b-qknmv 11094 ready 172.16.2.179
$c0 endpoint list
ENDPOINT POLICY (ingress) POLICY (egress) IDENTITY LABELS (source:key[=value]) IPv6 IPv4 STATUS
ENFORCEMENT ENFORCEMENT
543 Disabled Disabled 4167 k8s:app=netpod 172.16.0.235 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
1533 Disabled Disabled 1 k8s:node-role.kubernetes.io/control-plane ready
k8s:node.kubernetes.io/exclude-from-external-load-balancers
reserved:host
3599 Disabled Disabled 4 reserved:health 172.16.0.75 ready
$c0 bpf endpoint list
IP ADDRESS LOCAL ENDPOINT INFO
192.168.10.10:0 (localhost)
172.16.0.235:0 id=543 sec_id=4167 flags=0x0000 ifindex=10 mac=9E:2B:22:46:EC:A3 nodemac=EA:68:2A:30:09:BF
172.16.0.229:0 (localhost)
172.16.0.75:0 id=3599 sec_id=4 flags=0x0000 ifindex=8 mac=FA:1B:DC:45:D4:4A nodemac=3E:1C:AB:C9:28:3E
$c0 map get cilium_lxc
Key Value State Error
172.16.0.235:0 id=543 sec_id=4167 flags=0x0000 ifindex=10 mac=9E:2B:22:46:EC:A3 nodemac=EA:68:2A:30:09:BF sync
172.16.0.75:0 id=3599 sec_id=4 flags=0x0000 ifindex=8 mac=FA:1B:DC:45:D4:4A nodemac=3E:1C:AB:C9:28:3E sync
$c0 ip list
IP IDENTITY SOURCE
0.0.0.0/0 reserved:world
172.16.0.75/32 reserved:health
172.16.0.229/32 reserved:host
reserved:kube-apiserver
172.16.0.235/32 k8s:app=netpod custom-resource
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
172.16.1.47/32 k8s:app=webpod custom-resource
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
172.16.1.229/32 reserved:remote-node
172.16.1.230/32 reserved:health
172.16.2.65/32 k8s:app.kubernetes.io/name=hubble-relay custom-resource
k8s:app.kubernetes.io/part-of=cilium
k8s:io.cilium.k8s.namespace.labels.kubernetes.io/metadata.name=kube-system
k8s:io.cilium.k8s.policy.cluster=default
k8s:io.cilium.k8s.policy.serviceaccount=hubble-relay
k8s:io.kubernetes.pod.namespace=kube-system
k8s:k8s-app=hubble-relay
172.16.2.104/32 k8s:app=webpod custom-resource
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
172.16.2.129/32 reserved:health
172.16.2.134/32 reserved:remote-node
172.16.2.179/32 k8s:app.kubernetes.io/name=hubble-ui custom-resource
k8s:app.kubernetes.io/part-of=cilium
k8s:io.cilium.k8s.namespace.labels.kubernetes.io/metadata.name=kube-system
k8s:io.cilium.k8s.policy.cluster=default
k8s:io.cilium.k8s.policy.serviceaccount=hubble-ui
k8s:io.kubernetes.pod.namespace=kube-system
k8s:k8s-app=hubble-ui
172.16.2.184/32 k8s:io.cilium.k8s.namespace.labels.kubernetes.io/metadata.name=kube-system custom-resource
k8s:io.cilium.k8s.policy.cluster=default
k8s:io.cilium.k8s.policy.serviceaccount=coredns
k8s:io.kubernetes.pod.namespace=kube-system
k8s:k8s-app=kube-dns
172.16.2.194/32 k8s:io.cilium.k8s.namespace.labels.kubernetes.io/metadata.name=kube-system custom-resource
k8s:io.cilium.k8s.policy.cluster=default
k8s:io.cilium.k8s.policy.serviceaccount=coredns
k8s:io.kubernetes.pod.namespace=kube-system
k8s:k8s-app=kube-dns
192.168.10.10/32 reserved:host
reserved:kube-apiserver
192.168.10.101/32 reserved:remote-node
192.168.10.102/32 reserved:remote-node
# 테스트 파드들 IP
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) 지정
alias p0="kubectl exec -it netpod -- "
alias p1="kubectl exec -it webpod1 -- "
alias p2="kubectl exec -it webpod2 -- "
# netpod 네트워크 정보 확인
p0 ip -c -4 addr
p0 route -n
p0 ping -c 1 $WEBPOD1IP && p0 ping -c 1 $WEBPOD2IP
p0 curl -s $WEBPOD1IP && p0 curl -s $WEBPOD2IP
p0 curl -s $WEBPOD1IP:8080 ; p0 curl -s $WEBPOD2IP:8080
p0 ping -c 1 8.8.8.8 && p0 curl -s wttr.in/seoul
p0 ip -c neigh
# hubble cli 확인
hubble observe --pod netpod
hubble observe --pod webpod1
hubble observe --pod webpod2
# BPF maps : 목적지 파드와 통신 시 어느곳으로 보내야 될지 확인할 수 있다
c0 map get cilium_ipcache
c0 map get cilium_ipcache | grep $WEBPOD1IP
# netpod 의 LXC 변수 지정
LXC=<k8s-s의 가장 나중에 lxc 이름>
LXC=lxc35dd9f4e9602
# 파드와 veth pair 에 IP가 없다! proxy_arp 도 없다! 하지만 GW MAC 요청 시 lxc(veth)의 MAC 으로 응답이 온다! >> eBPF Magic!
# Cilium hijacks ARP table of POD1, forces the next hop to be the peer end (host side) of the veth pair.
ip -c addr show dev $LXC
10: lxc35dd9f4e9602@if9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc noqueue state UP group default qlen 1000
link/ether ea:68:2a:30:09:bf brd ff:ff:ff:ff:ff:ff link-netns cni-3e105af7-2b32-fe63-a985-50e366811b8b
inet6 fe80::e868:2aff:fe30:9bf/64 scope link
valid_lft forever preferred_lft forever

# list of eBPF programs
c0bpf net show
c0bpf net show | grep $LXC
lxc35dd9f4e9602(10) tcx/ingress cil_from_container prog_id 1360 link_id 23
lxc35dd9f4e9602(10) tcx/egress cil_to_container prog_id 1362 link_id 24
# Use bpftool prog show id to view additional information about a program, including a list of attached eBPF maps:
c0bpf prog show id <출력된 prog id 입력>
c0bpf prog show id 1529
1360: sched_cls name cil_from_container tag 0d99c2edeb2964bd gpl
loaded_at 2024-10-23T14:10:08+0000 uid 0
xlated 728B jited 545B memlock 4096B map_ids 176,66
btf_id 348

c0bpf map list
...
66: percpu_hash name cilium_metrics flags 0x1
key 8B value 16B max_entries 1024 memlock 19384B
...
176: prog_array name cilium_calls_00 flags 0x0
key 4B value 4B max_entries 50 memlock 720B
owner_prog_type sched_cls owner jited
...
그림 왼쪽(네트워크 기반 로드밸런싱) vs 오른쪽(소켓 기반 로드밸런싱)

Pod1 안에서 동작하는 앱이 connect() 시스템콜을 이용하여 소켓을 연결할 때 목적지 주소가 서비스 주소(10.10.8.55)이면 소켓의 목적지 주소를 바로 백엔드 주소(10.0.0.31)로 설정한다. 이후 앱에서 해당 소켓을 통해 보내는 모든 패킷의 목적지 주소는 이미 백엔드 주소(10.0.0.31)로 설정되어 있기 때문에 중간에 DNAT 변환 및 역변환 과정이 필요없어진다.

Socket operations : BPF socket operations program 은 root cgroup 에 연결되며 TCP event(ESTABLISHED) 에서 실행한다.
Socket send/recv : The socket send/recv hook 은 TCP socket 의 모든 송수신 작업에서 실행, hook 에서 검사/삭제/리다이렉션을 할 수 있다

connect() 와 sendto() 소켓 함수에 연결된 프로그램(connect4, sendmsg4)에서는 소켓의 목적지 주소를 백엔드 주소와 포트로 변환하고, cilium_lb4_backends 맵에 백엔드 주소와 포트를 등록해놓는다. 이후 recvmsg() 소켓 함수에 연결된 프로그램(recvmsg4)에서는 cilium_lb4_reverse_nat 맵을 이용해서 목적지 주소와 포트를 다시 서비스 주소와 포트로 변환함

cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Service
metadata:
name: svc
spec:
ports:
- name: svc-webport
port: 80
targetPort: 80
selector:
app: webpod
type: ClusterIP
EOF
# 서비스 생성 확인
kubectl get svc,ep svc
# 노드에 iptables 더이상 KUBE-SVC rule 이 생성되지 않는다!
iptables-save | grep KUBE-SVC
iptables-save | grep CILIUM
# 서비스IP를 변수에 지정
SVCIP=$(kubectl get svc svc -o jsonpath='{.spec.clusterIP}')
# Pod1 에서 Service(ClusterIP) 접속 트래픽 발생
kubectl exec netpod -- curl -s $SVCIP
kubectl exec netpod -- curl -s $SVCIP | grep Hostname
# 지속적으로 접속 트래픽 발생
SVCIP=$(kubectl get svc svc -o jsonpath='{.spec.clusterIP}')
while true; do kubectl exec netpod -- curl -s $SVCIP | grep Hostname;echo "-----";sleep 1;done
# 파드에서 SVC(ClusterIP) 접속 시 tcpdump 로 확인 >> 파드 내부 캡쳐인데, SVC(10.108.12.195)는 보이지 않고, DNAT 된 web-pod 의 IP가 확인! Magic!
kubectl exec netpod -- tcpdump -enni any -q
08:54:55.454271 eth0 Out ifindex 14 92:1a:b9:94:94:37 172.16.0.162.44718 > 172.16.1.234.80: tcp 0
08:54:55.454798 eth0 In ifindex 14 8a:0c:cc:a9:21:1a 172.16.1.234.80 > 172.16.0.162.44718: tcp 0
08:54:55.455030 eth0 Out ifindex 14 92:1a:b9:94:94:37 172.16.0.162.44718 > 172.16.1.234.80: tcp 77
...
service ip인 10.10.214.64가 tcpdump log에서 확인되지 않는다!

kubectl exec netpod -- sh -c "ngrep -tW byline -d eth0 '' 'tcp port 80'"
T 2024/10/20 08:07:36.663329 172.16.0.132:59964 -> 172.16.1.53:80 [AP] #34
GET / HTTP/1.1.
Host: 10.10.124.15.
User-Agent: curl/8.7.1.
Accept: */*.

# 서비스 정보 확인
c0 service list
ID Frontend Service Type Backend
15 10.10.214.64:80 ClusterIP 1 => 172.16.2.104:80 (active)
2 => 172.16.1.47:80 (active)
c0 bpf lb list
SERVICE ADDRESS BACKEND ADDRESS
10.108.12.195:80 0.0.0.0:0 (15) (0) [ClusterIP, non-routable]
172.16.2.104:80 (15) (1)
172.16.1.47:80 (15) (2)
# BPF maps
c0 map list --verbose
c0 map list --verbose | grep lb
c0 map get cilium_lb4_services_v2
c0 map get cilium_lb4_backends_v3
c0 map get cilium_lb4_reverse_nat
c0 map get cilium_lb4_reverse_sk
c0 map get cilium_lxc
c0 map get cilium_ipcache
# syacall 호출 확인
kubectl exec netpod -- strace -c curl -s $SVCIP
Hostname: webpod2
IP: 127.0.0.1
IP: ::1
IP: 172.16.2.104
IP: fe80::54ef:65ff:fef5:67fd
RemoteAddr: 172.16.0.235:45872
GET / HTTP/1.1
Host: 10.10.214.64
User-Agent: curl/8.7.1
Accept: */*
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
53.56 0.003825 273 14 mprotect
18.74 0.001338 16 79 mmap
5.81 0.000415 7 56 32 open
5.42 0.000387 14 27 munmap
5.10 0.000364 11 31 lseek
2.35 0.000168 7 23 fcntl
2.30 0.000164 6 27 read
1.32 0.000094 94 1 1 connect
1.16 0.000083 3 27 close
0.95 0.000068 17 4 setsockopt
0.80 0.000057 4 12 fstat
0.73 0.000052 1 31 rt_sigaction
0.39 0.000028 28 1 pipe
0.35 0.000025 25 1 socket
0.24 0.000017 1 10 readv
0.15 0.000011 1 6 poll
0.14 0.000010 5 2 geteuid
0.11 0.000008 2 3 brk
0.10 0.000007 1 4 getsockname
0.08 0.000006 6 1 getuid
0.07 0.000005 1 5 getrandom
0.07 0.000005 5 1 getgid
0.06 0.000004 4 1 getegid
0.00 0.000000 0 1 writev
0.00 0.000000 0 3 3 ioctl
0.00 0.000000 0 1 sendto
0.00 0.000000 0 1 recvfrom
0.00 0.000000 0 1 getsockopt
0.00 0.000000 0 1 execve
0.00 0.000000 0 12 rt_sigprocmask
0.00 0.000000 0 1 arch_prctl
0.00 0.000000 0 1 set_tid_address
------ ----------- ----------- --------- --------- ----------------
100.00 0.007141 18 389 36 total
# 출력 내용을 편집기에서 확인(검색)
kubectl exec netpod -- strace -s 65535 -f -tt curl -s $SVCIP
------------------------------------------------------------
15:49:48.336836 connect(5, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("10.10.214.64")}, 16) = -1 EINPROGRESS (Operation in progress)
15:49:48.337352 getsockname(5, {sa_family=AF_INET, sin_port=htons(34530), sin_addr=inet_addr("172.16.0.235")}, [128 => 16]) = 0# 소켓 주소 가져오기
...
15:49:48.339291 getsockopt(5, SOL_SOCKET, SO_ERROR, [0], [4]) = 0
15:49:48.339406 getsockname(5, {sa_family=AF_INET, sin_port=htons(34530), sin_addr=inet_addr("172.16.0.235")}, [128 => 16]) = 0# 소켓 주소 한번더 확인
...

# 특정 이벤트 : -e
kubectl exec netpod -- strace -e trace=connect curl -s $SVCIP
kubectl exec netpod -- strace -e trace=getsockname curl -s $SVCIP

# 배포
kubectl apply -f https://raw.githubusercontent.com/cilium/cilium/1.16.3/examples/kubernetes/addons/prometheus/monitoring-example.yaml
kubectl get all -n cilium-monitoring
# 파드와 서비스 확인
kubectl get pod,svc,ep -o wide -n cilium-monitoring
# NodePort 설정
kubectl patch svc grafana -n cilium-monitoring -p '{"spec": {"type": "NodePort"}}'
kubectl patch svc prometheus -n cilium-monitoring -p '{"spec": {"type": "NodePort"}}'
# Grafana 웹 접속
GPT=$(kubectl get svc -n cilium-monitoring grafana -o jsonpath={.spec.ports[0].nodePort})
echo -e "Grafana URL = http://$(curl -s ipinfo.io/ip):$GPT"
# Prometheus 웹 접속 정보 확인
PPT=$(kubectl get svc -n cilium-monitoring prometheus -o jsonpath={.spec.ports[0].nodePort})
echo -e "Prometheus URL = http://$(curl -s ipinfo.io/ip):$PPT"
Grafana URL = http://43.201.15.39:30123
Prometheus URL = http://43.201.15.39:30503
Prometheus 웹 접속 정보 확인

Grafana 웹 접속


cilium은 다음과 같이 굉장히 다양한 보안 기능들을 제공합니다.
레이어 3/4 (L3/L4) 네트워크 보안 정책
• Cilium은 IP 주소 및 포트 기반의 L3/L4 레벨 네트워크 정책을 정의하고 관리할 수 있습니다. 이 정책을 통해, 특정 포드나 애플리케이션이 허용된 IP와 포트 범위 내에서만 통신할 수 있게 제어하여, 트래픽을 필터링하고, 승인되지 않은 네트워크 접근을 차단합니다.
• 예를 들어, 특정 포드가 특정 IP 주소와 특정 포트에서만 통신할 수 있도록 허용하거나 제한할 수 있습니다.
레이어 7 (L7) 애플리케이션 보안 정책
• Cilium은 HTTP, gRPC, Kafka, DNS와 같은 애플리케이션 레벨 트래픽(L7)에 대한 보안 정책도 지원합니다. 이를 통해 애플리케이션 프로토콜을 인식하고, 트래픽 패턴을 기반으로 세밀한 보안 제어를 할 수 있습니다.
• 예를 들어, 특정 API 엔드포인트나 HTTP 메서드(GET, POST 등)에 대한 접근을 제어하거나, 특정 Kafka 토픽에 대한 접근을 제한할 수 있습니다.
분산형 네트워크 정책
• Cilium은 Kubernetes 네트워크 정책(Kubernetes Network Policies) 및 Cilium 네트워크 정책을 사용하여, 컨테이너 간 트래픽 흐름을 세밀하게 제어할 수 있습니다. 이를 통해 클러스터 내부뿐만 아니라, 외부에서 들어오는 트래픽까지 관리할 수 있습니다.
• 정책은 포드의 레이블을 기반으로 설정할 수 있으며, 이를 통해 동적으로 변화하는 컨테이너 환경에서도 유연한 네트워크 보안 정책 적용이 가능합니다.
투명한 보안 정책 시행
• Cilium은 eBPF를 활용하여 리눅스 커널 내에서 직접 보안 정책을 적용합니다. 이를 통해 네트워크 스택에서 발생하는 추가적인 오버헤드 없이 보안 정책을 빠르고 효율적으로 시행할 수 있습니다. 이 방식은 특히 성능에 민감한 대규모 환경에서 유리합니다.
네트워크 정책 감사 및 가시성
• Hubble과 통합된 Cilium의 보안 기능은 네트워크 정책의 실시간 상태를 모니터링하고, 정책에 따라 트래픽이 허용되었는지 차단되었는지에 대한 감사 로그를 제공합니다. 이를 통해 네트워크 상에서 발생하는 모든 트래픽 흐름을 투명하게 분석할 수 있고, 보안 정책의 적용 상태를 실시간으로 파악할 수 있습니다.
• 트래픽이 왜 차단되었는지, 어떤 정책에 의해 허용되었는지 등의 정보를 쉽게 확인할 수 있어 보안 문제 진단 및 사고 대응이 신속하게 이루어집니다.
암호화된 네트워크 트래픽 (Encryption)
• Cilium은 IPSec이나 WireGuard를 이용한 네트워크 트래픽 암호화를 지원합니다. 이를 통해 노드 간의 네트워크 트래픽이 외부에서 읽히거나 변조되는 것을 방지할 수 있습니다.
• 암호화는 네트워크 레이어에서 투명하게 처리되며, 클러스터 내의 노드 간 통신에 추가적인 보안 계층을 제공합니다.
DDoS 공격 방어 및 패킷 필터링
• Cilium은 DDoS 공격이나 트래픽 폭주와 같은 상황에서 네트워크 트래픽을 세밀하게 필터링하고, 불필요한 트래픽을 효율적으로 차단할 수 있습니다. eBPF를 사용해 커널 레벨에서 트래픽을 실시간으로 모니터링하며, 특정 패턴의 트래픽을 빠르게 감지하고 차단할 수 있습니다.
• 이를 통해 대규모 공격으로부터 클러스터를 보호하고, 서비스의 가용성을 유지할 수 있습니다.
서비스 메시 보안
• Cilium은 서비스 메시와의 통합을 통해 L7 트래픽에 대한 보안 정책을 적용할 수 있습니다. Envoy와 같은 서비스 메시 프록시와 통합되어, 서비스 간 트래픽을 세밀하게 관리하고, 각 서비스 간의 보안 경계를 강화할 수 있습니다.
• 서비스 메시 환경에서 마이크로서비스 간 통신을 보호하고, 트래픽을 모니터링하여 보안 사고를 예방할 수 있습니다.
멀티 테넌시(Multi-tenancy) 보안
• Cilium은 여러 사용자나 애플리케이션이 동일한 네트워크 환경을 공유하는 경우에도, 각 테넌트 간의 네트워크 트래픽을 격리하여 보안을 유지합니다. 이 기능은 멀티 테넌시 환경에서 개별 테넌트 간에 트래픽이 서로 혼재되지 않도록 보호하는 데 필수적입니다.
ID 기반
이 기능은 레이어 3에서 Identity 기반의 접근 제어를 가능하게 합니다. 레이블은 애플리케이션의 역할, 환경, 위치 등을 나타내며, 이를 통해 네트워크 정책을 정의합니다. 예를 들어, role=frontend 레이블을 가진 모든 엔드포인트는 role=backend 레이블을 가진 엔드포인트로 연결할 수 있도록 설정할 수 있습니다.

포트 기반
Cilium은 Kubernetes에서 제공하는 기본 네트워크 정책도 포트 기반 정책을 지원하며 정책을 더 세밀하게 제어할 수 있도록 도와줍니다. 특히 Cilium은 L3/L4 네트워크 정책을 통해 IP 주소와 포트를 결합한 보안 정책을 만들 수 있습니다.
애플리케이션 기반
Cilium은 eBPF 기술을 사용하여 이러한 애플리케이션 레벨(L7) 트래픽을 제어할 수 있습니다. HTTP, gRPC, Kafka, DNS와 같은 다양한 애플리케이션 프로토콜에 대해 세밀한 정책을 설정할 수 있으며, 이를 통해 특정 API 호출이나 애플리케이션 간의 통신을 더 정밀하게 제어할 수 있습니다.
Cilium에서는 이러한 L7 기반 정책을 eBPF와 Envoy 프록시를 활용하여 구현합니다.

트래픽 가로채기 (eBPF 기반 트래픽 인식)
• 네트워크를 통해 들어오는 트래픽은 eBPF 프로그램에 의해 가로채집니다.
• Cilium은 이 단계에서 트래픽이 어떤 애플리케이션 프로토콜을 사용하는지 파악합니다. 예를 들어, HTTP 요청인지 Kafka 메시지인지 또는 다른 프로토콜인지를 인식할 수 있습니다.
• 이 트래픽이 L3/L4 기반 정책을 통과했을 경우, L7 정책이 존재하는지 확인한 후 프록시(Envoy)로 트래픽을 전달합니다.
프록시(Envoy)로 트래픽 전달
• Envoy는 트래픽을 애플리케이션 레벨에서 분석합니다. 예를 들어, HTTP 요청의 경우 메서드(GET, POST), 경로, 헤더 등을 분석하고, gRPC 호출의 경우 서비스와 메서드를 분석할 수 있습니다.
• Cilium은 Envoy 프록시를 통해 트래픽이 애플리케이션 기반 정책과 일치하는지 검사하고, 허용 또는 차단 여부를 결정합니다.
애플리케이션 정책 적용
• 트래픽이 분석된 후, Cilium이 정의한 애플리케이션 기반 정책이 적용됩니다.
• 예를 들어, 특정 HTTP 요청 경로로만 접근을 허용하거나, 특정 Kafka 토픽에 대한 메시지 읽기/쓰기를 제어하는 정책이 적용될 수 있습니다.
정책 결과에 따른 트래픽 허용 또는 차단
• Envoy 프록시가 트래픽을 분석한 결과, 정책에 맞으면 트래픽은 애플리케이션으로 전달됩니다.
• 정책에 어긋나면, 해당 트래픽은 차단되고 애플리케이션에 도달하지 않으며, 필요 시 로그로 기록됩니다.


eBPF Datapath는 커널에서 직접 트래픽을 처리하여, 사용자 공간에서 발생하는 성능 오버헤드를 최소화하면서 네트워크 트래픽을 제어하는 기술입니다. 전통적으로, 네트워크 정책을 적용하기 위해서는 IPTables 같은 툴을 통해 사용자 공간에서 패킷을 분석하고, 규칙을 적용한 후 다시 커널로 트래픽을 전달해야 했습니다. 이 과정에서 성능 문제가 발생할 수 있는데요. 그러나 eBPF Datapath는 이와 달리 네트워크 트래픽을 커널에서 실시간으로 분석하고 처리하여 네트워크 정책을 적용합니다. 이를 통해 성능을 최적화하면서도 보안을 강화할 수 있습니다.
스타워즈에서 영감 받은 예제 : 디플로이먼트(웹 서버, deathstar, replicas 2), 파드(xwing, tiefighter), 서비스(ClusterIP, service/deathstar)

# 배포
kubectl create -f https://raw.githubusercontent.com/cilium/cilium/1.16.3/examples/minikube/http-sw-app.yaml
kubectl get all
# 파드 라벨 확인
kubectl get pod --show-labels
NAME READY STATUS RESTARTS AGE LABELS
deathstar-689f66b57d-4rwkf 1/1 Running 0 113s app.kubernetes.io/name=deathstar,class=deathstar,org=empire,pod-template-hash=689f66b57d
deathstar-689f66b57d-8p2l5 1/1 Running 0 113s app.kubernetes.io/name=deathstar,class=deathstar,org=empire,pod-template-hash=689f66b57d
tiefighter 1/1 Running 0 113s app.kubernetes.io/name=tiefighter,class=tiefighter,org=empire
xwing 1/1 Running 0 113s app.kubernetes.io/name=xwing,class=xwing,org=alliance
# cilium endpoint 확인
kubectl get ciliumendpoints
NAME SECURITY IDENTITY ENDPOINT STATE IPV4 IPV6
deathstar-689f66b57d-9mbhb 19092 ready 172.16.2.21
deathstar-689f66b57d-dqgxq 19092 ready 172.16.1.10
tiefighter 49569 ready 172.16.1.252
xwing 22863 ready 172.16.1.104
c1 endpoint list

c2 endpoint list

# 데스스타 SVC(ClusterIP) 접속하여 웹 파드 연결 확인 >> Hubble UI 에서 실시간 확인해보자!
kubectl exec xwing -- curl -s -XPOST deathstar.default.svc.cluster.local/v1/request-landing
Ship landed
kubectl exec tiefighter -- curl -s -XPOST deathstar.default.svc.cluster.local/v1/request-landing
Ship landed
# 확인
hubble observe


# L3/L4 정책 생성
cat <<EOF | kubectl apply -f -
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: "rule1"
spec:
description: "L3-L4 policy to restrict deathstar access to empire ships only"
endpointSelector:
matchLabels:
org: empire
class: deathstar
ingress:
- fromEndpoints:
- matchLabels:
org: empire
toPorts:
- ports:
- port: "80"
protocol: TCP
EOF
# 정책 확인
kubectl get cnp
kc describe cnp rule1
c0 policy get
# 파드 curl 접속 시도 시 파드 sh 접속 후 curl 시도하자!
# 데스스타 SVC(ClusterIP) 접속하여 웹 파드 연결 확인 >> Hubble UI 에서 drop 확인!
kubectl exec tiefighter -- curl -s -XPOST deathstar.default.svc.cluster.local/v1/request-landing
Ship landed
kubectl exec xwing -- curl -s -XPOST deathstar.default.svc.cluster.local/v1/request-landing
drop

# hubble cli 모니터링
hubble observe --pod xwing
hubble observe --pod tiefighter
hubble observe --pod deathstar
Dec 2 05:36:24.490: default/xwing:55464 <> default/deathstar-c74d84667-t7msh:80 Policy denied DROPPED (TCP Flags: SYN)
Dec 2 05:36:24.490: default/xwing:55464 <> default/deathstar-c74d84667-t7msh:80 Policy denied DROPPED (TCP Flags: SYN)
hubble observe --pod deathstar --verdict DROPPED
Oct 23 16:39:02.846: default/xwing:46186 (ID:22863) <> default/deathstar-689f66b57d-9mbhb:80 (ID:19092) policy-verdict:none INGRESS DENIED (TCP Flags: SYN)
Oct 23 16:39:02.847: default/xwing:46186 (ID:22863) <> default/deathstar-689f66b57d-9mbhb:80 (ID:19092) Policy denied DROPPED (TCP Flags: SYN)
Oct 23 16:39:04.895: default/xwing:46186 (ID:22863) <> default/deathstar-689f66b57d-9mbhb:80 (ID:19092) policy-verdict:none INGRESS DENIED (TCP Flags: SYN)
Oct 23 16:39:04.895: default/xwing:46186 (ID:22863) <> default/deathstar-689f66b57d-9mbhb:80 (ID:19092) Policy denied DROPPED (TCP Flags: SYN)
Oct 23 16:39:08.926: default/xwing:46186 (ID:22863) <> default/deathstar-689f66b57d-9mbhb:80 (ID:19092) policy-verdict:none INGRESS DENIED (TCP Flags: SYN)
Oct 23 16:39:08.926: default/xwing:46186 (ID:22863) <> default/deathstar-689f66b57d-9mbhb:80 (ID:19092) Policy denied DROPPED (TCP Flags: SYN)
Oct 23 16:39:17.246: default/xwing:46186 (ID:22863) <> default/deathstar-689f66b57d-9mbhb:80 (ID:19092) policy-verdict:none INGRESS DENIED (TCP Flags: SYN)
Oct 23 16:39:17.246: default/xwing:46186 (ID:22863) <> default/deathstar-689f66b57d-9mbhb:80 (ID:19092) Policy denied DROPPED (TCP Flags: SYN)
Oct 23 16:39:33.631: default/xwing:46186 (ID:22863) <> default/deathstar-689f66b57d-9mbhb:80 (ID:19092) policy-verdict:none INGRESS DENIED (TCP Flags: SYN)
Oct 23 16:39:33.631: default/xwing:46186 (ID:22863) <> default/deathstar-689f66b57d-9mbhb:80 (ID:19092) Policy denied DROPPED (TCP Flags: SYN)
Oct 23 16:40:05.886: default/xwing:46186 (ID:22863) <> default/deathstar-689f66b57d-9mbhb:80 (ID:19092) policy-verdict:none INGRESS DENIED (TCP Flags: SYN)
Oct 23 16:40:05.886: default/xwing:46186 (ID:22863) <> default/deathstar-689f66b57d-9mbhb:80 (ID:19092) Policy denied DROPPED (TCP Flags: SYN)
Inspecting the Policy
# If we run cilium endpoint list again we will see that the pods with the label org=empire and class=deathstar
# now have ingress policy enforcement enabled as per the policy above.
# endpoint list 에서 정책 적용 확인
c1 endpoint list | grep deathstar
249 Enabled Disabled 19092 k8s:app.kubernetes.io/name=deathstar 172.16.1.10 ready
k8s:class=deathstar
c2 endpoint list
ENDPOINT POLICY (ingress) POLICY (egress) IDENTITY LABELS (source:key[=value]) IPv6 IPv4 STATUS
ENFORCEMENT ENFORCEMENT
312 Disabled Disabled 18300 k8s:class=xwing 172.16.2.161 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
k8s:org=alliance
1972 Enabled Disabled 21144 k8s:class=deathstar 172.16.2.66 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
k8s:org=empirec2 endpoint list

# 데스스타 SVC(ClusterIP) 접속
kubectl exec tiefighter -- curl -s -XPUT deathstar.default.svc.cluster.local/v1/exhaust-port
Panic: deathstar exploded
...
# POST /v1/request-landing API 호출만 허용 정책으로 기존 정책 내용을 업데이트(configured)!
cat <<EOF | kubectl apply -f -
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: "rule1"
spec:
description: "L7 policy to restrict access to specific HTTP call"
endpointSelector:
matchLabels:
org: empire
class: deathstar
ingress:
- fromEndpoints:
- matchLabels:
org: empire
toPorts:
- ports:
- port: "80"
protocol: TCP
rules:
http:
- method: "POST"
path: "/v1/request-landing"
EOF
# 정책 확인
kc describe ciliumnetworkpolicies
c0 policy get
# 모니터링
c1 monitor -v --type l7
c2 monitor -v --type l7
<- Request http from 0 ([k8s:io.cilium.k8s.policy.cluster=default k8s:io.cilium.k8s.policy.serviceaccount=default k8s:io.kubernetes.pod.namespace=default k8s:org=empire k8s:class=tiefighter k8s:io.cilium.k8s.namespace.labels.kubernetes.io/metadata.name=default]) to 1972 ([k8s:class=deathstar k8s:org=empire k8s:io.cilium.k8s.namespace.labels.kubernetes.io/metadata.name=default k8s:io.kubernetes.pod.namespace=default k8s:io.cilium.k8s.policy.serviceaccount=default k8s:io.cilium.k8s.policy.cluster=default]), identity 42720->21144, verdict Denied PUT http://deathstar.default.svc.cluster.local/v1/exhaust-port => 403
=> 403
hubble observe --pod deathstar
hubble observe --pod deathstar --verdict DROPPED
# 접근 테스트
kubectl exec tiefighter -- curl -s -XPOST deathstar.default.svc.cluster.local/v1/request-landing
Ship landed
kubectl exec tiefighter -- curl -s -XPUT deathstar.default.svc.cluster.local/v1/exhaust-port
Access denied
## hubble cli 에 차단 로그 확인
hubble observe --pod deathstar --verdict DROPPED
Oct 23 16:50:02.603: default/tiefighter:59092 (ID:49569) -> default/deathstar-689f66b57d-dqgxq:80 (ID:19092) http-request DROPPED (HTTP/1.1 PUT http://deathstar.default.svc.cluster.local/v1/exhaust-port)
hubble observe --pod deathstar --protocol http
Oct 23 16:50:02.603: default/tiefighter:59092 (ID:49569) -> default/deathstar-689f66b57d-dqgxq:80 (ID:19092) http-request DROPPED (HTTP/1.1 PUT http://deathstar.default.svc.cluster.local/v1/exhaust-port)


kubernetes.io/egress-bandwidth Pod annotation which is enforced on egress at the native host networking devices.~~kubernetes.io/ingress-bandwidth~~ annotation is not supported
# 인터페이스 tc qdisc 확인
tc qdisc show dev ens5
qdisc mq 0: root
qdisc fq_codel 0: parent :4 limit 10240p flows 1024 quantum 1514 target 5ms interval 100ms memory_limit 32Mb ecn drop_batch 64
qdisc fq_codel 0: parent :3 limit 10240p flows 1024 quantum 1514 target 5ms interval 100ms memory_limit 32Mb ecn drop_batch 64
qdisc fq_codel 0: parent :2 limit 10240p flows 1024 quantum 1514 target 5ms interval 100ms memory_limit 32Mb ecn drop_batch 64
qdisc fq_codel 0: parent :1 limit 10240p flows 1024 quantum 1514 target 5ms interval 100ms memory_limit 32Mb ecn drop_batch 64
# 설정
helm upgrade cilium cilium/cilium --namespace kube-system --reuse-values --set bandwidthManager.enabled=true
# 적용 확인
cilium config view | grep bandwidth
enable-bandwidth-manager true
# egress bandwidth limitation 동작하는 인터페이스 확인
c0 status | grep BandwidthManager
BandwidthManager: EDT with BPF [CUBIC] [ens5]
# 인터페이스 tc qdisc 확인 : 설정 전후 옵션값들이 상당히 추가된다
tc qdisc
tc qdisc show dev ens5
qdisc mq 8002: root
qdisc fq 8005: parent 8002:2 limit 10000p flow_limit 100p buckets 32768 orphan_mask 1023 quantum 18030b initial_quantum 90150b low_rate_threshold 550Kbit refill_delay 40ms timer_slack 10us horizon 2s horizon_drop
qdisc fq 8003: parent 8002:4 limit 10000p flow_limit 100p buckets 32768 orphan_mask 1023 quantum 18030b initial_quantum 90150b low_rate_threshold 550Kbit refill_delay 40ms timer_slack 10us horizon 2s horizon_drop
qdisc fq 8004: parent 8002:3 limit 10000p flow_limit 100p buckets 32768 orphan_mask 1023 quantum 18030b initial_quantum 90150b low_rate_threshold 550Kbit refill_delay 40ms timer_slack 10us horizon 2s horizon_drop
qdisc fq 8006: parent 8002:1 limit 10000p flow_limit 100p buckets 32768 orphan_mask 1023 quantum 18030b initial_quantum 90150b low_rate_threshold 550Kbit refill_delay 40ms timer_slack 10us horizon 2s horizon_drop
# 테스트를 위한 트래픽 발생 서버/클라이언트 파드 생성
cat <<EOF | kubectl apply -f -
---
apiVersion: v1
kind: Pod
metadata:
annotations:
# Limits egress bandwidth to 10Mbit/s.
kubernetes.io/egress-bandwidth: "10M"
labels:
# This pod will act as server.
app.kubernetes.io/name: netperf-server
name: netperf-server
spec:
containers:
- name: netperf
image: cilium/netperf
ports:
- containerPort: 12865
---
apiVersion: v1
kind: Pod
metadata:
# This Pod will act as client.
name: netperf-client
spec:
affinity:
# Prevents the client from being scheduled to the
# same node as the server.
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app.kubernetes.io/name
operator: In
values:
- netperf-server
topologyKey: kubernetes.io/hostname
containers:
- name: netperf
args:
- sleep
- infinity
image: cilium/netperf
EOF
# egress BW 제한 정보 확인
kubectl describe pod netperf-server | grep Annotations:
Annotations: kubernetes.io/egress-bandwidth: 10M
# egress BW 제한이 설정된 파드가 있는 cilium pod 에서 제한 정보 확인
c1 bpf bandwidth list
c2 bpf bandwidth list
IDENTITY EGRESS BANDWIDTH (BitsPerSec)
904 10M
c1 endpoint list
c2 endpoint list
ENDPOINT POLICY (ingress) POLICY (egress) IDENTITY LABELS (source:key[=value]) IPv6 IPv4 STATUS
ENFORCEMENT ENFORCEMENT
904 Disabled Disabled 21565 k8s:app.kubernetes.io/name=netperf-server 172.16.2.153 ready
# 트래픽 발생 >> Hubble UI 에서 확인
# egress traffic of the netperf-server Pod has been limited to 10Mbit per second.
NETPERF_SERVER_IP=$(kubectl get pod netperf-server -o jsonpath='{.status.podIP}')
kubectl exec netperf-client -- netperf -t TCP_MAERTS -H "${NETPERF_SERVER_IP}"
Recv Send Send
Socket Socket Message Elapsed
Size Size Size Time Throughput
bytes bytes bytes secs. 10^6bits/sec
131072 16384 16384 10.04 9.88

# 5M 제한 설정 후 테스트
kubectl get pod netperf-server -o json | sed -e 's|10M|5M|g' | kubectl apply -f -
c1 bpf bandwidth list
c2 bpf bandwidth list
kubectl exec netperf-client -- netperf -t TCP_MAERTS -H "${NETPERF_SERVER_IP}"
Recv Send Send
Socket Socket Message Elapsed
Size Size Size Time Throughput
bytes bytes bytes secs. 10^6bits/sec
131072 16384 16384 10.09 4.56 # 4.5Mbps 제한 확인!

# 20M 제한 설정 후 테스트
kubectl get pod netperf-server -o json | sed -e 's|5M|20M|g' | kubectl apply -f -
kubectl exec netperf-client -- netperf -t TCP_MAERTS -H "${NETPERF_SERVER_IP}"
Recv Send Send
Socket Socket Message Elapsed
Size Size Size Time Throughput
bytes bytes bytes secs. 10^6bits/sec
131072 16384 16384 10.00 18.95 # 19Mbps 제한 확인!

tc qdisc show dev ens5
qdisc mq 8002: root
qdisc fq 8005: parent 8002:2 limit 10000p flow_limit 100p buckets 32768 orphan_mask 1023 quantum 18030b initial_quantum 90150b low_rate_threshold 550Kbit refill_delay 40ms timer_slack 10us horizon 2s horizon_drop
qdisc fq 8003: parent 8002:4 limit 10000p flow_limit 100p buckets 32768 orphan_mask 1023 quantum 18030b initial_quantum 90150b low_rate_threshold 550Kbit refill_delay 40ms timer_slack 10us horizon 2s horizon_drop
qdisc fq 8004: parent 8002:3 limit 10000p flow_limit 100p buckets 32768 orphan_mask 1023 quantum 18030b initial_quantum 90150b low_rate_threshold 550Kbit refill_delay 40ms timer_slack 10us horizon 2s horizon_drop
qdisc fq 8006: parent 8002:1 limit 10000p flow_limit 100p buckets 32768 orphan_mask 1023 quantum 18030b initial_quantum 90150b low_rate_threshold 550Kbit refill_delay 40ms timer_slack 10us horizon 2s horizon_drop
# 삭제
kubectl delete pod netperf-client netperf-server

#
helm upgrade cilium cilium/cilium --namespace kube-system --reuse-values \
--set l2announcements.enabled=true --set externalIPs.enabled=true \
--set l2announcements.leaseDuration=3s --set l2announcements.leaseRenewDeadline=1s --set l2announcements.leaseRetryPeriod=200ms
#
c0 config --all |grep L2
EnableL2Announcements : true
EnableL2NeighDiscovery : true
L2AnnouncerLeaseDuration : 3000000000
L2AnnouncerRenewDeadline : 1000000000
L2AnnouncerRetryPeriod : 200000000
# CiliumL2AnnouncementPolicy 생성
cat <<EOF | kubectl apply -f -
apiVersion: "cilium.io/v2alpha1"
kind: CiliumL2AnnouncementPolicy
metadata:
name: policy1
spec:
serviceSelector:
matchLabels:
color: blue
nodeSelector:
matchExpressions:
- key: node-role.kubernetes.io/control-plane
operator: DoesNotExist
interfaces:
- ^ens[0-9]+
externalIPs: true
loadBalancerIPs: true
EOF
# 확인
kubectl get ciliuml2announcementpolicy
kc describe l2announcement
#
cat <<EOF | kubectl apply -f -
apiVersion: "cilium.io/v2alpha1"
kind: CiliumLoadBalancerIPPool
metadata:
name: "cilium-pool"
spec:
allowFirstLastIPs: "No"
blocks:
- cidr: "10.10.200.0/29"
EOF
# cilium ip pool 조회
kubectl get CiliumLoadBalancerIPPool
NAME DISABLED CONFLICTING IPS AVAILABLE AGE
cilium-pool false False 3 3m5s
#
cat <<EOF | kubectl apply -f -
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
---
apiVersion: v1
kind: Service
metadata:
name: svc1
spec:
ports:
- name: svc1-webport
port: 80
targetPort: 80
selector:
app: webpod
type: LoadBalancer # 서비스 타입이 LoadBalancer
---
apiVersion: v1
kind: Service
metadata:
name: svc2
spec:
ports:
- name: svc2-webport
port: 80
targetPort: 80
selector:
app: webpod
type: LoadBalancer
---
apiVersion: v1
kind: Service
metadata:
name: svc3
spec:
ports:
- name: svc3-webport
port: 80
targetPort: 80
selector:
app: webpod
type: LoadBalancer
EOF
#
kubectl get svc,ep
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.10.0.1 <none> 443/TCP 5h20m
service/svc1 LoadBalancer 10.10.226.228 10.10.200.1 80:32693/TCP 5m30s
service/svc2 LoadBalancer 10.10.166.59 10.10.200.2 80:30107/TCP 5m30s
service/svc3 LoadBalancer 10.10.106.144 10.10.200.3 80:31564/TCP 5m30s
NAME ENDPOINTS AGE
endpoints/kubernetes 192.168.10.10:6443 5h20m
endpoints/svc1 172.16.1.52:80,172.16.2.196:80 5m30s
endpoints/svc2 172.16.1.52:80,172.16.2.196:80 5m30s
endpoints/svc3 172.16.1.52:80,172.16.2.196:80 5m30s
#
curl -s 10.10.200.1
curl -s 10.10.200.2
curl -s 10.10.200.3
