eBPF(extended Berkeley Packet Filter)
eBPF는 샌드박스(Sandbox) 기술을 사용해서 커널에서 안전하게 사용자 프로그램을 실행할 수 있도록 하는 기술입니다. eBPF 프로그램은 JIT(Just-In-Time) 컴파일러에 의해 런타임에서 프로그램을 커널에 로드하고, 빠르게 동작시킬 수 있습니다. 또한 JIT 컴파일러를 거치기 전에, Verifier를 통해 검증되기 때문에, 프로그램이 커널에 손상을 주거나 시스템을 불안정하게 만들 가능성을 줄입니다.
얼마 전, 크라우드 스트라이크 사태에서도, 윈도우의 업데이트 패치에서 커널을 잘못건드려 전 세계 시스템이 마비가 된 적이 있습니다. 이처럼 커널을 제어하는 것은 매우 어려운 일이며, 안전하고 빠른 프로그램을 만들 수 있는 eBPF가 대두되기도 했습니다. 실제로 마이크로소프트에서도 리눅스가 아닌 윈도우에서도 eBPF를 사용할 수 있도록 하는 프로젝트를 진행 중인 것으로 알고 있습니다.
eBPF는 원래 BPF(Berkeley Packet Filter)에서의 패킷 필터링 기능을 확장해, 커널의 여러 부분에서 유연하고 안전한 프로그래밍을 가능하게 만들었습니다. 덕분에 네트워킹뿐만 아니라 보안, 성능 모니터링, 시스템 트레이싱 등 다양한 분야에서 활용되고 있으며, 클라우드 네이티브 환경의 핵심 기술로 자리 잡았습니다. eBPF는 커널 개발의 복잡성과 어려움을 해결할 뿐만 아니라, 오픈소스인 리눅스 커널에 기여하는 데 필요한 긴 시간과 복잡한 과정을 크게 줄여주었기 때문에, 소프트웨어의 발전을 크게 앞당겼습니다.
XDP(eXpress Data Path)
XDP는 리눅스 커널의 고속 네트워킹 프레임워크로 네트워킹 드라이버의 가장 앞 단에서 XDP eBPF hook을 통해 BPF 프로그램을 트리거하기 떄문에, 패킷을 매우 빠르게 처리할 수 있습니다.
기존 리눅스 네트워크 스택은 단계가 복잡하고, 방화벽 규칙, 라우팅, NAT 등을 거치면서, 지연 시간이 길어질 수 있습니다. iptables의 경우, 규칙에 일치할 때까지 모든 규칙을 다 검사해야 하는데, 서비스가 많아지면 많아질수록 병목을 불러옵니다.
XDP는 eBPF를 통해, iptables를 사용하지 않고 패킷을 처리하여 문제점을 해결할 수 있습니다. NIC 드라이버 레벨에서 패킷을 빠르게 필터링하고 드롭할 수 있어, 대량의 트래픽을 효율적으로 처리해야 하는 CloudFlare와 같은 큰 회사에서도 고가의 방화벽 장비대신 XDP 기술을 통해서 DDos 방어등에 활용하고 있다고 합니다.
네트워크 패킷이 유저스페이스에서 커널 스택을 거쳐 NIC로 가는 경로에서 eBPF 프로그램이 어떻게 다양한 위치에서 필터링과 트래픽 제어를 수행하는지 보여줍니다.
Cilium에서 XDP를 활용하면 다른 네트워크 기술 대비, 압도적으로 빠른 성능을 제공할 수 있는 것을 확인할 수 있습니다.
XDP를 사용하려면 XDP를 지원하는 NIC를 사용하여야 합니다. Network drive를 확인하여 XDP를 지원하는 NIC인지 확인합니다.
# Native XDP 지원 NIC 확인 : https://docs.cilium.io/en/stable/bpf/progtypes/#xdp-drivers
ethtool -i ens5
driver: ena
version: 6.8.0-1015-aws
firmware-version:
...
Cilium 소개
Cilium은 eBPF 기반의 Kubernetes 네트워크와 보안을 제공하는 CNI Plugin입니다. eBPF를 통해 커널 레벨 직접 트래픽을 처리해 낮은 오버헤드(커널과 유저스페이스 간)와 높은 성능을 제공합니다. kube-proxy를 100% 대체할 수 있으며, iptables를 거의 사용하지 않아도 동작합니다. (2021년 10월 cilium은 CNCF에 Join)
Cilium 설치
helm-chart를 통해 cilium을 배포합니다. 환경에 맞게 아래 파라미터에 설정합니다.
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 pod를 확인합니다.
- Cilium Agent : 데몬셋으로 실행, K8S API 설정으로부터 '네트워크 설정, 네트워크 정책, 서비스 부하분산, 모니터링' 등을 수행하며, eBPF 프로그램을 관리합니다.
- Cilium Client (CLI) : Cilium 커멘드툴, eBPF maps에 직접 접속하여 상태를 확인할 수 있습니다.
- Cilium Operator : K8S 클러스터에 대한 한 번씩 처리해야 하는 작업을 관리합니다.
- Hubble : 네트워크와 보안 모니터링 플랫폼 역할을 하여, 'Server, Relay, Client, Graphical UI'로 구성되어 있습니다.
- Data Store : Cilium Agent 간의 상태를 저장하고 전파하는 데이터 저장소, 2가지 종류 중 선택(K8S CRDs, Key-Value Store)합니다.
(⎈|jlab:default) root@k8s-s:~# kubectl get pod -n kube-system
NAME READY STATUS
cilium-47ckt 1/1 Running
cilium-4xlzh 1/1 Running
cilium-envoy-5cv7l 1/1 Running
cilium-envoy-5lgfd 1/1 Running
cilium-envoy-pv4rs 1/1 Running
cilium-operator-76bb588dbc-qpcmz 1/1 Running
cilium-qg992 1/1 Running
coredns-55cb58b774-5cdff 1/1 Running
coredns-55cb58b774-h56tw 1/1 Running
etcd-k8s-s 1/1 Running
hubble-relay-88f7f89d4-dwk4j 1/1 Running
hubble-ui-59bb4cb67b-2dmwg 2/2 Running
kube-apiserver-k8s-s 1/1 Running
kube-controller-manager-k8s-s 1/1 Running
kube-scheduler-k8s-s 1/1 Running
Cilium crd를 확인합니다.
- CRDs: cilium이 관리하는 다양한 네트워크 보안 관련 CRD
- ciliumnodes: cilium의 연결된 클러스터 및 네트워크 정보
- ciliumendpoins: 파드의 네트워크 연결 상태 정보
- securiry: 파드별로 부여되는 보안 ID로, 이를 통해 네트워크 정책을 효율적으로 관리
(⎈|jlab:kube-system) root@k8s-s:~# kubectl get crd
NAME CREATED AT
ciliumcidrgroups.cilium.io 2024-10-23T10:48:26Z
ciliumclusterwidenetworkpolicies.cilium.io 2024-10-23T10:48:27Z
ciliumendpoints.cilium.io 2024-10-23T10:48:26Z
ciliumexternalworkloads.cilium.io 2024-10-23T10:48:26Z
ciliumidentities.cilium.io 2024-10-23T10:48:26Z
ciliuml2announcementpolicies.cilium.io 2024-10-23T10:48:26Z
ciliumloadbalancerippools.cilium.io 2024-10-23T10:48:26Z
ciliumnetworkpolicies.cilium.io 2024-10-23T10:48:26Z
ciliumnodeconfigs.cilium.io 2024-10-23T10:48:26Z
ciliumnodes.cilium.io 2024-10-23T10:48:26Z
ciliumpodippools.cilium.io 2024-10-23T10:48:26Z
(⎈|jlab:kube-system) root@k8s-s:~# kubectl get ciliumnodes
NAME CILIUMINTERNALIP INTERNALIP AGE
k8s-s 172.16.0.180 192.168.10.10 2d18h
k8s-w1 172.16.1.181 192.168.10.101 2d18h
k8s-w2 172.16.2.187 192.168.10.102 2d18h
(⎈|jlab:kube-system) root@k8s-s:~# kubectl get ciliumendpoints -A
NAMESPACE NAME SECURITY IDENTITY ENDPOINT STATE IPV4 IPV6
kube-system coredns-55cb58b774-5cdff 65291 ready 172.16.1.237
kube-system coredns-55cb58b774-h56tw 65291 ready 172.16.1.191
kube-system hubble-relay-88f7f89d4-dwk4j 54604 ready 172.16.1.138
kube-system hubble-ui-59bb4cb67b-2dmwg 57454 ready 172.16.1.71
cilium-config
컨피그 맵을 통해서 cilium 다양한 설정 정보를 확인할 수 있습니다.
(⎈|jlab:kube-system) root@k8s-s:~# kubectl get cm -n kube-system cilium-config -o json | jq
{
"apiVersion": "v1",
"data": {
"agent-not-ready-taint-key": "node.cilium.io/agent-not-ready",
"arping-refresh-period": "30s",
"auto-direct-node-routes": "true",
"bpf-events-drop-enabled": "true",
"bpf-events-policy-verdict-enabled": "true",
"bpf-events-trace-enabled": "true",
"bpf-lb-acceleration": "disabled",
"bpf-lb-external-clusterip": "false",
"bpf-lb-map-max": "65536",
"bpf-lb-sock": "false",
"bpf-lb-sock-terminate-pod-connections": "false",
"bpf-map-dynamic-size-ratio": "0.0025",
"bpf-policy-map-max": "16384",
"bpf-root": "/sys/fs/bpf",
"cgroup-root": "/run/cilium/cgroupv2",
"cilium-endpoint-gc-interval": "5m0s",
"cluster-id": "0",
"cluster-name": "default",
"clustermesh-enable-endpoint-sync": "false",
"clustermesh-enable-mcs-api": "false",
"cni-exclusive": "true",
"cni-log-file": "/var/run/cilium/cilium-cni.log",
"controller-group-metrics": "write-cni-file sync-host-ips sync-lb-maps-with-k8s-services",
"custom-cni-conf": "false",
"datapath-mode": "veth",
"debug": "true",
"debug-verbose": "",
"direct-routing-skip-unreachable": "false",
"dnsproxy-enable-transparent-mode": "true",
"dnsproxy-socket-linger-timeout": "10",
cilium 네트워크 인터페이스 확인합니다.
Cilium의 네트워크는 아래와 같이 구성되어 있습니다.
컨테이너 간 통신에서는 각 컨테이너의 eth0
인터페이스는 lxc11, lxc22와 같은 가상 인터페이스를 통해 cilium_net으로 연결되며, cilium이 네트워크 정책을 관리하고 적용합니다.
외부네트워크와의 통신 cilium_host를 통해 트래픽이 물리적으로 인터페이스(eth0, eth1)로 전달되며, CIDR Gateway에서 마스커레이드 설정이 적용되어 외부와 통신이 이루어집니다.
정리하자면, 아래 네트워크 구조는 eBPF와 XDP를 통해 커널 스택에서 네트워크 트래픽이 처리되는 방식을 나타내며, 이를 통해서 고성능 네트워크 처리를 가능하게 합니다.
기존에 사용했던 Iptables 룰과 비교하면 확연하게 줄어든 것을 확인할 수 있습니다.
installNoConntrackIptablesRules=true
옵션을 적용하면 cilium은 pod 간 네트워크 트래픽을 conntract
테이블에 기록되지 않아, 빈번하게 통신되는 쿠버네티스 네트워크에서 오버헤드를 줄여, 성능을 최적화할 수 있습니다.
helm upgrade cilium cilium/cilium --namespace kube-system --reuse-values --set installNoConntrackIptablesRules=true
iptables 규칙에 notrack을
사용하여 Connection Tracking을 비활성화함을 확인할 수 있습니다.
conntrack 테이블을 확인해 보면 개수가 현저하게 줄어들었음을 확인할 수 있습니다. Cilium과 eBPF가 전통적인 네트워크 구성 방식에 비해 얼마나 효율적인지를 보여줍니다. 이러한 혁신적인 기술 덕에 앞으로 Cilium 사용이 증가될 것으로 보입니다.
(⎈|jlab:kube-system) root@k8s-s:~# conntrack -L | grep -v 2379 # etcd 서비스 포트를 제외
conntrack v1.4.6 (conntrack-tools): tcp 6 8 CLOSE src=127.0.0.1 dst=127.0.0.1 sport=37852 dport=10259 src=127.0.0.1 dst=127.0.0.1 sport=10259 dport=37852 [ASSURED] mark=0 use=1
tcp 6 6 CLOSE src=127.0.0.1 dst=127.0.0.1 sport=36936 dport=10257 src=127.0.0.1 dst=127.0.0.1 sport=10257 dport=36936 [ASSURED] mark=0 use=1
72 flow entries have been shown.
Cilium CLI
cilium cli 설치합니다.
# 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
명령어는 cilium의 다양한 리소스의 상태 정보를 한눈에 파악할 수 있습니다.
cilium config view
명렁어는 cilium의 다양한 설정 정보를 출력합니다.
(⎈|jlab:kube-system) root@k8s-s:~# cilium config view
agent-not-ready-taint-key node.cilium.io/agent-not-ready
arping-refresh-period 30s
auto-direct-node-routes true
bpf-events-drop-enabled true
bpf-events-policy-verdict-enabled true
bpf-events-trace-enabled true
... # 생략
cilium 데몬셋 파드 내에서 cilium 명령어를 사용할 수 있습니다. 편리한 실습을 위해 데몬셋 파드마다 alias를 설정합니다.
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"
k8s-s(control-plane)의 데몬셋 cilium 파드에서 cilium status 명령어를 확인합니다.
c0 status
KVStore: Ok Disabled
Kubernetes: Ok 1.30 (v1.30.5) [linux/arm64]
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 fe80::20c:29ff:fe11:2dca 192.168.72.128, eth1 192.168.10.10 fe80::20c:29ff:fe11:2dd4 (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,
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]
Controller Status: 20/20 healthy
Proxy Status: OK, ip 172.16.0.180, 0 redirects active on ports 10000-20000, Envoy: external
Global Identity Range: min 256, max 65535
Hubble: Ok Current/Max Flows: 4095/4095 (100.00%), Flows/s: 0.94 Metrics: Ok
Encryption: Disabled
Cluster health: 3/3 reachable (2024-10-26T06:21:20Z)
Modules Health: Stopped(0) Degraded(0) OK(39)
KubeProxyReplacement 설정은 kube-proxy를
사용하지 않고 통신하는 옵션입니다.
# 192.168.0.0/16 대역은 kube-proxy를 사용하지 않고, 라우팅
c0 status | grep KubeProxyReplacement
KubeProxyReplacement: True [eth0 fe80::20c:29ff:fe11:2dca 192.168.72.128, eth1 192.168.10.10 fe80::20c:29ff:fe11:2dd4 (Direct Routing)]
cilium은 BPF를 사용해 마스커레이드 작업을 처리할 수 있기 때문에, 기존에 NetFilter보다 eBPF에서 더 빠르게 처리할 수 있습니다.
c0 status --verbose | grep Masquerading
Masquerading: BPF [eth0, eth1] 192.168.0.0/16 [IPv4: Enabled, IPv6: Disabled]
Masquerade(마스커레이드)
외부 통신 시, 내부 네트워크 IP 주소가 외부에 그대로 노출되지 않도록 출발지 IP주소를 라우터나 게이트웨이의 IP주소로 변환하는 방식
NAT의 일부 기능으로 내부 내트워크를 보호와, 외부 네트워크와 통신을 하기 위해서 사용됩니다.
ipMasqAgent.enabled
옵션을 활성화하면 IP Masq Agent를 통해서, cilium은 Pod 트래픽이 외부 네트워크 통신 시에, IP 마스커레이드 규칙을 자동으로 설정하고 관리도 하도록 할 수 있습니다.
helm upgrade cilium cilium/cilium --namespace kube-system --reuse-values --set ipMasqAgent.enabled=true
masq(마스커레이드) 설정을 확인합니다.
cilium config view | grep -i masq
enable-bpf-masquerade true
enable-ip-masq-agent true
자주 사용하는 명령어는 다음과 같습니다.
Cilium 클러스터 내의 IP와 관련 보안 정보를 한눈에 확인할 수 있습니다.
(⎈|jlab:kube-system) root@k8s-s:~# c0 ip list
IP IDENTITY SOURCE
0.0.0.0/0 reserved:world
172.16.0.1/32 reserved:health
172.16.0.180/32 reserved:host
reserved:kube-apiserver
172.16.1.19/32 reserved:health
172.16.1.71/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.1.138/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
쿠버네티스 서비스와 엔드포인트 간의 연결 상태를 보여줍니다. 네트워크 구성과 로드 벨런싱 상태를 확인하는데 유용합니다.
(⎈|jlab:kube-system) root@k8s-s:~# c0 service list
ID Frontend Service Type Backend
1 10.10.0.1:443 ClusterIP 1 => 192.168.10.10:6443 (active)
2 10.10.115.219:443 ClusterIP 1 => 192.168.10.10:4244 (active)
3 10.10.108.29:80 ClusterIP 1 => 172.16.1.138:4245 (active)
4 10.10.113.105:80 ClusterIP 1 => 172.16.1.71:8081 (active)
5 10.10.0.10:9153 ClusterIP 1 => 172.16.1.191:9153 (active)
2 => 172.16.1.237:9153 (active)
6 10.10.0.10:53 ClusterIP 1 => 172.16.1.191:53 (active)
2 => 172.16.1.237:53 (active)
(⎈|jlab:kube-system) root@k8s-s:~#
cilium 네트워크에서 발생하는 트래픽 이벤트를 실시간으로 모니터링할 수 있습니다. 네트워크 문제 해결과 성능 최적화 시 유용합니다.
(⎈|jlab:kube-system) root@k8s-s:~# c0 monitor -v
Listening for events on 2 CPUs with 64x4096 of shared memory
Press Ctrl-C to quit
CPU 00: [pre-xlate-rev] cgroup_id: 7628 sock_cookie: 631510, dst [192.168.10.10]:45680 tcp
CPU 01: [pre-xlate-rev] cgroup_id: 7782 sock_cookie: 628189, dst [192.168.10.10]:6443 tcp
CPU 01: [pre-xlate-rev] cgroup_id: 7628 sock_cookie: 631511, dst [192.168.10.10]:45694 tcp
CPU 01: [pre-xlate-rev] cgroup_id: 12302 sock_cookie: 628190, dst [192.168.10.10]:6443 tcp
CPU 01: [pre-xlate-rev] cgroup_id: 7628 sock_cookie: 631512, dst [192.168.10.10]:45696 tcp
CPU 01: [pre-xlate-rev] cgroup_id: 8835 sock_cookie: 628191, dst [192.168.10.10]:6443 tcp
CPU 01: [pre-xlate-rev] cgroup_id: 7782 sock_cookie: 628192, dst [192.168.10.10]:6443 tcp
CPU 01: [pre-xlate-rev] cgroup_id: 7628 sock_cookie: 628193, dst [192.168.10.10]:45700 tcp
CPU 01: [pre-xlate-rev] cgroup_id: 11090 sock_cookie: 628194, dst [127.0.0.1]:40820 tcp
CPU 01: [pre-xlate-rev] cgroup_id: 7782 sock_cookie: 631513, dst [127.0.0.1]:10259 tcp
CPU 01: [pre-xlate-rev] cgroup_id: 7782 sock_cookie: 628195, dst [192.168.10.10]:6443 tcp
CPU 01: [pre-xlate-rev] cgroup_id: 7628 sock_cookie: 628196, dst [192.168.10.10]:45716 tcp
그 외에도, 수많은 Cilium CLI를 제공합니다. 아래 CheetSheet를 통해 확인할 수 있습니다.
Cilium Hubble UI & CLI
Cilium Hubble은 Cilium의 네트워크 가시성 확장 도구로, eBPF를 통해 Kubernetes 클러스터 내에서 실시간 트래픽 모니터링과 정책 적용 상태를 시각화합니다. Cilium의 네트워크 보안 및 성능 관리 기능과 연계되어, 트래픽 흐름과 보안 상태를 직관적으로 파악할 수 있어 클러스터 운영과 최적화에 큰 도움을 줍니다.
eBPF를 통해 통신하게 되면 기존의 전통적인 네트워크를 관찰하는 모니터링 도구에서는 추적이 되지 않아 사용할 수 없게 되는데, Cilium에서 ebpf 모니터링 도구인 hubble를 제공해 주는 것은 큰 장점이라고 생각합니다.
hubble client 설치합니다. 4245 포트를 통해 hubble API를 접근하는데, Access 할 수 있도록 cilium hubble port-forward &
명령어를 통해 포트 포워딩 합니다.
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 &
# CLI 로 Hubble API 상태 확인 hubble status
hubble status
# 웹 접속을 위해 노드포트로 변경
kubectl patch -n kube-system svc hubble-ui -p '{"spec": {"type": "NodePort"}}'
웹을 통해 Hubble-UI에 접속하면 Istio의 Kilai Dasbhoard와 같이 트래픽의 흐름을 시각화하여 잘 보여줍니다.
UI 하단에서 트래픽 흐름을 목록을 보여주는데, hubble observe
명령어를 통해서 트래픽의 흐름을 상세하게 확인할 수 있습니다.
Cilium Prometheus & Grafana 배포
cilium 관련 메트릭 정보를 확인할 수 있는 prometheus와 grafana 또한 제공합니다.
# 배포
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"
Cilium 기본 통신(노드 간 파드 통신)
각 노드에 파드를 배포하여 노드 간 파드 끼리 통신을 확인합니다.
netpod에서 webpod1, webpod2로 ICMP Ping을 요청결과를 hubble observe 명령어에서 모니터링할 수 있습니다.
Cilium 통신 흐름 분석(Socket-Based LoadBalancing)
Cilium은 소켓 기반 로드벨런싱(Sokcet-based LoadBalancing)은 지원합니다. 기존의 네트워크 기반 로드벨런싱은 Service(10.10.0.31)로 통신할 때, 목적지 파드의 주소(10.0.0.31)로 DNAT 변환 및 역변환하는 과정이 있었는데, 소켓 기반 로드벨런싱의 경우, 파드 안에서 connect() 시스템콜을 통해 목적지의 파드의 IP주소를 바로 설정합니다.
전통적인 네트워크 기반 로드 벨런싱에서는 서비스 IP(10.10.8.55)로 들어온 트래픽을 파드 IP(10.0.0.32)로 변환(DNAT)하고, 응답 시, 다시 역변환(SNAT) 해야 합니다. 하지만, Cilium의 소켓 기반 로드 벨런싱에서는 트래픽이 eBPF를 사용해 커널 레벨에서 초기 단계에 로드벨런싱을 수행하여, 파드 앱 안에서 connect() 시스템 콜을 호출하여 직접 목적지 IP로 설정하게 합니다. 이를 통해 전통적인 DNAT/SNAT 오버헤드 없이 트래픽을 효율적으로 처리할 수 있어. 성능이 엄청나게 최적화됩니다.
실습을 통해 조금 더 살펴봅니다.
webpod의 서비스를 하나 생성합니다.
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
netpod에서 생성한 서비스의 ClusterIP로 지속적인 트래픽을 발생시키고 tcpdump로 통신흐름을 분석합니다.
# 지속적으로 접속 트래픽 발생
SVCIP=$(kubectl get svc svc -o jsonpath='{.spec.clusterIP}')
while true; do kubectl exec netpod -- curl -s $SVCIP | grep Hostname;echo "-----";sleep 1;done
netpod 내부의 캡처인데, 서비스의 Cluster-IP(10.10.16.140) IP는 보이지 않고, DNAT 된 webpod1,2의 IP 출력되는 것을 확인할 수 있습니다.
어떻게 이러한 통신이 가능한 것일까요? strace라는 시스템 콜 트레이싱 도구를 통해서 동작을 분석해 봅니다.
먼저, bfp 정보에 해당 서비스의 로드밸런싱 정보를 확인할 수 있습니다.
strace를 통해 netpod에서 서비스로 접속 시, 모든 시스템콜 출력하여 connect() 시스템 콜을 확인합니다.
kubectl exec netpod -- strace -s 65535 -f -tt curl -s $SVCIP
...
# coneect() 시스템콜 확인
12:02:35.317953 connect(5, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("10.10.16.140")}, 16) = -1 EINPROGRESS (Operation in progress)
12:02:35.320286 getsockname(5, {sa_family=AF_INET, sin_port=htons(48628), sin_addr=inet_addr("172.16.0.8")}, [128 => 16]) = 0
12:02:35.320977 rt_sigaction(SIGPIPE, {sa_handler=SIG_IGN, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0xffff9c49fe68}, NULL, 8) = 0
12:02:35.321404 ppoll([{fd=5, events=POLLOUT}, {fd=3, events=POLLIN}], 2, {tv_sec=1, tv_nsec=0}, NULL, 8) = 1 ([{fd=5, revents=POLLOUT}], left {tv_sec=0, tv_nsec=999998959})
...
아래 strace 결과는 소켓 기반 로드 벨런싱의 동작을 보여줍니다. connect()
시스템 콜을 통해 애플리케이션이 목적지 파드의 IP 주소로 직접 연결을 시도하고, getsocketname()을
통해 로컬 네트워크 인터페이스 IP와 포트 정보를 조회합니다. 이는 Cilium의 소켓 기반 로드 밸런싱이 DNAT/SNAT 없이 목적지 파드를 직접 지정하여 트래픽을 최적화하는 방식을 보여줍니다.
# -e 옵션: 특정 이벤트
kubectl exec netpod -- strace -e trace=connect curl -s $SVCIP
kubectl exec netpod -- strace -e trace=getsockname curl -s $SVCIP
# 결과
connect(5, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("10.10.16.140")}, 16) = -1 EINPROGRESS (Operation in progress)
getsockname(5, {sa_family=AF_INET, sin_port=htons(40102), sin_addr=inet_addr("172.16.0.8")}, [128 => 16]) = 0
Cilium NetworkPolicy(L3, L4, L7)
- 3가지의 보안 유형(L3, L4, L7)을 제공합니다.
- id
- port
- http
- L7 정책은 Userspace Proxy(envoy) 사용으로 성능이 조금 떨어짐
실습 예제를 배포합니다. 스타워즈를 영감으로 만들었다고 합니다.
kubectl create -f https://raw.githubusercontent.com/cilium/cilium/1.16.3/examples/minikube/http-sw-app.yaml
# 서비스 확인
kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
deathstar ClusterIP 10.10.215.162 80/TCP 23m
# 파드 확인
kubectl get pod --show-labels
NAME READY STATUS RESTARTS AGE LABELS
deathstar-689f66b57d-ptdwk 1/1 Running 0 17m app.kubernetes.io/name=deathstar,class=deathstar,org=empire,pod-template-hash=689f66b57d
deathstar-689f66b57d-zvxgc 1/1 Running 0 17m app.kubernetes.io/name=deathstar,class=deathstar,org=empire,pod-template-hash=689f66b57d
tiefighter 1/1 Running 0 17m app.kubernetes.io/name=tiefighter,class=tiefighter,org=empire
xwing 1/1 Running 0 17m app.kubernetes.io/name=xwing,class=xwing,org=alliance
구성은 다음과 같습니다. 각각 라벨링 된 wxing, tiefighter 파드가 deathstar의 서비스로 통신하는 구성입니다.
Identity-Aware and HTTP-Aware Policy Enforcement Apply an L3/L4 Policy
Cilium에서는 Endpoint IP 대신, 파드의 Label(라벨)을 사용하여 보안 정책을 적용합니다. 다음은 org=empire
라벨이 부착된 파드만 통신을 허용합니다.
CiliumNetworkPolicy
를 생성합니다. 특정 라벨을 가진 파드만 서비스에 접속하도록 L3/L4 정책을 설정합니다.
# 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
# cnp=CiliumNetworkPolicy의 약어
kubectl get cnp
NAME AGE
rule1 17m
deathstar의 서비스로 접속 시, org=empire
라벨이 있는 tiefighter 파드에서는 잘 통신되지만, xwing 파드에서는 Drop 되는 것을 확인될 수 있습니다.
Identity-Aware and HTTP-Aware Policy Enforcement Apply and Test HTTP-aware L7 Policy
HTTP L7 필터링을 적용 : 아래처럼 PUT /v1/exhaust-port 요청을 차단합니다.
CiliumNetworkPolicy를 생성합니다. 특정 라벨을 가진 파드에서만 포트 외에도 특정 path의 URL에 접근하도록 합니다.
# 생성 전 서비스 응답
kubectl exec tiefighter -- curl -s -XPUT deathstar.default.svc.cluster.local/v1/exhaust-port
Panic: deathstar exploded
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
deathstar 서비스의 v1/request-landing
, /v1/exhaust-port
로 접근하면, 같은 서비스(L3, L4)이지만, L7은 영역으로, 요청한 URL에 따라서 접근이 제한되는 것을 확인할 수 있습니다.
Cilium Bandwidth Manager
Bandwidth Manager는 eBPF를 이용하여 tc를 통해 커널 레벨에서 네트워크 트래픽을 효율적으로 제어하여, 대역폭 제한과 같은 QoS 기능을 수행할 수 있게 합니다. 파드의 kubernetes.io/egress-bandwidth
어노테이션으로 설정을 추가하면, 손쉽게 대역폭을 제한할 수 있습니다. 그리고 현재까지는 egress만 지원하고 있습니다.
TC(Traffic Control)
Linux 커널에서 네트워크 트래픽을 제어하고 관리하는 도구
bandwidthManager.enabled=true
옵션을 통해 bandwidthManger를 활성화합니다. tc 인터페이스를 확인해 보면 설정 전 후의 옵션들이 다른 것을 확인할 수 있습니다.
# 인터페이스 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
테스트를 위해 netperf 파드를 사용하여 서버/클라이언트 트래픽을 발생시켜 성능을 측정합니다. kubernetes.io/egress-bandwidth: "10M"
설정을 통해 netper-server의 대역폭 제한을 10M로 제한합니다.
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
10M 대역폭을 제한 후 테스트해 보면 9.88로 10M를 넘기지 않는 것을 확인할 수 있습니다.
대역폭을 10M -> 5M로 변경합니다.
kubectl get pod netperf-server -o json | sed -e 's|10M|5M|g' | kubectl apply -f -
다시 테스트해 보면, 5M를 넘기지 않는 것을 확인할 수 있습니다.
cilium bpf 명령어를 통해서도 설정된 대역폭을 확인할 수 있습니다.
Cilium L2 Announcements(metalLB 대안)
L2 Announcements는 온프레미스 환경에서 서비스를 외부에 노출할 수 있도록 해주는 기능입니다. ARP 응답을 통해 트래픽을 한 노드가 받아 서비스 로드 벨런싱을 수행합니다. 각 서비스는 고유의 External IP나 LoadBalancer IP를 사용할 수 있어, 여러 서비스가 동일한 포트를 사용할 수 있습니다. 이전에 포스팅하였던, MetalLB가 대표적인 L2 Announcements를 기능을 제공하는 오픈소스입니다. 하지만 cilium을 사용하면 metalLB 없이 손쉽게 L2 Announcements 기능을 사용할 수 있습니다.
l2 announcement 기능을 활성화하여 cilium을 재배포합니다.
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
metalLB(L2 Advertisement, IPAddressPool)에서와 같이, cilium에서는 CiliumL2 AnnouncementPolicy,
l2 announcement을
생성합니다.
CiliumL2 AnnouncementPolicy:
특정 서비스에 대해 L2 Announcement 기능을 적용할지 여부를 설정하는 정책 리소스.l2 announcement:
External IP 및 LoadBalancer IP를 L2 네트워크에 알리는 기능을 활성화하는 설정.
# 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
이제 파드와 LoadBalancer Type
서비스를 배포합니다.
#
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
서비스를 확인해 보면 설정한 IP 대역의 External-IP가 할당되고, 통신도 잘 되는 것을 확인할 수 있습니다. 이렇게 Cilium을 사용하면 metalLB 없이도 너무나도 손쉽게 L2 Announcements 기능을 사용할 수 있습니다.
(⎈|jlab:default) root@k8s-s:~# k get svc,ep
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.10.0.1 <none> 443/TCP 4d23h
service/svc ClusterIP 10.10.16.140 <none> 80/TCP 3h
service/svc1 LoadBalancer 10.10.181.119 10.10.200.1 80:32217/TCP 5m42s
service/svc2 LoadBalancer 10.10.107.176 10.10.200.2 80:32060/TCP 5m42s
service/svc3 LoadBalancer 10.10.117.180 10.10.200.3 80:30574/TCP 5m42s
NAME ENDPOINTS AGE
endpoints/kubernetes 192.168.10.10:6443 4d23h
endpoints/svc 172.16.1.141:80,172.16.2.136:80 3h
endpoints/svc1 172.16.1.141:80,172.16.2.136:80 5m42s
endpoints/svc2 172.16.1.141:80,172.16.2.136:80 5m42s
endpoints/svc3 172.16.1.141:80,172.16.2.136:80 5m42s
curl -s 10.10.200.1
curl -s 10.10.200.2
curl -s 10.10.200.3
Hostname: webpod2
IP: 127.0.0.1
IP: ::1
IP: 172.16.2.136
...
결론
Cilium은 이밖에 다루지 못한 Cilium Egress Gateway
, IPSec, WireGuard
, Cilium XDP
, 심지어 Service Mesh 기능까지 수많은 네트워크 기능들이 통합되어 있어, 최적의 네트워크 성능과 보안을 제공합니다. 특히 eBPF 덕분에 리소스 효율성이 높고 오버헤드가 적어 서비스의 수가 많아지는 클라우드 네이티브 환경에서는 거의 필수적인 선택이라는 생각도 듭니다.(왜 시스코가 왜 비싸게 주고 인수했는지 알 것 같네요) eBPF 프로젝트가 대세인 만큼, Istio, calio 등 급변하는 네트워크 시장 경쟁에서 앞으로의 cilium의 활약이 기대가 되는 것 같습니다.
'Kubernetes > Network' 카테고리의 다른 글
AWS EKS VPC CNI - KANS 9주차 (2) | 2024.11.02 |
---|---|
Istio & Service Mesh- KANS 7주차 (4) | 2024.10.20 |
Ingress와 Kubernetes Gateway Api - KANS 6주차 (0) | 2024.10.13 |
kube-proxy IPVS 모드의 동작 원리 이해 - KANS 5주차 2 (1) | 2024.10.06 |
MetalLB를 활용한 LoadBalancer 서비스의 동작 원리 이해 - KANS 5주차 1 (2) | 2024.10.06 |