Kubernetes

Cilium CNI 공부하기

mokpolar 2024. 10. 25. 17:29
반응형

안녕하세요?
이 글에서는 Cilium CNI에 대해서 알아보겠습니다.

 

이 글은 CloudNeta 팀 가시다님의 KANS(Kubernetes Advanced Network Study) 8주차 과제로 작성되었습니다.

TL; DR

 

Cilium이 뭔가요?

Cilium은 그 전에 살펴보았던 Flannel, Calico와 같이 Kubernetes CNI 중 하나입니다.

eBPF (Berkeley Packet Filter)를 기반으로 Pod Network 환경 + 보안 을 제공합니다.

그리고 다른 CNI들을 압도하는 성능을 갖고 있다고 합니다.

 

아래의 connection rate benchmark 참조

출처 : https://cilium.io/blog/2021/05/11/cni-benchmark/

 

 

그리고 아래와 같이 많은 기능을 Cilium으로 모두 대체해버릴 수 있는 것 처럼 보입니다.

 

Load Balancer, Ingress, Kube-proxy, Service-mesh(Istio도 노리고 있는 것 같습니다), Sidecar ..
많은 것들을 대체한다고 되어 있습니다.

출처: https://isovalent.com/blog/post/migrating-from-metallb-to-cilium/

 

 

Cilium의 특징과 동작 방식

Kube-proxy를 100% 대체 가능

Kube-proxy를 100% 대체할 수 있습니다.

기존의 어떤 단점을 해결하기 위해 Kube-proxy를 대체하는 지는 잠시 뒤에 알아보겠습니다.

 

추가적인 App 이나 설정 변경 없이 리눅스 커널을 자유롭게 프로그래밍하여 동작 가능

 

Kernel Layer에서 동작하는 Bytecode를 안전하게 Kernel에 Loading(injection) 할 수 있습니다.

출처 : https://cilium.io/blog/2020/11/10/ebpf-future-of-networking/

 

들어오는 모든 패킷을 추가 처리하기 위해 이 링크들의 인그레스 TC 훅에 eBPF 프로그램을 연결

 

Linux TC(Traffic Control)는 커널에서 동작하는 패킷 스케줄러입니다.
Cilium은 모든 패킷을 가로채기 위해서 수신 NIC 의 ingress TC hooks 를 사용합니다.

아래 그림에서 보이는 하단 NIC에 TC Hooks, 그리고 그 위에 eBPF 프로그램이 Attach되어서 실행됩니다.

출처 : https://cilium.io/blog/2020/11/10/ebpf-future-of-networking/

 

터널 모드(VXLAN, GENEVE), 네이티브 라우팅 모드의 2가지 네트워크 모드를 제공

 

Cilium은 2 가지 네트워크 모드를 제공합니다. 그 중

  • 터널 모드에서는 Cilium이 여러 개의 VXLAN(UDP 8472) 또는 Geneve(UDP 6081) 인터페이스를 설정하고 이를 통해 트래픽을 전달합니다.
  • 네이티브 라우팅 모드에서는 Cilium이 연결성을 설정하지 않으며, 외부에서 제공될 것을 가정합니다.

출처: https://docs.cilium.io/en/stable/concepts/networking/routing/#native-routing

 

Cilium은 기존의 어떤 문제를 해결하나요?

Iptables 의 단점들

 

리눅스 네트워크 스택의 단점은 복잡하고, 변경에 시간이 걸리고, 레이어를 건너뛰기 어렵다는 것입니다.

특히 성능에 영향을 미치는 기존 Kube-proxy Iptables 모드에 대한 단점을 예로 들어볼 수 있을 것 같습니다.

아래는 Iptables 그 자체의 단점 목록입니다.

  • Iptables 업데이트는 모든 규칙을 한 번에 재생성하고 업데이트해야 하는 단일 트랜잭션으로 이루어져야 합니다.
  • 규칙 체인을 연결 리스트로 구현하므로 모든 연산이 O(n)입니다.
  • Iptables에서 액세스 제어 목록(ACL)을 구현하는 일반적인 방법은 규칙을 순차적으로 나열하는 것입니다.
  • IP와 포트를 매칭하는 방식에 기반하고 있어 L7(애플리케이션 계층) 프로토콜을 인식하지 못합니다.
  • 새로운 IP 또는 포트를 매칭할 때마다 규칙을 추가하고 체인을 변경해야 합니다.
  • Kubernetes에서 리소스 소비가 많습니다.

위에서 언급한 문제들로 인해 많은 트래픽이 발생하거나 iptable 규칙에 많은 변경이 필요한 시스템에서는 성능 저하가 발생합니다. 측정 결과 서비스 수가 증가할수록 예측 불가능한 지연과 성능 저하가 나타났습니다.

 

출처 : https://bit.ly/3EiLsU3

 

그래서 그 문제를 어떻게 해소하나요?

Berkely Packet Filter, Cilium에서 사용하는 BPF 기술은 일종의 Kernel Hook으로 볼 수 있습니다.
네트워크 트래픽을 분석하고 필터링하기 위해서 설계 되었습니다.

Kernel Hook : 운영체제의 커널에 특정 이벤트가 발생할 때 이를 가로채서(“hook”) 원하는 동작을 수행할 수 있게 하는 메커니즘입니다. 주로 보안, 성능 개선, 시스템 모니터링, 디버깅 등을 위해 사용됩니다. Kernel hook을 사용하면 커널의 기본 동작을 수정하거나 확장할 수 있습니다.

 

eBPF는 extended BPF라고 하여, Linux Kernel 안에 내장하는 VM입니다.

BPF와는 레지스터와 그 레지스터들의 스택이 존재하며, limit 제한이 없는 Maps가 존재한 다는 구조상 차이가 있습니다.

 

특정 이벤트가 발생할 때 실행되는 프로그램을 Kernel에 적재해서 원하는 동작으로 커스터마이징 할 수 있습니다.

출처: https://cilium.io/blog/2020/11/10/ebpf-future-of-networking/

 

eBPF로 인해 패킷을 필터링(통제)할 수 있고, 여러 레이어에서 Hook을 통해서 접근할 수 있습니다.
그리고 매우 경량화 되어 있고, Kernel 내부에서 실행하기 때문에 패킷 처리시 성능 저하가 거의 없습니다.

 

특히 패킷을 Kernel에서 처리하기 전에 필터링해서 불필요한 패킷을 애플리케이션에 전달하지 않을 수 있습니다.

그래서 기존에 여러 과정이 포함되어 있던 네트워크를 우회할 수 있습니다.

 

아래 그림처럼 network device 에서 들어오는 단계에서 TC hook을 통해 Kernel 내에서 많은 단계를 건너뛰게 합니다.

출처 : https://bit.ly/3EiLsU3

 

리눅스 네트워크 스택을 봤을 때 아래와 같이 개입합니다.

특히 XDP를 적용시 Network Device 단에서 네트워크 처리를 바로 해서 성능이 더 좋아질 수 있는 모습이 보입니다.

출처 : https://bit.ly/3EiLsU3

 

기존 Iptables 를 사용할 때 거치던 과정입니다.

PREROUTING mangle -> nat -> FORWARD -> POSTROUTING 까지 거치는 chain들을 지나서

Kubernetes 환경에서는 veth를 통해 Pod내 통신으로 들어가게 됩니다.

 

각 노드마다 Pod들에 대한 이런 iptables 규칙들이 모두 생기니 시간이 지나고 규모가 커질 수록 점점 더 큰 부담이 됩니다.

출처 : https://bit.ly/3EiLsU3

 

아래처럼 비교를 해볼 수 있습니다.

유지에 대한 부분을 대신해주니 conntrack이 빠져서 이를 유지하는 공수가 줄어들게 되는 부분도 보입니다.

출처 : https://cilium.io/blog/2021/05/11/cni-benchmark/

출처: https://cilium.io/blog/2020/11/10/ebpf-future-of-networking/

 

Cilium Architecture

Cilium의 구성요소

  • Cilium Agent
    • Cilium Agent는 데몬셋으로 배포됩니다.
    • K8S API Server와 통신하여 네트워크 설정, 네트워크 정책, 서비스 부하분산, 모니터링을 수행합니다.
    • eBPF 프로그램을 관리합니다.
    • Linux Kernel과 상호작용하여 eBPF 프로그램을 로드하고 eBPF 맵을 업데이트합니다.
    • Cilium CNI 플러그인과 상호동작하여 새로운 워크로드가 예약될 때 알림을 받습니다.
  • Cilium Operator
    • K8S 클러스터에 대해 처리해야 하는 작업을 관리합니다.
  • Data Store
    • Cilium Agent 간의 상태를 저장하고 전파하는 데이터 저장소입니다.
    • 2가지 타입 중에서 고를 수 있습니다.
      • K8S CRDs
      • Key-Value store

구조를 그려보면 아래와 같습니다.

실제 Cilium을 설치했을 때 Daemonset으로 배포되는 건 2가지인데,

 

* cilium-envoy:
각 cilium-envoy Pod는 Cilium이 관리하는 네트워크 내에서 실행됩니다.
이들은 Cilium의 정책을 적용하여 서비스 간의 L7 트래픽을 처리합니다.
각 노드에서 실행되는 서비스의 트래픽을 관리합니다.

 

* cilium:
Cilium의 다른 구성 요소는 네트워크 정책, 패킷 처리 및 클러스터 상태를 관리합니다.
이들은 Cilium의 기능을 지원하는 데 필요한 데이터 플레인과 제어 플레인 구성 요소입니다.

 

그리고 Cilium CNI가 각 노드들을 연결하는 모습을 구조로 그려보면 아래와 같습니다.

 

Cilium을 실제 동작 시켜보기

Cilium 환경 구성

 

Cilium을 위한 테스트 환경은 icebreaker70님이 구성해두신 Vagrant로 Vanila Cilium 예제를 사용했습니다.
GitHub - icebreaker70/kans-vanilla-k8s-cilium: MacBook M 시리즈에서 cilium cni 테스트하기 위해 vagrant 이용하여 vanilla-k8s Cluster 구성

 

구성 후 아래와 같이 k8s-s, k8s-w1, k8s-w2, testpc 라는 4개의 VM이 떠 있는 상태입니다.

vagrant status
Current machine states:

k8s-s                     running (vmware_desktop)
k8s-w1                    running (vmware_desktop)
k8s-w2                    running (vmware_desktop)
testpc                    running (vmware_desktop)

 

컨트롤 플레인으로 접근해보겠습니다.
처음에 연결이 되어있지 않은 상태입니다.

vagrant ssh k8s-s

k get node -o wide
NAME     STATUS     ROLES           AGE   VERSION   INTERNAL-IP      EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION      CONTAINER-RUNTIME
k8s-s    NotReady   control-plane   48m   v1.30.6   192.168.10.10    <none>        Ubuntu 22.04.3 LTS   5.15.0-92-generic   containerd://1.7.22
k8s-w1   NotReady   <none>          46m   v1.30.6   192.168.10.101   <none>        Ubuntu 22.04.3 LTS   5.15.0-92-generic   containerd://1.7.22
k8s-w2   NotReady   <none>          45m   v1.30.6   192.168.10.102   <none>        Ubuntu 22.04.3 LTS   5.15.0-92-generic   containerd://1.7.22

 

이제 CIlium을 설치하겠습니다.

helm repo add cilium https://helm.cilium.io/
"cilium" has been added to your repositories

helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "cilium" chart repository
Update Complete. ⎈Happy Helming!⎈

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
NAME: cilium
LAST DEPLOYED: Sat Oct 26 17:58:37 2024
NAMESPACE: kube-system
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
You have successfully installed Cilium with Hubble Relay and Hubble UI.

Your release version is 1.16.3.

For any further help, visit https://docs.cilium.io/en/v1.16/gettinghelp

 

위에 Cilium 설치에 사용된 각 파라미터들의 의미를 자세히 알아볼 필요가 있습니다.

--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 적용이 가능

 

이제 각 노드들이 연결이 되는 모습을 볼 수 있습니다.

k get node -o wide
NAME     STATUS   ROLES           AGE   VERSION   INTERNAL-IP      EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION      CONTAINER-RUNTIME
k8s-s    Ready    control-plane   75m   v1.30.6   192.168.10.10    <none>        Ubuntu 22.04.3 LTS   5.15.0-92-generic   containerd://1.7.22
k8s-w1   Ready    <none>          74m   v1.30.6   192.168.10.101   <none>        Ubuntu 22.04.3 LTS   5.15.0-92-generic   containerd://1.7.22
k8s-w2   Ready    <none>          72m   v1.30.6   192.168.10.102   <none>        Ubuntu 22.04.3 LTS   5.15.0-92-generic   containerd://1.7.22

 

이제 Cilium CNI에서는 뭐가 달라지는지 network interface들을 살펴보겠습니다.

ip -c addr
...
4: cilium_net@cilium_host: <BROADCAST,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether ea:83:f5:37:ab:f3 brd ff:ff:ff:ff:ff:ff
    inet6 fe80::e883:f5ff:fe37:abf3/64 scope link
       valid_lft forever preferred_lft forever
5: cilium_host@cilium_net: <BROADCAST,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 0e:9d:bf:7f:6b:53 brd ff:ff:ff:ff:ff:ff
    inet 172.16.0.157/32 scope global cilium_host
       valid_lft forever preferred_lft forever

 

Cilium이 생성한 가상 인터페이스인 cilium_net과 cilium_host이 보입니다.
172.16.0.157/32은 cilium_host에 할당된 IP입니다.

...
7: lxc_health@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 8e:7a:8c:43:aa:63 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet6 fe80::8c7a:8cff:fe43:aa63/64 scope link
       valid_lft forever preferred_lft forever

 

lxc_health는 컨테이너의 상태 모니터링에 사용됩니다.

그 다음 NAT 테이블을 살펴보겠습니다.

 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

 

위 내용을 살펴보면,

-N CILIUM_OUTPUT_nat
-N CILIUM_POST_nat
-N CILIUM_PRE_nat

 

Cilium이 생성한 Custom chain을 볼 수 있습니다.

-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
  • -A PREROUTING: PREROUTING 체인에 규칙을 추가합니다. 이 규칙은 Cilium에서 관리하는 CILIUM_PRE_nat 체인으로 패킷을 전달하는 역할을 합니다.
  • -A OUTPUT: 로컬에서 생성된 패킷이 외부로 나갈 때, Cilium에서 관리하는 CILIUM_OUTPUT_nat 체인으로 전달됩니다.
  • -A POSTROUTING: 패킷이 라우팅된 후, Cilium에서 관리하는 CILIUM_POST_nat 체인으로 전달됩니다.

 

Cilium을 설치하면 CR로 ciliumnodes가 추가됩니다.
이 CR을 통해서 관리되는 노드들을 살펴볼 수 있습니다.

kubectl get ciliumnodes
NAME     CILIUMINTERNALIP   INTERNALIP       AGE
k8s-s    172.16.0.157       192.168.10.10    57m
k8s-w1   172.16.1.54        192.168.10.101   56m
k8s-w2   172.16.2.210       192.168.10.102   57m

 

CILIUMINTERNALIP는 cilium_host 인터페이스의 IP를 확인해줍니다.

 kubectl get ciliumendpoints -A
NAMESPACE     NAME                           SECURITY IDENTITY   ENDPOINT STATE   IPV4           IPV6
kube-system   coredns-55cb58b774-9nlk9       24081               ready            172.16.2.150
kube-system   coredns-55cb58b774-jp8jt       24081               ready            172.16.2.143
kube-system   hubble-relay-88f7f89d4-ws55k   33525               ready            172.16.2.24
kube-system   hubble-ui-59bb4cb67b-pd2xd     31023               ready            172.16.2.160

 

Cilium endpoint들도 위 명령으로 살펴볼 수 있습니다.

처음 CIlium을 설치할 때 초기 설정과 반영한 파라미터들로 인한 Cilium-config도 확인할 수 있습니다.

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",
...
    "vtep-cidr": "",
    "vtep-endpoint": "",
    "vtep-mac": "",
    "vtep-mask": "",
    "write-cni-conf-when-ready": "/host/etc/cni/net.d/05-cilium.conflist"
  },
  "kind": "ConfigMap",
  "metadata": {
    "annotations": {
      "meta.helm.sh/release-name": "cilium",
      "meta.helm.sh/release-namespace": "kube-system"
    },
    "labels": {
      "app.kubernetes.io/managed-by": "Helm"
    },
    "name": "cilium-config",
    "namespace": "kube-system",
  }
}

 

이 중 몇개만 살펴보면,

  • "ipam: kubernetes" : Cilium이 Kubernetes의 IPAM(아이피 주소 관리) 모드를 사용합니다. 즉, Kubernetes가 Pod에 IP를 할당하는 역할을 합니다.
  • "kube-proxy-replacement: true" : Kubernetes의 kube-proxy 기능을 Cilium이 대체합니다. 이로 인해 Cilium이 서비스 라우팅과 로드 밸런싱을 처리하게 됩니다.
  • "enable-k8s-networkpolicy: true" : Kubernetes 네트워크 정책을 활성화하여 클러스터 내에서 네트워크 트래픽 제어가 가능합니다.
  • "enable-bpf-masquerade: true" : BPF를 사용하여 마스커레이딩(출발지 IP 변환)이 활성화되어 있습니다. 이는 클러스터 외부로 나가는 트래픽의 IP 주소를 변환하는 데 사용됩니다.
  • "enable-hubble: true" : Hubble은 Cilium의 분산 트래픽 모니터링 기능입니다. 이 설정을 통해 클러스터 내부의 네트워크 트래픽을 모니터링할 수 있습니다.
  • "bpf-root: /sys/fs/bpf" : BPF 파일 시스템이 마운트된 경로입니다.
  • "bpf-lb-map-max: 65536" : BPF 로드 밸런서 맵의 최대 엔트리 수는 65536개입니다.

 

Cilium CLI

 

Cilium은 CLI를 제공합니다.
이 CLI를 통해 Cilum의 설치 상태를 확인하거나 여러 가지 기능을 사용할 수 있습니다.

 

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 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
...

 

Cilium Config

 

cilium config 커맨드로 설정들을 살펴볼 수 있습니다.

cilium config view
agent-not-ready-taint-key                         node.cilium.io/agent-not-ready
arping-refresh-period                             30s
...
write-cni-conf-when-ready                         /host/etc/cni/net.d/05-cilium.conflist

 

Cilium Agent는 Daemonset으로 배포됩니다.
이에 접근해서 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   [eth0   172.16.180.132 fe80::20c:29ff:febb:42f, eth1   192.168.10.10 fe80::20c:29ff:febb:439 (Direct Routing)]

 

Cilium이 Kube-proxy의 기능을 대체하여 클러스터 내의 서비스 트래픽을 처리하고 있음을 의미합니다.

  Devices:                eth0   172.16.180.132 fe80::20c:29ff:febb:42f, eth1   192.168.10.10 fe80::20c:29ff:febb:439 (Direct Routing)

 

Cilium이 사용하는 라우팅 방법을 나타냅니다.

"Direct Routing"은 클러스터 내의 서비스 트래픽이 직접 파드에 전달됨을 의미합니다.

 

이는 Cilium이 BPF를 사용하여 패킷을 직접 필터링하고 처리할 수 있게 하여, 네트워크 성능을 향상시킵니다.

Routing:                Network: Native   Host: BPF
  • Network: Native
    Cilium이 Kubernetes 클러스터 내에서 사용하는 네트워크 라우팅 방식입니다. "Native"는 Cilium이 네이티브 IP 주소를 사용하여 패킷을 라우팅한다는 의미입니다. 이는 Cilium이 Kubernetes의 네트워킹 스택과 통합되어 IP 패킷을 직접 처리하고, Kubernetes API를 통해 서비스를 관리한다는 것을 의미합니다.
  • Host: BPF
    Cilium이 호스트 시스템에서 패킷을 처리하는 방법이 BPF라는 의미입니다.
Masquerading:           BPF   [eth0, eth1]   192.168.0.0/16 [IPv4: Enabled, IPv6: Disabled]

 

Masquerading: BPF
Cilium은 패킷의 출발지 IP 주소를 변경하기 위해 BPF(Berkeley Packet Filter) 기술을 사용합니다. BPF는 패킷을 커널에서 필터링하고 처리할 수 있게 해주는 기능으로, 성능을 향상시키는 데 도움을 줍니다.

 

eth0, eht1 :
Cilium이 masquerading을 적용하는 네트워크 인터페이스의 이름입니다. 이 인터페이스를 통해 패킷이 전송되며, Cilium은 해당 인터페이스를 통해 나가는 패킷의 출발지 IP 주소를 변경합니다.

 

192.168.0.0/16
이 CIDR 블록은 masquerading이 적용되는 IP 주소 범위를 나타냅니다. 여기서는 192.168.0.0부터 192.168.255.255까지의 모든 주소가 포함됩니다. 이 주소 범위 내의 모든 패킷은 masquerading 규칙에 따라 처리됩니다.

 

Proxy Status:            OK, ip 172.16.0.157, 0 redirects active on ports 10000-20000, Envoy: external

 

Proxy Status: OK
Cilium 프록시의 상태가 정상임을 나타냅니다.

 

ip 172.16.0.157
Cilium 프록시의 내부 IP 주소입니다. 이 주소는 Cilium에서 관리하는 트래픽의 출발지 또는 목적지로 사용될 수 있습니다.

 

0 redirects active on ports 10000-20000
현재 10000번에서 20000번 포트 간에 활성화된 리다이렉트가 없음을 나타냅니다. 리다이렉트는 특정 포트에서 수신한 트래픽을 다른 포트로 전송하는 기능입니다. 이 경우에는 해당 포트 범위 내에서 리다이렉트가 설정되지 않았습니다.

 

Envoy: external
Cilium의 Envoy 프록시가 외부 환경에서 실행되고 있음을 나타냅니다. Envoy는 HTTP 및 TCP 요청을 관리하는 고성능 프록시 서버로, 서비스 간의 통신을 지원합니다. "external"이라는 용어는 Envoy가 클러스터 외부와의 통신을 포함한 외부 환경에 연결되어 있음을 의미합니다.

 

Cilium 환경에서 파드 간 통신 확인

이제 Cilium 환경에서 파드 간 통신을 확인해보도록 하겠습니다.

Cilium 환경에서 Packet이 어떻게 움직이는지는 아래 docs에서 상세하게 확인할 수 있습니다.
Life of a Packet — Cilium 1.16.3 documentation

 

Endpoint to Endpoint

Hubble 설치

테스트 전에 동작 확인을 위해 Hubble client를 설치하겠습니다.
Hubble은 Cilium에서 통신 및 서비스, 네트워크 동작에 대해 관찰성을 제공하기 위한 도구입니다.


참고 : Announcing Hubble - Network, Service & Security Observability for Kubernetes

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 status
Healthcheck (via localhost:4245): Ok
Current/Max Flows: 12,285/12,285 (100.00%)
Flows/s: 18.48
Connected Nodes: 3/3

 

그리고 hubble observe 커맨드를 통해 Cilium의 통신 확인을 할 수 있습니다.

hubble observe
Oct 26 11:00:13.445: 127.0.0.1:45340 (world) <> 192.168.10.101 (host) pre-xlate-rev TRACED (TCP)
Oct 26 11:00:14.221: kube-system/hubble-relay-88f7f89d4-ws55k:36540 (ID:33525) <> 192.168.10.101 (host) pre-xlate-rev TRACED (TCP)
Oct 26 11:00:15.024: 127.0.0.1:56156 (world) <> 192.168.10.10 (host) pre-xlate-rev TRACED (TCP)
...

 

테스트 환경 준비

이 내용을 확인하기 위해 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

 

그러면 ciliumendpoints가 생겨납니다.

kubectl get ciliumendpoints
NAME      SECURITY IDENTITY   ENDPOINT STATE   IPV4           IPV6
netpod    31515               ready            172.16.0.229
webpod1   57358               ready            172.16.1.93
webpod2   57358               ready            172.16.2.31

 

cilium의 명령으로 bpf endpoint도 확인할 수 있습니다.

c0 bpf endpoint list
IP ADDRESS         LOCAL ENDPOINT INFO
172.16.0.229:0     id=2561  sec_id=31515 flags=0x0000 ifindex=9   mac=32:4E:42:32:4B:FE nodemac=6A:E7:BC:C2:0F:47
...

c1 bpf endpoint list
IP ADDRESS         LOCAL ENDPOINT INFO
172.16.1.93:0      id=3038  sec_id=57358 flags=0x0000 ifindex=9   mac=D6:69:B3:8C:3C:89 nodemac=96:41:22:16:E1:CF
...

c2 bpf endpoint list
IP ADDRESS         LOCAL ENDPOINT INFO
172.16.2.31:0      id=389   sec_id=57358 flags=0x0000 ifindex=17  mac=A6:A8:B3:20:65:F1 nodemac=12:49:3E:92:A6:A6
...

 

통신 테스트

테스트를 쉽게 하기 위해 변수를 지정합니다.

# 테스트 파드들 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 -- "

 

각 pod에 패킷을 보내고 응답을 확인해보겠습니다.

p0 ping -c 1 $WEBPOD1IP && p0 ping -c 1 $WEBPOD2IP
PING 172.16.1.93 (172.16.1.93) 56(84) bytes of data.
64 bytes from 172.16.1.93: icmp_seq=1 ttl=62 time=0.585 ms

--- 172.16.1.93 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.585/0.585/0.585/0.000 ms
PING 172.16.2.31 (172.16.2.31) 56(84) bytes of data.
64 bytes from 172.16.2.31: icmp_seq=1 ttl=62 time=0.561 ms

--- 172.16.2.31 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.561/0.561/0.561/0.000 ms

 

그러면 이제 이 통신내용을 hubble로 확인해볼 수 있습니다.

hubble observe --pod netpod
Oct 26 11:02:29.951: default/netpod (ID:31515) -> default/webpod1 (ID:57358) to-network FORWARDED (ICMPv4 EchoRequest)
Oct 26 11:02:29.951: default/netpod (ID:31515) <- default/webpod1 (ID:57358) to-endpoint FORWARDED (ICMPv4 EchoReply)
Oct 26 11:02:30.030: default/netpod (ID:31515) -> default/webpod2 (ID:57358) to-network FORWARDED (ICMPv4 EchoRequest)
Oct 26 11:02:30.031: default/netpod (ID:31515) <- default/webpod2 (ID:57358) to-endpoint FORWARDED (ICMPv4 EchoReply)

 

netpod가 webpod로 ping 요청을 보냅니다. 이 패킷은 네트워크를 통해 전달(FORWARDED)됩니다.
netpod가 webpod로부터 ping 응답을 받습니다. 이 응답 패킷 역시 네트워크를 통해 전달(FORWARDED)됩니다.

반대의 경우도 확인할 수 있습니다.

hubble observe --pod webpod1
Oct 26 11:02:29.951: default/netpod (ID:31515) -> default/webpod1 (ID:57358) to-network FORWARDED (ICMPv4 EchoRequest)
Oct 26 11:02:29.951: default/netpod (ID:31515) <- default/webpod1 (ID:57358) to-endpoint FORWARDED (ICMPv4 EchoReply)
Oct 26 11:02:29.998: 192.168.10.10 (kube-apiserver) -> default/webpod1 (ID:57358) to-endpoint FORWARDED (ICMPv4 EchoRequest)
Oct 26 11:02:29.998: 192.168.10.10 (kube-apiserver) <- default/webpod1 (ID:57358) to-network FORWARDED (ICMPv4 EchoReply)
hubble observe --pod webpod2
Oct 26 11:02:30.030: default/netpod (ID:31515) -> default/webpod2 (ID:57358) to-network FORWARDED (ICMPv4 EchoRequest)
Oct 26 11:02:30.031: default/netpod (ID:31515) <- default/webpod2 (ID:57358) to-endpoint FORWARDED (ICMPv4 EchoReply)
Oct 26 11:02:30.134: 192.168.10.10 (kube-apiserver) -> default/webpod2 (ID:57358) to-endpoint FORWARDED (ICMPv4 EchoRequest)
Oct 26 11:02:30.134: 192.168.10.10 (kube-apiserver) <- default/webpod2 (ID:57358) to-network FORWARDED (ICMPv4 EchoReply)

 

 

목적지 파드와 통신시 어느 곳으로 보내야 할지를 BPF map으로 확인할 수 있습니다.
cilium cli로 확인해보겠습니다.

c0 map get cilium_ipcache
Key                 Value                                                                     State   Error
172.16.0.157/32     identity=1 encryptkey=0 tunnelendpoint=0.0.0.0, flags=<none>              sync
0.0.0.0/0           identity=2 encryptkey=0 tunnelendpoint=0.0.0.0, flags=<none>              sync
172.16.2.150/32     identity=24081 encryptkey=0 tunnelendpoint=192.168.10.102, flags=<none>   sync
172.16.2.31/32      identity=57358 encryptkey=0 tunnelendpoint=192.168.10.102, flags=<none>   sync
192.168.10.10/32    identity=1 encryptkey=0 tunnelendpoint=0.0.0.0, flags=<none>              sync
172.16.0.183/32     identity=4 encryptkey=0 tunnelendpoint=0.0.0.0, flags=<none>              sync
172.16.180.132/32   identity=1 encryptkey=0 tunnelendpoint=0.0.0.0, flags=<none>              sync
172.16.2.145/32     identity=4 encryptkey=0 tunnelendpoint=192.168.10.102, flags=<none>       sync
172.16.1.93/32      identity=57358 encryptkey=0 tunnelendpoint=192.168.10.101, flags=<none>   sync
172.16.0.229/32     identity=31515 encryptkey=0 tunnelendpoint=0.0.0.0, flags=<none>          sync
192.168.10.102/32   identity=6 encryptkey=0 tunnelendpoint=0.0.0.0, flags=<none>              sync
172.16.2.210/32     identity=6 encryptkey=0 tunnelendpoint=192.168.10.102, flags=<none>       sync
172.16.2.160/32     identity=31023 encryptkey=0 tunnelendpoint=192.168.10.102, flags=<none>   sync
172.16.2.143/32     identity=24081 encryptkey=0 tunnelendpoint=192.168.10.102, flags=<none>   sync
192.168.10.101/32   identity=6 encryptkey=0 tunnelendpoint=0.0.0.0, flags=<none>              sync
172.16.2.24/32      identity=33525 encryptkey=0 tunnelendpoint=192.168.10.102, flags=<none>   sync
172.16.1.26/32      identity=4 encryptkey=0 tunnelendpoint=192.168.10.101, flags=<none>       sync
172.16.1.54/32      identity=6 encryptkey=0 tunnelendpoint=192.168.10.101, flags=<none>       sync
c0 map get cilium_ipcache | grep $WEBPOD1IP
172.16.1.93/32      identity=57358 encryptkey=0 tunnelendpoint=192.168.10.101, flags=<none>   sync

 

 

Reference

반응형