티스토리 뷰

IT/Container&k8s

K8S Cilium CNI

Hayley Shim 2024. 10. 27. 08:27

안녕하세요. Kubernetes Advanced Networking Study(=KANS) 3기 모임에서 스터디한 내용을 정리했습니다. 해당 글에서는 Cilium CNI에 대해 자세히 알아보겠습니다.

 

eBPF

- User space에서 동작하는 기능을 kernel space에서 동작할 수 있게 하는 기능

- eBPF는 프로토콜이 아니라 리눅스 커널에 샌드박스 기술을 적용하는 기술

 

Cilium

- eBPF 오픈소스 프로젝트 중 하나로 eBPF (Berkeley Packet Filter)를 기반으로 Pod Network 환경 + 보안 을 제공하는 CNI Plugin

- BPF 는 커널에 삽입하여 패킷을 통제(필터링) 할 수 있으며 BPF를 확장해서 eBPF가 나왔고 eBPF다양한 영역 (보안, 추적, 네트워킹, 모니터링)에서 활용하기 시작함

- Kubernetes와 같은 Linux 컨테이너 관리 플랫폼을 사용하여 배포된 응용 프로그램 서비스 간의 네트워크 및 API 연결을 제공하는 오픈 소스 소프트웨어

- Google GKE dataplane 과 AWS EKS Anywhere 에 기본 CNI 로 Cilium 을 사용함

  1. cilium — eks anywhere 에 탑재. 별도 encapsulation 없이 같은 대역에 네트워킹이 가능하도록 함
  2. calico — bgp 등에 사용됨. 현재 vxlan 안쓰는 방향으로 진행하며 data plane쪽만 eBPF로 진행
  3. loxiLB — 현재 eks anywhere에서 metal LB를 loxiLB로 대체하려 시도 중. go 언어 기반으로 동작

 

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

 

 

Cillium Architecture

https://github.com/cilium/cilium

실습환경

- VPC 1개(퍼블릭 서브넷 2개), EC2 인스턴스 3대 (Ubuntu 22.04 LTS, t3.xlarge - vCPU 4 , Mem 16) , testpc 1대는 t3.small

 

# YAML 파일 다운로드
curl -O https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/kans/kans-8w.yaml

# CloudFormation 스택 배포
# aws cloudformation deploy --template-file kans-8w.yaml --stack-name mylab --parameter-overrides KeyName=<My SSH Keyname> SgIngressSshCidr=<My Home Public IP Address>/32 --region ap-northeast-2
예시) aws cloudformation deploy --template-file kans-8w.yaml --stack-name mylab --parameter-overrides KeyName=kp-gasida SgIngressSshCidr=$(curl -s ipinfo.io/ip)/32 --region ap-northeast-2

## Tip. 인스턴스 타입 변경 : MyInstanceType=t3.xlarge (vCPU 4, Mem 16)
예시) aws cloudformation deploy --template-file kans-8w.yaml --stack-name mylab --parameter-overrides MyInstanceType=t3.xlarge KeyName=kp-gasida SgIngressSshCidr=$(curl -s ipinfo.io/ip)/32 --region ap-northeast-2

# CloudFormation 스택 배포 완료 후 작업용 EC2 IP 출력
aws cloudformation describe-stacks --stack-name mylab --query 'Stacks[*].Outputs[0].OutputValue' --output text --region ap-northeast-2

# [모니터링] CloudFormation 스택 상태 : 생성 완료 확인
while true; do 
  date
  AWS_PAGER="" aws cloudformation list-stacks \
    --stack-status-filter CREATE_IN_PROGRESS CREATE_COMPLETE CREATE_FAILED DELETE_IN_PROGRESS DELETE_FAILED \
    --query "StackSummaries[*].{StackName:StackName, StackStatus:StackStatus}" \
    --output table
  sleep 1
done

# 배포된 aws ec2 유동 공인 IP 확인
aws ec2 describe-instances --query "Reservations[*].Instances[*].{PublicIPAdd:PublicIpAddress,InstanceName:Tags[?Key=='Name']|[0].Value,Status:State.Name}" --filters Name=instance-state-name,Values=running --output text

# EC2 SSH 접속 : 바로 접속하지 말고, 5~7분 정도 후에 접속 할 것
ssh -i ~/.ssh/kp-gasida.pem ubuntu@$(aws cloudformation describe-stacks --stack-name mylab --query 'Stacks[*].Outputs[0].OutputValue' --output text --region ap-northeast-2)
...
(⎈|kubernetes-admin@kubernetes:N/A) root@k8s-s:~# <- kubeps 가 나오지 않을 경우 ssh logout 후 다시 ssh 접속 할 것!

 

k8s-s , testpc 각각 접속 후 확인

aws ec2 describe-instances --query "Reservations[*].Instances[*].{PublicIPAdd:PublicIpAddress,InstanceName:Tags[?Key=='Name']|[0].Value,Status:State.Name}" --filters Name=instance-state-name,Values=running --output text

 

k8s-s 접속 후 확인 : ssh -i <> ubuntu@[aws ec2 공인 IP]

# config rename-context
kubectl config rename-context "kubernetes-admin@kubernetes" "CiliumLab"

# 기본 정보 확인
kubectl cluster-info

# node 상태 확인
kc get node -owide
NAME     STATUS     ROLES           AGE     VERSION   INTERNAL-IP      EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION   CONTAINER-RUNTIME
k8s-s    NotReady   control-plane   5m54s   v1.30.5   192.168.10.10    <none>        Ubuntu 22.04.5 LTS   6.8.0-1015-aws   containerd://1.7.22
k8s-w1   NotReady   <none>          5m32s   v1.30.5   192.168.10.101   <none>        Ubuntu 22.04.5 LTS   6.8.0-1015-aws   containerd://1.7.22
k8s-w2   NotReady   <none>          5m34s   v1.30.5   192.168.10.102   <none>        Ubuntu 22.04.5 LTS   6.8.0-1015-aws   containerd://1.7.22

# kube-proxy 없다!
kc get pod -A
NAMESPACE     NAME                            READY   STATUS    RESTARTS   AGE
kube-system   coredns-55cb58b774-8l84j        0/1     Pending   0          8m17s
kube-system   coredns-55cb58b774-9prtv        0/1     Pending   0          8m17s
kube-system   etcd-k8s-s                      1/1     Running   0          8m31s
kube-system   kube-apiserver-k8s-s            1/1     Running   0          8m31s
kube-system   kube-controller-manager-k8s-s   1/1     Running   0          8m31s
kube-system   kube-scheduler-k8s-s            1/1     Running   0          8m31s

hostnamectl

# cilium 의 제대로?된 동작을 위해서 커널 버전은 최소 5.8 이상 권장
uname -a
Linux k8s-s 6.8.0-1015-aws #16~204.1-Ubuntu SMP Mon Aug 19 19:38:17 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux

 

testpc 각각 접속 후 확인 : ssh -i <> ubuntu@[IP]

ip -br -c addr
hostnamectl 
curl localhost

 

Cilium 배포

Cilium 설치 정보(w/Helm) 및 확인 - Docs

# 모니터링
watch -d kubectl get node,pod -A -owide

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

# 설정 및 확인
ip -c addr
kubectl get node,pod,svc -A -owide
iptables -t nat -S
iptables -t filter -S
iptables -t raw -S
iptables -t mangle -S
conntrack -L

kubectl get crd
kubectl get ciliumnodes # cilium_host 인터페이스의 IP 확인 : CILIUMINTERNALIP
kubectl get ciliumendpoints -A

kubectl get cm -n kube-system cilium-config -o json | jq

kubetail -n kube-system -l k8s-app=cilium --since 1h
kubetail -n kube-system -l k8s-app=cilium-envoy --since 1h

# Native XDP 지원 NIC 확인 : https://docs.cilium.io/en/stable/bpf/progtypes/#xdp-drivers
ethtool -i ens5
driver: ena
version: 6.8.0-1015-aws
...

# https://docs.cilium.io/en/stable/operations/performance/tuning/#bypass-iptables-connection-tracking
watch -d kubectl get pod -A # 모니터링
helm upgrade cilium cilium/cilium --namespace kube-system --reuse-values --set installNoConntrackIptablesRules=true

# 확인: 기존 raw 에 아래 rule 추가 확인
iptables -t raw -S | grep notrack
-A CILIUM_OUTPUT_raw -d 192.168.0.0/16 -m comment --comment "cilium: NOTRACK for pod traffic" -j CT --notrack
-A CILIUM_OUTPUT_raw -s 192.168.0.0/16 -m comment --comment "cilium: NOTRACK for pod traffic" -j CT --notrack
...

conntrack -F
conntrack -L
conntrack -L |grep -v 2379

 

Cilium CLI 설치 

inspect the state of a Cilium installation, and enable/disable various features (e.g. clustermesh, Hubble) - Link

# 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
ubeProxyReplacement:    True   [ens5   192.168.10.10 fe80::57:abff:fee3:da8d (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"

 

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}')
export CILIUMPOD1=$(kubectl get -l k8s-app=cilium pods -n kube-system --field-selector spec.nodeName=k8s-w1 -o jsonpath='{.items[0].metadata.name}')
export CILIUMPOD2=$(kubectl get -l k8s-app=cilium pods -n kube-system --field-selector spec.nodeName=k8s-w2 -o jsonpath='{.items[0].metadata.name}')

# 단축키(alias) 지정
alias c0="kubectl exec -it $CILIUMPOD0 -n kube-system -c cilium-agent -- cilium"
alias c1="kubectl exec -it $CILIUMPOD1 -n kube-system -c cilium-agent -- cilium"
alias c2="kubectl exec -it $CILIUMPOD2 -n kube-system -c cilium-agent -- cilium"

alias c0bpf="kubectl exec -it $CILIUMPOD0 -n kube-system -c cilium-agent -- bpftool"
alias c1bpf="kubectl exec -it $CILIUMPOD1 -n kube-system -c cilium-agent -- bpftool"
alias c2bpf="kubectl exec -it $CILIUMPOD2 -n kube-system -c cilium-agent -- bpftool"

# Hubble UI 웹 접속
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"

# 자주 사용 명령
helm upgrade cilium cilium/cilium --namespace kube-system --reuse-values --set
kubetail -n kube-system -l k8s-app=cilium --since 12h
kubetail -n kube-system -l k8s-app=cilium-envoy --since 12h

 

자주 쓰는 Cilium CLI 명령어

# 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

 

네트워크 기본 정보 확인 : k8s-w1/w2 에 SSH 접속 후 ip -c link/route 정보 확인

# 네트워크 인터페이스 정보 확인
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
	5: cilium_net@cilium_host: <BROADCAST,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
	    link/ether 36:88:bf:c9:5c:6c brd ff:ff:ff:ff:ff:ff
	   ...
	6: cilium_host@cilium_net: <BROADCAST,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
	    link/ether 4e:6a:8e:44:85:61 brd ff:ff:ff:ff:ff:ff
	    inet 172.16.1.254/32 scope link cilium_host
   ...

# 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

 

Hubble

- Hubble is a fully distributed networking and security observability platform. → 네트워크/보안 모니터링 플랫폼

https://cilium.io/blog/2019/11/19/announcing-hubble

 

# 확인
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
conntrack -L
conntrack -L |grep -v 2379

# 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 &

# CLI 로 Hubble API 상태 확인
hubble status

# 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

 

노드 간 파드 통신 확인

파드 생성 및 확인

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

 

파드 변수 지정

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

 

 

파드의 ARP 동작 확인 ← Hubble Web UI 모니터링

# 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=lxc335e04832afa

# 파드와 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

 

 

Node’s eBPF programs

# list of eBPF programs
c0bpf net show
c0bpf net show | grep $LXC
lxc335e04832afa(12) tcx/ingress cil_from_container prog_id 1529 link_id 26 
lxc335e04832afa(12) tcx/egress cil_to_container prog_id 1531 link_id 27 

# 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
1531: sched_cls  name cil_to_container  tag 3f1e92871a2c4013  gpl
	loaded_at 2024-10-20T07:47:27+0000  uid 0
	xlated 1712B  jited 1015B  memlock 4096B  map_ids 66,239
	btf_id 474

c0bpf map list
...
66: percpu_hash  name cilium_metrics  flags 0x1
	key 8B  value 16B  max_entries 1024  memlock 19384B
...
239: prog_array  name cilium_calls_00  flags 0x0
	key 4B  value 4B  max_entries 50  memlock 720B
	owner_prog_type sched_cls  owner jited
...

 

서비스 통신 확인

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

 

- Socket operations : BPF socket operations programroot 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 맵을 이용해서 목적지 주소와 포트를 다시 서비스 주소와 포트로 변환함.

 

https://www.tkng.io/services/clusterip/dataplane/ebpf/

 

서비스 생성 및 접속 확인 : 파드 내에서 바로 DNAT

서비스 생성

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

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
16   10.108.12.195:80      ClusterIP      1 => 172.16.2.157:80
                                          2 => 172.16.1.234:80
c0 bpf lb list
SERVICE ADDRESS       BACKEND ADDRESS
10.108.12.195:80      0.0.0.0:0 (16) [ClusterIP, non-routable]
                      172.16.1.234:80 (16)
                      172.16.2.157:80 (16)
# 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

 

Socket-Based LoadBalancing 관련 설정값 확인 및 Cgroup 관련 정보 확인

# Socket-Based LoadBalancing 관련 설정들 확인
c0 status --verbose
...
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

# cgroup root 경로 확인
tree /run/cilium/cgroupv2 -L 1
tree /run/cilium/cgroupv2 -L 2
cilium config view | grep cgroup
cgroup-root                                    /run/cilium/cgroupv2

# eBPF cgroup 확인 : Socket based LB 와 관련
c0bpf cgroup tree
CgroupPath
ID       AttachType      AttachFlags     Name           
/sys/fs/cgroup
1081     cgroup_device   multi                                          
1498     tcx_ingress                     cil_to_host                    
1501     tcx_egress                      cil_from_host  

# cilium 파드의 Init Containers 에서 cgroup 마운트!
Init Containers:
  mount-cgroup:
    Container ID:  containerd://72e9d2ee9731e3536c893f9daaa7674809638e3d137f9eb0f46fe916c2aa2839
    Image:         quay.io/cilium/cilium:v1.16.3@sha256:62d2a09bbef840a46099ac4c69421c90f84f28d018d479749049011329aa7f28
    Image ID:      quay.io/cilium/cilium@sha256:62d2a09bbef840a46099ac4c69421c90f84f28d018d479749049011329aa7f28
    Port:          <none>
    Host Port:     <none>
    Command:
      sh
      -ec
      cp /usr/bin/cilium-mount /hostbin/cilium-mount;
      nsenter --cgroup=/hostproc/1/ns/cgroup --mount=/hostproc/1/ns/mnt "${BIN_PATH}/cilium-mount" $CGROUP_ROOT;
      rm /hostbin/cilium-mount
      
    State:          Terminated
      Reason:       Completed
      Exit Code:    0
      Started:      Sun, 20 Oct 2024 15:45:34 +0900
      Finished:     Sun, 20 Oct 2024 15:45:34 +0900
    Ready:          True
    Restart Count:  0
    Environment:
      CGROUP_ROOT:  /run/cilium/cgroupv2
      BIN_PATH:     /opt/cni/bin
    Mounts:
      /hostbin from cni-path (rw)
      /hostproc from hostproc (rw)
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-p6bcr (ro)

# mount-cgroup 로그 확인
kubetail -n kube-system -c mount-cgroup --since 12h
...
[cilium-lnwcr] time="2024-10-20T15:45:52+09:00" level=info msg="Mounted cgroupv2 filesystem at /run/cilium/cgroupv2" subsys=cgroups 
[cilium-jmr7d] time="2024-10-20T15:45:33+09:00" level=info msg="Mounted cgroupv2 filesystem at /run/cilium/cgroupv2" subsys=cgroups 
...

 

strace 시스템 콜 트레이싱 도구를 통해 파드 내에서 동작 확인

# syacall 호출 확인
kubectl exec netpod -- strace -c curl -s $SVCIP
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 28.74    0.000971          12        79           mmap
 16.37    0.000553           9        56        32 open
 10.33    0.000349          29        12           fstat
  5.83    0.000197           6        31           rt_sigaction
  5.62    0.000190           7        27           munmap
  5.33    0.000180           6        27           close
  3.17    0.000107           3        27           read
  2.81    0.000095          15         6           poll
  2.69    0.000091           3        23           fcntl
  2.66    0.000090           2        31           lseek
  2.63    0.000089           8        10           readv
  2.34    0.000079           5        14           mprotect
  2.31    0.000078          78         1         1 connect
  2.25    0.000076           6        12           rt_sigprocmask
  1.30    0.000044          44         1           sendto
  1.12    0.000038           9         4           getsockname
  0.89    0.000030           7         4           setsockopt
  0.50    0.000017           3         5           getrandom
  0.50    0.000017          17         1           socket
  0.44    0.000015           5         3           brk
  0.44    0.000015          15         1           writev
  0.41    0.000014           4         3         3 ioctl
  0.38    0.000013          13         1           getsockopt
  0.27    0.000009           9         1           recvfrom
  0.27    0.000009           9         1           arch_prctl
  0.21    0.000007           7         1           pipe
  0.18    0.000006           6         1           set_tid_address
  0.00    0.000000           0         1           execve
  0.00    0.000000           0         1           getuid
  0.00    0.000000           0         1           getgid
  0.00    0.000000           0         2           geteuid
  0.00    0.000000           0         1           getegid
------ ----------- ----------- --------- --------- ----------------
100.00    0.003379           8       389        36 total


# 출력 내용을 편집기에서 확인(검색)
kubectl exec netpod -- strace -s 65535 -f -tt curl -s $SVCIP
------------------------------------------------------------
08:19:14.846995 connect(5, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("10.10.124.15")}, 16) = -1 EINPROGRESS (Operation in progress) # 소켓 연결 시도
08:19:14.847653 getsockname(5, {sa_family=AF_INET, sin_port=htons(41312), sin_addr=inet_addr("172.16.0.132")}, [128 => 16]) = 0 # 소켓 주소 가져오기
...
08:19:14.852497 getsockopt(5, SOL_SOCKET, SO_ERROR, [0], [4]) = 0 # 소켓 연결 성공
08:19:14.852940 getsockname(5, {sa_family=AF_INET, sin_port=htons(41312), sin_addr=inet_addr("172.16.0.132")}, [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 delete pod --all && kubectl delete svc svc

 

참고자료

https://cilium.io/

 

Cilium - Cloud Native, eBPF-based Networking, Observability, and Security

Cloud Native, eBPF-based Networking, Observability, and Security

cilium.io

https://www.tkng.io/services/clusterip/dataplane/ebpf/

 

eBPF :: The Kubernetes Networking Guide

eBPF eBPF has emerged as a new alternative to IPTables and IPVS mechanisms implemented by kube-proxy with the promise to reduce CPU utilization and latency, improve throughput and increase scale. As of today, there are two implementations of Kubernetes Ser

www.tkng.io

https://github.com/cilium/cilium

 

GitHub - cilium/cilium: eBPF-based Networking, Security, and Observability

eBPF-based Networking, Security, and Observability - cilium/cilium

github.com

https://cilium.io/blog/2019/11/19/announcing-hubble

 

Announcing Hubble - Network, Service & Security Observability for Kubernetes

Hubble is a fully distributed networking and security observability platform for cloud native workloads. Hubble is open source softwa...

cilium.io

 

'IT > Container&k8s' 카테고리의 다른 글

AWS VPC CNI  (1) 2024.11.03
K8S Service Mesh Istio  (6) 2024.10.20
K8s Gateway API  (0) 2024.10.13
K8s Ingress(L7)  (6) 2024.10.13
K8S IPVS Proxy 모드  (3) 2024.10.05
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2025/02   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28
글 보관함