[KANS] 3기 8주 – Cilium CNI

CloudNet@팀에서 진행하는 쿠버네티스 네트워크 스터디 3기 참가글입니다.

‘쿠버네티스’ 네트워크에 대해서 직접 실습을 통해 장애 시 해결 능력을 갖추고, 학습 내용을 전파 및 공유합니다!


1. Cilium 소개

Cilium은 Docker와 Kubernetes와 같은 Linux 컨테이너 관리 플랫폼을 사용하여 배포된 애플리케이션 서비스 간의 네트워크 연결을 투명하게 보호하는 오픈 소스 소프트웨어입니다.

Cilium is open source software for transparently securing the network connectivity between application services deployed using Linux container management platforms like Docker and Kubernetes.

What is Cilium?, Cilium Docs

Cilium은 

  • eBPF (Berkeley Packet Filter)를 기반으로
  • Pod Network 환경 + 보안 을 제공하는
  • CNI Plugin

1.1. eBPF

Berkeley Packet Filter(BPF; BSD 패킷 필터, 클래식 BPF 또는 cBPF)는 운영 체제 수준에서 컴퓨터 네트워크 패킷을 캡처하고 필터링할 수 있는 네트워크 탭 및 패킷 필터이다. BPF는 주로 네트워크 모니터링 및 트래픽 필터링에 사용되며, 네트워크 성능에 영향을 최소화하면서 특정 조건에 맞는 패킷만을 선택적으로 분석할 수 있게 해줍니다.

The Berkeley Packet Filter (BPF; also BSD Packet Filterclassic BPF or cBPF) is a network tap and packet filter which permits computer network packets to be captured and filtered at the operating system level. 

Berkeley Packet Filter, Wikipedia


BPF는 Berkeley Packet Filter의 약자로 BPF(Berkeley Packet Filter)는 네트워크 트래픽을 분석해야 하는 프로그램을 위한 패킷 필터링 도구이다.


기존 Netfilter와 같은 리눅스 네트워크 스택은 복잡하고, 변경에 시간이 걸리며, 레이어를 우회하기 어렵다는 단점이 존재한다.

Linux Network Stack, Replacing iptables with eBPF in Kubernetes with Cilium


BPF는 커널 훅 방식을 통해, 커널에 훅을 삽입하여 패킷을 통제(필터링) 할 수 있으며, 다양한 영역에서 Hook 을 통해서 접근 할 수 있다.

BPF kernel hooks, Replacing iptables with eBPF in Kubernetes with Cilium


eBPF (Extended Berkeley Packet Filter)는 이러한 BPF가 확장된 기능이다. 기존 BPF 환경에서 애플리케이션이 시스템 호출을 통해 네트워크 트래픽을 처리하기 위해, 커널의 소켓레이어를 거쳐야 했다. 이에 비해 eBPF에서는 커널 내부에서 실행되어 소켓 레이어를 거치지 않고도 직접 트래픽을 처리할 수 있다.

Embedded in the Linux kernel, Cilium Blog


커널내에서 자유롭게 프로그래밍하여 적용할 수 있기 때문에, 단순 네트워크 통신 목적외에도 보안(필터링), 트레이싱, 모니터링(observability)등에도 활용할 수 있다.

1.2. Cilium

Migrating from MetalLB to Cilium, Isovalent


Cilium은 eBPF (Berkeley Packet Filter)를 기반으로 Pod Network 환경 + 보안 을 제공하는 CNI Plugin 이다.

  • Cilium eBPF 는 추가적인 App 이나 설정 변경 없이 리눅스 커널을 자유롭게 프로그래밍하여 동작 가능
    • Kernel Layer에서 동작하는 Bytecode를 안전하게 Kernel에 Loading(injection) 할 수 있다
  • 패킷 처리를 위해 모든 수신 패킷이 들어오는 NIC의 ingress TC hooks에 eBPF 프로그램을 삽입한다.
    • Linux TC(Traffic Control) : 커널에서 동작하는 패킷 스케줄러 – 참고
  • 아래 그림의 NIC 에 TC Hooks 에 eBPF 프로그램이 Attach 되어서 실행된다.
New GKE Dataplane V2 increases security and visibility for containers


  • 2개의 네트워크 모드: 터널 모드(VXLAN, GENEVE), 네이티브 라우팅 모드
    • 터널 모드: VXLAN(UDP 8472) 또는 Geneve(UDP 6081) 사용 트래픽 제어
    • 네이티브 라우팅 모드: Cilium보다는 외부 장비/설정 사용(라우터/BGP등)


1.3. Cilium Architecture

Cilium Github
  • 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)

실습환경 구성
EC2 on AWS

  • 구성
    • VPC 1개(퍼블릭 서브넷 2개)
    • EC2 인스턴스 3대 (Ubuntu 22.04 LTS, t3.xlarge – vCPU 4 , Mem 16)
    • testpc 1대는 t3.small
  • CloudFormation 스택 배포
# 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 접속 확인
    • K8S v1.30.5
    • 노드 OS(Ubuntu 22.04.5)
    • CNI(Cilium)
    • kube-proxy 미설치
  • [자신의 PC] 배포된 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
  • k8s-s) 접속 후 확인 : ssh -i <> ubuntu@3.38.151.222
# 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@54.180.243.135
ip -br -c addr
hostnamectl 
curl localhost

2. Cilium 배포

지난 istio 배포시, CLI(istioctl) 또는 helm 등의 배포 옵션이 있었다. 유사하게 Cilium 또한 CLI(cilium) 또는 helm을 통해 배포할 수 있다.

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

  • Cilium 설치 정보(w/Helm) 및 확인
# 모니터링
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 interface 및 관련 정책들 추가
  • Cilium CNI 구성 이후 추가된 CRD 목록 및 축약어 조회
  • ciliumnode(cn), ciliumendpoints(cnp)등 조회
  • cilium CLI를 통한 구성상태 확인 및 ip-masq-agent 설정

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

Cilium 기본 정보 확인

# cilium 버전 확인
cilium version

# cilium 상태 확인
cilium status

# kube-proxy 파드 확인 >> 없다!
kubectl get pod -A

# cilium 설정 정보 확인
kubectl get cm -n kube-system cilium-config -o yaml
cilium config view

# ciliumnodes(cn) 정보 확인
kubectl get cn
kubectl get cn k8s-m -o yaml

# 노드별 파드 대역 확인
kubectl get ciliumnodes -o yaml | grep podCIDRs -A1

# cilium 파드 확인
kubectl get pod -n kube-system -l k8s-app=cilium -owide

# cilium 엔드포인트 확인
kubectl get ciliumendpoints.cilium.io -A

--------------------------------------------
# cilium cli 도움말
c0 help

# cilium 파드의 cilium 상태 확인
c0 status
c1 status
c2 status
c0 status --verbose

# 각 노드에서 파드에 할당된 IP 확인
c0 status --verbose | grep Allocated -A5
c1 status --verbose | grep Allocated -A5
c2 status --verbose | grep Allocated -A5

# 엔드포인트 리스트 : ID, 정책, 라벨, IP 주소, 상태 확인
c2 endpoint list

# 노드 리스트
c0 node list

# BFP(Direct access to local BPF maps)
# BFP 터널 리스트 확인 : Overlay Mode 사용 시 터널 정보 출력
c0 bpf tunnel list
	TUNNEL         VALUE
	172.16.1.0:0   192.168.200.101:0
	172.16.2.0:0   192.168.200.102:0
c1 bpf tunnel list
	TUNNEL         VALUE
	172.16.2.0:0   192.168.200.102:0
	172.16.0.0:0   192.168.200.10:0
c2 bpf tunnel list
	TUNNEL         VALUE
	172.16.0.0:0   192.168.200.10:0
	172.16.1.0:0   192.168.200.101:0

# 해당 노드의 로컬 엔드포인트 리스트 : nodemac 은 해당 파드와 veth pair 인 인터페이스의 mac 주소이다!
c0 bpf endpoint list

# Connection tracking tables - List connection tracking entries
c0 bpf ct list global

# Flush all NAT mapping entries
c0 bpf nat flush

# List all NAT mapping entries
c0 bpf nat list

# service list 확인, Frontend 는 Service IP, Backend 는 Pod IP(endpoint IP)를 보여준다.
c0 service list

# List load-balancing configuration
c0 bpf lb list

# List reverse NAT entries
c0 bpf lb list --revnat

# List all open BPF maps
c0 map list
c0 map list --verbose
c0 map get cilium_lxc
c0 map get cilium_ipcache

# cilium monitor
c0 monitor -h
c0 monitor -v
c0 monitor -v --type l7

# Cilium will automatically mount cgroup v2 filesystem required to attach BPF cgroup programs by default at the path /run/cilium/cgroupv2
mount | grep cilium
tree /run/cilium/cgroupv2/ -L 1

# CNI Plugin 확인
tree /etc/cni/net.d/
cat /etc/cni/net.d/05-cilium.conf

# Manage IP addresses and associated information - IP List
c0 ip list

# IDENTITY :  1(host), 2(world), 4(health), 6(remote), 파드마다 개별 ID를 가지는 것으로 보인다!
c0 ip list -n

# 엔드포인트 설정 확인 및 변경
c0 endpoint config <엔트포인트ID>

# 엔드포인트 상세 정보 확인
c0 endpoint get <엔트포인트ID>

# 엔드포인트 로그 확인
c0 endpoint log <엔트포인트ID>

# Show bpf filesystem mount details
c0 bpf fs show

# bfp 마운트 폴더 확인
tree /sys/fs/bpf

# List contents of a policy BPF map : Dump all policy maps
c0 bpf policy get --all
c0 bpf policy get --all -n

# BPF datapath traffic metrics
c0 bpf metrics list

# Manage the IPCache mappings for IP/CIDR <-> Identity
c0 bpf ipcache list

# Manage compiled BPF template objects
c0 bpf sha list

# Get datapath SHA header
c0 bpf sha get <Datapath SHA>

# Retrieve information about an identity
c0 identity list

# 엔드포인트 기준 ID
c0 identity list --endpoints

# Access metric status
c0 metrics list

Network 기본 정보 확인

4.2 Host Networking, Ctrip Network Architecture Evolution in the Cloud Computing Era
# 네트워크 인터페이스 정보 확인
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


  • Cilium Container Networking Control Flow
Cilium Container Networking Control Flow

결과

  • Hubble Web UI 접근
  • clium cli 명령어들을 통한 내부 리소스간 설정상황 확인
  • 워커노드(k8s-w1/w2)의 네트워크 설정 상태 (ip -c link/route) 확인
    • cilium_net / cilium_host 확인


2.3. Hubble UI & CLI

Hubble은 완전히 분산된 네트워킹 및 보안 가시성 플랫폼으로, Cilium과 eBPF 위에 구축되어 서비스 간 통신과 동작뿐만 아니라 네트워킹 인프라에 대해 완전히 투명한 방식으로 깊이 있는 가시성을 제공한다.

Hubble is a fully distributed networking and security observability platform. It is built on top of Cilium and eBPF to enable deep visibility into the communication and behavior of services as well as the networking infrastructure in a completely transparent manner.

What is Hubble?, Cilium Docs
  • Hubble: 통신 및 서비스와 네트워킹 인프라의 동작에 대한 심층적인 가시성을 완전히 투명한 방식으로 제공하는 관찰성을 제공
    • Announcing Hubble – Network, Service & Security Observability for Kubernetes
    • Hubble is a fully distributed networking and security observability platform. → 네트워크/보안 모니터링
    • It is built on top of Cilium and eBPF to enable deep visibility into the communication and behavior of services as well as the networking infrastructure in a completely transparent manner. without requiring the application to change in any way. → 애플리케이션의 코드 수정 등 추가 설정 없이 동작
    • containerized workloads as well as more traditional workloads such as virtual machines and standard Linux processes. → VM/서버도 모니터링 가능
    • By leveraging Linux eBPF, Cilium retains the ability to transparently insert security visibility + enforcement, but does so in a way that is based on service / pod / container identity (in contrast to IP address identification in traditional systems) and can filter on application-layer (e.g. HTTP). → 전통적인 IP 기반은 모니터링/통제가 아니라 서비스/파드/ID 기반으로 모니터링/통제를 제공
    • 기본적으로 Hubble API는 Cilium 에이전트가 실행되는 개별 노드의 범위 내에서 작동합니다. 이는 네트워크 통찰력을 로컬 Cilium 에이전트가 관찰한 트래픽으로 제한합니다. Hubble CLIhubble)를 사용하여 로컬 Unix Domain Socket을 통해 제공된 Hubble API를 쿼리할 수 있습니다. Hubble CLI 바이너리는 기본적으로 Cilium 에이전트 포드에 설치됩니다.
    • Hubble Relay를 배포하면 전체 클러스터 또는 ClusterMesh 시나리오의 여러 클러스터에 대한 네트워크 가시성이 제공됩니다. 이 모드에서 Hubble 데이터는 Hubble CLI( hubble)를 Hubble Relay 서비스로 지정하거나 Hubble UI를 통해 액세스할 수 있습니다. Hubble UI는 L3/L4 및 L7 계층에서 서비스 종속성 그래프를 자동으로 검색할 수 있는 웹 인터페이스로, 사용자 친화적인 시각화 및 서비스 맵으로서의 데이터 흐름 필터링을 허용합니다.
  • Prometheus를 통한 메트릭 관찰: 주요 메트릭이 Prometheus를 통해 기존 대시보드와 통합될 수 있도록 내보내짐
  • Hubble Relay를 사용한 클러스터 수준의 관찰가능성
Cluster-wide observability with Hubble Relay
  • 서비스 종속성 그래프 예시
  • 다양한 메트릭(네트워크, HTTP, DNS 등) 모니터링
  • 통제 정책
    • Allow all HTTP requests with method GET and path /public/.*. Deny all other requests.
    • Allow service1 to produce on Kafka topic topic1 and service2 to consume on topic1. Reject all other Kafka messages.
    • Require the HTTP header X-Token: [0-9]+ to be present in all REST calls.

Hubble UI/CLI 접근 및 확인

# 확인
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

결과

  • Hubble 포트포워딩 설정이후 api요청을 통한 모니터링 결과 확인

3. 노드 간 파드 통신 확인

Life of a Packet (Cilium)

  • Endpoint to Endpoint
    • pod? ciliumentity? 암튼 endpoint=pod
    • 내부간 통신
  • Egress from Endpoint
    • 내부에서 외부로의 접근
  • Ingress to Endpoint
    • 외부에서 내부로의 접근


파드 생성 및 확인

cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
  name: netpod
  labels:
    app: netpod
spec:
  nodeName: k8s-s
  containers:
  - name: netshoot-pod
    image: nicolaka/netshoot
    command: ["tail"]
    args: ["-f", "/dev/null"]
  terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Pod
metadata:
  name: webpod1
  labels:
    app: webpod
spec:
  nodeName: k8s-w1
  containers:
  - name: container
    image: traefik/whoami
  terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Pod
metadata:
  name: webpod2
  labels:
    app: webpod
spec:
  nodeName: k8s-w2
  containers:
  - name: container
    image: traefik/whoami
  terminationGracePeriodSeconds: 0
EOF

# 확인
kubectl get pod -o wide
c0 status --verbose | grep Allocated -A5
c1 status --verbose | grep Allocated -A5
c2 status --verbose | grep Allocated -A5

kubectl get ciliumendpoints
kubectl get ciliumendpoints -A
c0 endpoint list
c0 bpf endpoint list
c0 map get cilium_lxc
c0 ip list

파드 변수 지정

# 테스트 파드들 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 모니터링

  • hubble cli 확인
# 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
...

결과

  • 테스트 리소스 생성 이후 접근등 cilium 설정 반영 확인
  • pod의 ARP 동작 확인
    • 통신대상 위치등의 기록을 hubble UI/CLI에서 확인할 수 있다
  • cilium cli를 통해 목적지등을 볼 수 있지만, nic 설정등에서는 대상 찾기어려움
  • node의 eBPF 프로그램 확인

4. 서비스 통신 상태 확인

Socket-Based LoadBalancing – 한국블로그설명, Cilium.io 블로그

일반적인 로드 밸런시은 아래 두 방법 중 한가지로 수행

  • 클라이언트 사이드:
    • 앱이 클라이언트 측에서 로드밸런싱을 수행하고 목적지로 직접 연결
    • 최초 연결시 한번 로드밸런싱 수행, 이후 추가적 오버헤드 없음
    • 앱에게 로드밸런싱 상황이 투명(명확)하지 않음
  • 네트워크 사이드: (서버사이드?)
    • 중간 장치를 통해 특정 서비스 IP로 요청 변환하여 수행
    • 클라이언트사이드에 비해 투명(명확)함
    • 요청/응답시 모두 각 네트워크패킷의 IP 변환의 필요

실리움 1.6부터는 두 방식의 장점을 결합한 소켓 기반 로드밸런싱 도입

  • 투명성
    • 로드밸런싱은 앱에있어 100% 투명함 유지
    • 서비스가 표준적인 k8s svc 정의에 따라 정의됨
  • 고효율
    • connect(2) 시스템 콜내에서 주소를 변환하여 소켓 수준의 로드밸런싱 수행
    • 연결 설정 시 한번만 로드밸런싱 비용이 발생하고 이후 연결 지속시간은 추가 변환 필요 X
    • 앱이 백엔드와 직접 통신하는것과 동일한 성능


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


  • 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 맵을 이용해서 목적지 주소와 포트를 다시 서비스 주소와 포트로 변환함.
eBPF, TKNG(The Kubernetes Networking Guide)


서비스 생성 및 접속 확인 > 파드 내에서 바로 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

결과 확인

  • 트래픽 발생 시 SVC는 보이지 않고, DNAT된 대상 앱(web-pod) IP만 확인
  • clilium 명령어를 통한 구성상황 조회 시 Frontend의 service로의 요청이 Backend인 web-pod로의 설정 확인 가능
  • Socket-Based LoadBalancing 설정 확인
  • cgroup의 root 경로 확인
  • cilium pod의 Init Container 중 mount-cgroup 컨테이너만 조회
    • kubectl get pod -n kube-system cilium-7c7gj -o json | jq '.spec.initContainers[] | select(.name == "mount-cgroup")'
    • namespace를 넘나드며 CGROUP_ROOT를 마운트
  • 마운트 기록
  • strace (socket trace?) 로 조회한 요청시 처리기록
    • connect: 10.10.44.8 > svcIP
    • getsockname: 172.16.1.99 > web-pod1IP
    • getsockopt: ~ = 0 (연결 성공)
    • getsockname *3: 연결재확인

5. Prometheus & Grafana

Cilium 구성요소의 작동상황을 확인하기위해, prometheus로 메트릭을 내보낼 수 있고 Grafana를 통해 시각화 할 수 있다.+

CiliumHubble, and Cilium Operator의 구성요소는 기본적으로 메트릭을 제공하지 않으며, (Helm기준) 별도설정을 통해 각 99629965, and 9963 포트로 메트릭을 제공할 수 있다.

설정

# 배포
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"

결과

  • Prometheus에서 cilium_, hubble_ 로 시작하는 메트릭 집계
  • Grafana에서 기본 4개의 대시보드 생성 및 시각화
    • Cilium Metric, Cilium Operator, Hubble, Hubble L7 HTTP Metrics by Workload

6. Network Policy (L3, L4, L7)

Cilium은 여러수준의 보안정책을 제공한다. 개별적 또는 결합하여 사용할 수 있다.

  1. 신원 기반(Identity-Based)
  • 엔드포인트 간의 연결 정책 설정(L3)
  • ex)
    • role=frontend 레이블의 ep는 role=backend 레이블의 ep와 통신가능

2. 접근 포트 제한(Restriction of accessible ports)

  • 들어오고/나가는 연결 모두에 대한 포트 제한(L4)
  • ex)
    • role=frontend 레이블의 ep는 포트 443(https)만 나가기 허용
    • role=backend 레이블의 ep는 포트 443(https)의 요청만 허용

    3. 앱 프로토콜 수준의 정밀 제어 (Fine grained access control on application protocol)

    • HTTP 또는 RPC
    • ex)
      • role=frontend 레이블의 ep는 role=backend 레이블의 ep에 대해
      • GET /userdata/[0-9]+ 만 허용 / 그 외 모든 요청 거부
    • 실질적인 구현은 envoy proxy 추가(Proxy Injection)에 의해 작동
    • Prefilter: XDP 프로그램으로, 최상의 성능을 위해 네트워크에서 트래픽을 필터링하는 데 사용되는 일련의 prefilter 규칙 제공
    • Endpoint Policy: 정책에 따라 패킷을 차단/전달하거나, 서비스로 전달하거나, L7로 정책을 전달. Cilium 데이터 경로는 패킷을 식별자에 매핑하고 L3 및 L4 정책을 적용.
    • L7 Policy: L7 Policy 객체는 proxy 트래픽을 Cilium userspace proxy 인스턴스로 전달. Cilium은 Envoy 인스턴스를 userspace proxy로 사용. Envoy는 이후 트래픽을 전달하거나 구성된 L7 정책에 따라 적절한 거부 메시지를 생성.
    • > L7 정책은 커널 hookpoint와 사용자 userspace proxy 사용으로 인해 성능이 조금 떨어질 수 있음
    • 스타워즈에서 영감 받은 예제 :
      • 디플로이먼트(웹 서버, deathstar, replicas 2), 파드(xwing, tiefighter), 서비스(ClusterIP, service/deathstar)
    # 배포
    kubectl create -f https://raw.githubusercontent.com/cilium/cilium/1.16.3/examples/minikube/http-sw-app.yaml
    kubectl get all
    
    # 파드 라벨 확인
    kubectl get pod --show-labels
    NAME                         READY   STATUS    RESTARTS   AGE    LABELS
    deathstar-689f66b57d-4rwkf   1/1     Running   0          113s   app.kubernetes.io/name=deathstar,class=deathstar,org=empire,pod-template-hash=689f66b57d
    deathstar-689f66b57d-8p2l5   1/1     Running   0          113s   app.kubernetes.io/name=deathstar,class=deathstar,org=empire,pod-template-hash=689f66b57d
    tiefighter                   1/1     Running   0          113s   app.kubernetes.io/name=tiefighter,class=tiefighter,org=empire
    xwing                        1/1     Running   0          113s   app.kubernetes.io/name=xwing,class=xwing,org=alliance
    
    # cilium endpoint 확인
    kubectl get ciliumendpoints
    c1 endpoint list
    c2 endpoint list
    
    # 데스스타 SVC(ClusterIP) 접속하여 웹 파드 연결 확인 >> Hubble UI 에서 실시간 확인해보자!
    kubectl exec xwing -- curl -s -XPOST deathstar.default.svc.cluster.local/v1/request-landing
    Ship landed
    
    kubectl exec tiefighter -- curl -s -XPOST deathstar.default.svc.cluster.local/v1/request-landing
    Ship landed
    
    # 확인
    hubble observe

    기본 배포 확인

    • xwing, tiefighter > deathstar 연결 가능

    Identity-Aware and HTTP-Aware Policy Enforcement
    Apply an L3/L4 Policy

    • Cilium 에서는 Endpoint IP 대신, 파드의 Labels(라벨)을 사용(기준)하여 보안 정책을 적용합니다
    • IP/Port 필터링을 L3/L4 네트워크 정책이라고 한다
    • 아래 처럼 ‘org=empire’ Labels(라벨) 부착된 파드만 허용해보자
    • Cilium performs stateful connection tracking 이므로 리턴 트래픽은 자동으로 허용됨
    # L3/L4 정책 생성
    cat <<EOF | kubectl apply -f - 
    apiVersion: "cilium.io/v2"
    kind: CiliumNetworkPolicy
    metadata:
      name: "rule1"
    spec:
      description: "L3-L4 policy to restrict deathstar access to empire ships only"
      endpointSelector:
        matchLabels:
          org: empire
          class: deathstar
      ingress:
      - fromEndpoints:
        - matchLabels:
            org: empire
        toPorts:
        - ports:
          - port: "80"
            protocol: TCP
    EOF
    
    # 정책 확인
    kubectl get cnp
    kc describe cnp rule1
    c0 policy get
    
    
    # 파드 curl 접속 시도 시 파드 sh 접속 후 curl 시도하자!
    # 데스스타 SVC(ClusterIP) 접속하여 웹 파드 연결 확인 >> Hubble UI 에서 drop 확인!
    kubectl exec tiefighter -- curl -s -XPOST deathstar.default.svc.cluster.local/v1/request-landing
    Ship landed
    
    kubectl exec xwing -- curl -s -XPOST deathstar.default.svc.cluster.local/v1/request-landing
    drop
    
    # hubble cli 모니터링 
    hubble observe --pod xwing
    hubble observe --pod tiefighter
    hubble observe --pod deathstar
    Dec  2 05:36:24.490: default/xwing:55464 <> default/deathstar-c74d84667-t7msh:80 Policy denied DROPPED (TCP Flags: SYN)
    Dec  2 05:36:24.490: default/xwing:55464 <> default/deathstar-c74d84667-t7msh:80 Policy denied DROPPED (TCP Flags: SYN)
    
    hubble observe --pod deathstar --verdict DROPPED
    Nov 30 15:23:47.721: default/xwing:60086 <> default/deathstar-c74d84667-ksnbd:80 Policy denied DROPPED (TCP Flags: SYN)
    Nov 30 15:23:47.721: default/xwing:60086 <> default/deathstar-c74d84667-ksnbd:80 Policy denied DROPPED (TCP Flags: SYN)
    Nov 30 15:27:40.250: default/tiefighter:41656 -> default/deathstar-c74d84667-ksnbd:80 http-request DROPPED (HTTP/1.1 PUT http://deathstar.default.svc.cluster.local/v1/exhaust-port)
    Nov 30 15:28:00.707: default/tiefighter:41666 -> default/deathstar-c74d84667-ksnbd:80 http-request DROPPED (HTTP/1.1 PUT http://deathstar.default.svc.cluster.local/v1/exhaust-port)
    
    Inspecting the Policy
    # If we run cilium endpoint list again we will see that the pods with the label org=empire and class=deathstar
    # now have ingress policy enforcement enabled as per the policy above.
    
    # endpoint list 에서 정책 적용 확인
    c1 endpoint list | grep deathstar
    c2 endpoint list
    ENDPOINT   POLICY (ingress)   POLICY (egress)   IDENTITY   LABELS (source:key[=value])                                              IPv6   IPv4           STATUS
               ENFORCEMENT        ENFORCEMENT
    312        Disabled           Disabled          18300      k8s:class=xwing                                                                 172.16.2.161   ready
                                                               k8s:io.cilium.k8s.namespace.labels.kubernetes.io/metadata.name=default
                                                               k8s:io.cilium.k8s.policy.cluster=default
                                                               k8s:io.cilium.k8s.policy.serviceaccount=default
                                                               k8s:io.kubernetes.pod.namespace=default
                                                               k8s:org=alliance
    
    1972       Enabled            Disabled          21144      k8s:class=deathstar                                                             172.16.2.66    ready
                                                               k8s:io.cilium.k8s.namespace.labels.kubernetes.io/metadata.name=default
                                                               k8s:io.cilium.k8s.policy.cluster=default
                                                               k8s:io.cilium.k8s.policy.serviceaccount=default
                                                               k8s:io.kubernetes.pod.namespace=default
                                                               k8s:org=empirec2 endpoint list

    L3/L4 결과

    • 기존 pod label 확인 및 org=empire만 허용 정책
    • Hubble UI) empire의 tiefighter는 접근 가능, aliance의 xwing은 불가
    • Hubble CLI) deathstar 로그
    • cilium CLI) 각 워커노드의 정책 확인

    Identity-Aware and HTTP-Aware Policy Enforcement
    Apply and Test HTTP-aware L7 Policy

    • HTTP L7 필터링을 적용 : 아래 처럼 PUT /v1/exhaust-port 요청을 차단!
    # 데스스타 SVC(ClusterIP) 접속
    kubectl exec tiefighter -- curl -s -XPUT deathstar.default.svc.cluster.local/v1/exhaust-port
    Panic: deathstar exploded
    ...
    
    # POST /v1/request-landing API 호출만 허용 정책으로 기존 정책 내용을 업데이트(configured)!
    cat <<EOF | kubectl apply -f - 
    apiVersion: "cilium.io/v2"
    kind: CiliumNetworkPolicy
    metadata:
      name: "rule1"
    spec:
      description: "L7 policy to restrict access to specific HTTP call"
      endpointSelector:
        matchLabels:
          org: empire
          class: deathstar
      ingress:
      - fromEndpoints:
        - matchLabels:
            org: empire
        toPorts:
        - ports:
          - port: "80"
            protocol: TCP
          rules:
            http:
            - method: "POST"
              path: "/v1/request-landing"
    EOF
    
    # 정책 확인
    kc describe ciliumnetworkpolicies
    c0 policy get
    
    # 모니터링
    c1 monitor -v --type l7
    c2 monitor -v --type l7
    <- Request http from 0 ([k8s:io.cilium.k8s.policy.cluster=default k8s:io.cilium.k8s.policy.serviceaccount=default k8s:io.kubernetes.pod.namespace=default k8s:org=empire k8s:class=tiefighter k8s:io.cilium.k8s.namespace.labels.kubernetes.io/metadata.name=default]) to 1972 ([k8s:class=deathstar k8s:org=empire k8s:io.cilium.k8s.namespace.labels.kubernetes.io/metadata.name=default k8s:io.kubernetes.pod.namespace=default k8s:io.cilium.k8s.policy.serviceaccount=default k8s:io.cilium.k8s.policy.cluster=default]), identity 42720->21144, verdict Denied PUT http://deathstar.default.svc.cluster.local/v1/exhaust-port => 403
     => 403
    hubble observe --pod deathstar
    hubble observe --pod deathstar --verdict DROPPED
    
    
    # 접근 테스트
    kubectl exec tiefighter -- curl -s -XPOST deathstar.default.svc.cluster.local/v1/request-landing
    Ship landed
    
    kubectl exec tiefighter -- curl -s -XPUT deathstar.default.svc.cluster.local/v1/exhaust-port
    Access denied
    
    ## hubble cli 에 차단 로그 확인
    hubble observe --pod deathstar --verdict DROPPED
    Feb 28 11:39:59.078: default/tiefighter:33762 -> default/deathstar-c74d84667-lf2wl:80 http-request DROPPED (HTTP/1.1 PUT http://deathstar.default.svc.cluster.local/v1/exhaust-port)
    
    hubble observe --pod deathstar --protocol http
    Feb 28 12:05:22.095: default/tiefighter:40428 -> default/deathstar-6f87496b94-cvv9r:80 http-request DROPPED (HTTP/1.1 PUT http://deathstar.default.svc.cluster.local/v1/exhaust-port)
    
    # 삭제
    kubectl delete -f https://raw.githubusercontent.com/cilium/cilium/1.16.3/examples/minikube/http-sw-app.yaml
    kubectl delete cnp rule1

    L7 결과

    • 특정 api 로의 요청 차단

    Leave a Reply