티스토리 뷰
안녕하세요. 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 을 사용함
- cilium — eks anywhere 에 탑재. 별도 encapsulation 없이 같은 대역에 네트워킹이 가능하도록 함
- calico — bgp 등에 사용됨. 현재 vxlan 안쓰는 방향으로 진행하며 data plane쪽만 eBPF로 진행
- loxiLB — 현재 eks anywhere에서 metal LB를 loxiLB로 대체하려 시도 중. go 언어 기반으로 동작
Cillium Architecture
실습환경
- 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. → 네트워크/보안 모니터링 플랫폼
# 확인
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 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 맵을 이용해서 목적지 주소와 포트를 다시 서비스 주소와 포트로 변환함.
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
참고자료
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
- AWS
- security
- cni
- gcp serverless
- IaC
- 도서
- k8s
- SDWAN
- controltower
- EKS
- NFT
- VPN
- 파이썬
- NW
- GKE
- cloud
- 혼공단
- 국제 개발 협력
- S3
- AI
- operator
- GCP
- terraform
- 혼공챌린지
- k8s cni
- handson
- k8s calico
- OS
- PYTHON
- 혼공파
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |