들어가며
이번 포스팅은 CloudNet@ 커뮤니티에서 주최하는 KANS 스터디 8주 차 주제인 "Cilium CNI"에 대해서 정리한 내용입니다.
실습환경 구성
이번 주차 실습은 지난 스터디와 마찬가지로 VPC 1개(퍼블릭 서브넷 2개), Kubernetes 클러스터 구성용 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=jhpark 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/devlos.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 인스턴스 정보
test PC 정보
쿠버네티스 환경에서 IP tables 기반 통신 매커니즘의 한계
Kubernetes는 iptables를 다음과 같은 용도로 사용합니다.
- kube-proxy - DNAT iptables 규칙을 통해 서비스와 로드 밸런싱을 구현하는 컴포넌트
- 대부분의 CNI 플러그인 - 네트워크 정책을 위해 iptables를 사용
하지만 IPtables는 다음과 같은 한계점이 있습니다.
- Iptables 업데이트는 모든 규칙을 하나의 트랜잭션에서 다시 생성하고 업데이트해야만 합니다.
- 규칙 체인을 연결 리스트로 구현하기 때문에 모든 작업이 O(n)입니다. 다시말해서 추가되는 규칙을 적용하기 위해 전체 iptables list를 교체해야합니다.
(5,000개의 Service (40,000개의 규칙)이 있다면 하나의 Rule을 추가하는데 11분 정도가 소요된다고 합니다.) - Iptables에 의해 구현된 접근 제어 목록(ACL)을 구현하는 표준 방법은 일련의 규칙 목록을 사용하는 것이었습니다.
- IP와 포트를 일치시키는 것에 기반하며, L7 프로토콜에 대해서는 인지하지 못합니다.
- 새 IP 또는 포트를 매칭해야 할 때마다 규칙을 추가하고 체인을 변경해야 합니다.
- Kubernetes에서 높은 리소스 소비를 일으킵니다.
- 운영적인 측면에서 기하급수적으로 증가하는 iptables를 관리할 수 없습니다.
위에서 언급한 문제들로 인해, 높은 트래픽 상황이나 iptable 규칙에 많은 변경이 발생하는 시스템에서는 성능이 저하됩니다. 측정 결과 서비스 수가 증가할수록 예측할 수 없는 지연 시간과 성능 저하가 나타납니다.
Iptables의 라우팅 규칙이 기하급수적으로 증가한 상황입니다.
앞선 스터디들을 통해 Kubernetes Cluster에서 iptables로 동작하는 과정은 Netfilter 규칙을 통해 진행되므로, 라우팅, Nat 등 여러가지 성능저하 요인들이 있다는 것을 확인했습니다.
따라서 이러한 iptables의 라우팅 부하를 줄이고 더 나은 접근 방식을 적용하기 위해 BPF와 eBPF를 활용한 새로운 네트워크 구축 방법이 하나의 대안이 될 수 있습니다.
BPF란 무엇인가?
BPF(Berkeley Packet Filter)는 커널 수준에서의 패킷 처리 및 커널 동작을 효율적으로 확장하고 관찰하기 위한 기술입니다.
BPF는 원래 네트워크 패킷 필터링을 위해 1992년에 개발된 기술입니다. 주된 목적은 효율적으로 패킷을 필터링하여 필요한 패킷만 사용자 공간으로 전달하는 것이었습니다. 네트워크 패킷을 효율적으로 처리하기 위해 사용되는 일종의 가상 머신으로, 사용자 정의 필터 프로그램을 작성해 특정 조건을 만족하는 패킷만을 선별할 수 있게 해줍니다.
그렇기 때문에 BPF는 주로 패킷 캡처 및 필터링에 사용합니다. tcpdump가 BPF를 사용합니다. 사용자 공간에서 커널로의 패킷 전달을 최소화하여 성능을 향상시켰습니다.
출처 - https://www.tcpdump.org/papers/bpf-usenix93.pdf
해당 논문에서 BPF의 메커니즘에 대한 설명을 찾을 수 있습니다.
- 패킷이 네트워크 인터페이스에 도착하면, link-level driver가 기본적으로 패킷을 시스템 프로토콜 스택으로 전달합니다. 그러나 BPF가 활성화되어 있으면, 드라이버는 먼저 BPF에 패킷을 전달합니다.
- BPF는 각 참여 프로세스의 필터에 패킷을 전달하며, 이 필터는 사용자가 정의한 기준에 따라 패킷을 허용할지 여부를 결정합니다.
- 허용된 패킷에 대해, BPF는 해당 필터와 연결된 버퍼에 필요한 데이터를 복사합니다.
- 이후, link-level driver가 다시 제어권을 얻습니다. 패킷이 로컬 호스트로 전송된 것이 아니라면 인터럽트에서 반환하고, 그렇지 않다면 정상적인 프로토콜 처리가 계속 진행됩니다.
그렇다면 eBPF(Extended Berkeley Packet Filter)란 무엇인가?
eBPF는 기존 BPF의 확장 버전으로, 단순한 패킷 필터링을 넘어 커널의 거의 모든 부분을 관찰하고 제어할 수 있게 해줍니다. eBPF를 통해 사용자 공간에서 작성된 프로그램을 커널의 여러 지점에 동적으로 로드하고 실행할 수 있습니다.
eBPF의 특징은 다음과 같습니다.
- 확장성: 커널 내에서 다양한 이벤트에 반응하는 프로그램을 동적으로 로드하고 실행할 수 있습니다. 패킷 필터링뿐만 아니라, 성능 모니터링, 네트워크 트래픽 관리, 보안 모니터링 등에도 사용됩니다.
- 고성능: eBPF 프로그램은 커널 내에서 직접 실행되기 때문에, 사용자 공간과 커널 간의 전환 오버헤드가 줄어들어 높은 성능을 제공합니다.
- 안전성: eBPF 프로그램은 커널로 로드되기 전에 검증기를 거치며, 이 검증기는 프로그램이 안전하게 실행되도록 보장합니다. 덕분에 커널의 안정성을 저해하지 않고 실행할 수 있습니다.
- 유연한 활용: 네트워크 관리, 추적 및 모니터링, 성능 최적화, 보안 정책 적용, DDoS 방지 등 다양한 용도로 활용될 수 있습니다.
eBPF의 활용을 통해 Netfilter의 복잡한 체인들을 모두 거치지 않고, 필요한 위치에서 빠르게 패킷을 처리하거나 필터링하는 것이 가능합니다. 즉, 패킷이 특정 체인들을 거치지 않고 보라색 원(eBPF TC hooks) 사이에서 직접 통신이 이루어질 수 있습니다.
이를 통해 eBPF는 패킷 처리 속도 향상과 성능 최적화의 이점을 제공합니다.
또한 eBPF는 다음과 같이 여러가지 관리적인 측면에서 사용될 수 있습니다.
Security (보안)
eBPF는 보안 정책을 강화하고, 시스템 호출(Syscall)을 모니터링 및 제어하는 데 사용됩니다. eBPF 프로그램이 커널 레벨에서 시스템 호출을 가로채고, 불법적이거나 의심스러운 시스템 호출을 탐지하고 막을 수 있으며, 보안 모니터링을 통해 잠재적인 보안 위협을 조기에 탐지하고 차단할 수 있습니다.
Tracing (추적)
eBPF는 커널과 사용자 공간의 프로세스를 모니터링하고 스택 추적(Stack Traces)을 기록할 수 있습니다. 커널과 프로세스 양쪽에서 eBPF가 동작하면서 스택 트레이스를 수집하고 있습니다. 이 기능은 애플리케이션의 성능 문제를 진단하거나, 특정 시스템 호출이 어디에서 발생하는지 추적하는 데 유용합니다.
Networking (네트워킹)
eBPF는 네트워크 패킷의 필터링, 수정, 라우팅 등을 처리하는 데 사용될 수 있습니다. 네트워크 인터페이스와 eBPF가 상호작용하는 것을 보여줍니다. 이는 eBPF가 네트워크 패킷의 흐름을 제어하고 고성능 네트워크 처리를 가능하게 한다는 의미입니다. XDP와 같은 기술을 사용해 네트워크 성능을 최적화할 수 있습니다.
Observability (관찰 가능성)
eBPF는 시스템의 메트릭, 히스토그램, 이벤트 등의 정보를 수집하고, 이를 관찰 및 분석할 수 있게 해줍니다. VFS(Virtual File System) 레벨에서 eBPF가 데이터를 수집하고, 이를 eBPF Maps라는 데이터 구조로 저장하는 것을 보여줍니다. 이 기능을 통해 실시간으로 시스템 성능 및 상태를 모니터링할 수 있으며, 관찰된 데이터를 바탕으로 성능 분석 및 문제 해결을 할 수 있습니다.
출처 - https://cilium.io/blog/2020/11/10/ebpf-future-of-networking/
Cilium에 사용되는 eBPF는 추가적인 App이나 설정 변경 없이 리눅스 커널을 자유롭게 프로그래밍하여 동작 가능합니다.
Cilium(실리움) CNI(Container Network Interface)란?
드디어 이번 스터디의 핵심적인 내용인 Cilium CNI에 대해 설명 드리겠습니다.
Cilium은 네트워크 및 보안 관리를 위해 eBPF 기술을 사용하는 오픈 소스 프로젝트입니다. 주로 Kubernetes 환경에서의 네트워크 연결, 로드 밸런싱, 보안 정책 관리 등을 간소화하고, 성능을 최적화하기 위해 설계되었습니다. 네트워크 성능을 최적화하고, L3/L4뿐만 아니라 L7 계층까지 다루는 세분화된 정책 관리를 가능하게 하며, 뛰어난 확장성과 가시성을 제공합니다.
Cilium CNI의 주요 기능과 특징은 다음과 같습니다.
- eBPF 기반 네트워킹
Cilium은 eBPF를 활용하여 네트워크 트래픽을 효율적으로 처리하고, Kubernetes 환경의 CNI(Container Network Interface) 플러그인으로 작동합니다. 기존의 iptables 기반의 네트워크 정책보다 성능과 유연성이 크게 향상됩니다. - 고성능 네트워크 정책 관리
Cilium은 L3/L4 뿐만 아니라, L7 계층에서의 정책을 지원합니다. 즉, IP 주소 및 포트뿐만 아니라 HTTP, gRPC, Kafka 등의 애플리케이션 레벨에서 세부적인 정책을 정의할 수 있습니다. - 네트워크 보안
Cilium은 네트워크 격리를 강화하고, 세분화된 네트워크 정책을 구현하여 각 Pod 또는 컨테이너 간의 통신을 제어할 수 있습니다. 이를 통해 마이크로서비스 아키텍처에서 보안 정책을 더욱 정밀하게 적용할 수 있습니다. - 통합 로드 밸런싱
eBPF를 활용한 네이티브 로드 밸런싱을 제공하여, Kubernetes의 kube-proxy를 대체하거나 보완할 수 있습니다. 이를 통해 성능 및 지연 시간을 줄이고, 더 나은 확장성을 제공합니다. - L7 가시성 및 모니터링
Cilium은 애플리케이션 레벨에서 발생하는 트래픽의 가시성을 제공하여, L7 레벨에서의 메트릭 및 통계를 수집할 수 있습니다. 이를 통해 네트워크 성능 분석 및 문제 해결이 용이해집니다.
주요 활용 사례는 Kubernetes에 마이크로서비스 보안 정책을 적용시키는 것입니다. eBPF를 활용해 네트워크 데이터 경로를 최적화하고, 로드 밸런싱과 네트워크 정책을 효율적으로 관리합니다.
Cilium은 그림과 같이 Kubernetes 환경에서 Kubelet을 통해 네트워크 정책과 설정을 받아와 eBPF를 활용하여 Pod간의 네트워크 연결, 네트워크 정책, 서비스 로드밸런싱, 로깅 및 모니터링, 매트릭 수집등을 수행합니다.
* Clium의 대부분의 기능을 사용하고 싶다면, Kernel 최신버전 (5.10 이상)을 추천합니다.
Cilium 배포
# 모니터링
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
설치 후에 iptables 룰을 살펴보면, 앞서 스터디에서 봤던 룰들과는 달리 룰이 거의 등록되어있지 않은 것을 확인할 수 있습니다.
설치후 네트워크 인터페이스 정보를 확인해 보면, 다음과 같이 cilium 관련 인터페이스들이 추가된 것을 확인할 수 있습니다.
네트워크 구조는 다음과 같습니다.
위에서 확인한 NIC을 보면 lxcx와 cilium_net, cilium_host 등을 확인할 수 있습니다.
kubectl get ciliumnodes 명령어로 노드 IP들을 확인해 보면 위에서 추가된 cilium_host@cilium_net IP와 동일합니다.
ens5의 NIC을 확인해보면 ena Driver 를 사용합니다. - 참고
Cilum CNI를 설치합니다.
# 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
# Native Routing 확인 : # 192.168.0.0/16 대역은 IP Masq 없이 라우팅
c0 status | grep KubeProxyReplacement
# enableIPv4Masquerade=true(기본값) , bpf.masquerade=true 확인
cilium config view | egrep 'enable-ipv4-masquerade|enable-bpf-masquerade'
# Masquerading 정보 확인
c0 status --verbose | grep Masquerading
#ipMasqAgent 켜기
helm upgrade cilium cilium/cilium --namespace kube-system --reuse-values --set ipMasqAgent.enabled=true
#
cilium config view | grep -i masq
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
Cilium 기본정보 확인
실습 - 단축기 설정 및 Hubble 접속주소 획득
우선 실습을 위해 사용할 변수와 단축기 설정 및 Hubble UI 웹 접속주소를 확인합니다.
# 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
istio와 비슷하게 생긴 Hubble UI가 확인됩니다. 여기서 모든 트래픽 들을 확인할 수 있고, 필터링 또한 가능합니다.
실습 - 자주 쓰는 명령어 확인
kubectl get pod -n kube-system -l k8s-app=cilium -o wide # Cilium 파드 정보 확인
kubectl -n kube-system rollout restart ds/cilium # Cilium 파드 재시작
kubectl delete pod -n kube-system -l k8s-app=cilium # Cilium 파드 강제 재시작
cilium config view # Cilium 설정 정보 확인
c0 status --verbose # Cilium 파드의 상태 확인
kubectl get ciliumendpoints -A # 모든 네임스페이스에서 Cilium 엔드포인트 확인
c0 endpoint list # Cilium 엔드포인트 목록 확인
c0 bpf endpoint list # BPF 엔드포인트 목록 확인
c0 map get cilium_lxc # 'cilium_lxc' 맵 내용 확인
c0 ip list # IP 리스트 확인
c0 bpf ipcache list # IP/CIDR와 Identity 매핑 정보 확인
c0 service list # 서비스 목록 확인
c0 bpf lb list # BPF 로드밸런서 목록 확인
c0 bpf lb list --revnat # Reverse NAT 로드밸런서 목록 확인
c0 bpf nat list # NAT 목록 확인
c0 map list # 모든 BPF 맵 목록 확인
c0 map list --verbose # 상세한 BPF 맵 목록 확인
c0 bpf policy get --all # 모든 정책 맵 내용 확인
c0 bpf policy get --all -n # 네임스페이스 별 정책 맵 내용 확인
c0 monitor -v # Cilium 모니터링 (상세 모드)
c0 monitor -v --type l7 # L7 계층 모니터링 (상세 모드)
실습 - Cilium 기본정보 확인 명령어
Cilium 기본정보 확인 명령어는 다음과 같습니다.
cilium version # Cilium 버전 확인
cilium status # Cilium 상태 확인
kubectl get pod -A # kube-proxy 파드 확인 >> 없다!
kubectl get cm -n kube-system cilium-config -o yaml # Cilium 설정 정보 확인
cilium config view # Cilium 설정 정보 확인
kubectl get cn # Ciliumnodes(cn) 정보 확인
kubectl get cn k8s-s -o yaml # 특정 노드의 상세 정보 확인
kubectl get ciliumnodes -o yaml | grep podCIDRs -A1 # 노드별 파드 대역 확인
kubectl get pod -n kube-system -l k8s-app=cilium -owide # Cilium 파드 정보 확인
kubectl get ciliumendpoints.cilium.io -A # 모든 네임스페이스에서 Cilium 엔드포인트 확인
# cilium cli 도움말
c0 help # Cilium CLI 도움말 확인
# cilium 파드의 cilium 상태 확인
c0 status # Cilium 파드 상태 확인 (노드 0)
c1 status # Cilium 파드 상태 확인 (노드 1)
c2 status # Cilium 파드 상태 확인 (노드 2)
c0 status --verbose # Cilium 파드 상세 상태 확인 (노드 0)
# 각 노드에서 파드에 할당된 IP 확인
c0 status --verbose | grep Allocated -A5 # 노드 0의 할당된 IP 확인
c1 status --verbose | grep Allocated -A5 # 노드 1의 할당된 IP 확인
c2 status --verbose | grep Allocated -A5 # 노드 2의 할당된 IP 확인
# 엔드포인트 리스트 : ID, 정책, 라벨, IP 주소, 상태 확인
c2 endpoint list # 노드 2의 엔드포인트 목록 확인
# 노드 리스트
c0 node list # Cilium 노드 목록 확인
# BFP(Direct access to local BPF maps)
c0 bpf tunnel list # BPF 터널 리스트 확인 (노드 0)
c1 bpf tunnel list # BPF 터널 리스트 확인 (노드 1)
c2 bpf tunnel list # BPF 터널 리스트 확인 (노드 2)
# 해당 노드의 로컬 엔드포인트 리스트
c0 bpf endpoint list # 노드 0의 로컬 엔드포인트 리스트 확인
# Connection tracking tables - List connection tracking entries
c0 bpf ct list global # 글로벌 connection tracking 테이블 확인
# Flush all NAT mapping entries
c0 bpf nat flush # 모든 NAT 매핑 엔트리 초기화
# List all NAT mapping entries
c0 bpf nat list # 모든 NAT 매핑 엔트리 확인
# service list 확인
c0 service list # 서비스 목록 확인, Frontend는 Service IP, Backend는 Pod IP(Endpoint IP) 표시
# List load-balancing configuration
c0 bpf lb list # 로드밸런싱 구성 목록 확인
# List reverse NAT entries
c0 bpf lb list --revnat # Reverse NAT 엔트리 목록 확인
# List all open BPF maps
c0 map list # 모든 BPF 맵 목록 확인
c0 map list --verbose # 상세한 BPF 맵 목록 확인
c0 map get cilium_lxc # 'cilium_lxc' 맵 내용 확인
c0 map get cilium_ipcache # 'cilium_ipcache' 맵 내용 확인
# cilium monitor
c0 monitor -h # Cilium 모니터 도움말 확인
c0 monitor -v # Cilium 모니터링 (상세 모드)
c0 monitor -v --type l7 # 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 # Cilium 관련 마운트 확인
tree /run/cilium/cgroupv2/ -L 1 # cgroup v2 마운트 구조 확인
# CNI Plugin 확인
tree /etc/cni/net.d/ # CNI 플러그인 구성 확인
cat /etc/cni/net.d/05-cilium.conf # Cilium CNI 설정 확인
# Manage IP addresses and associated information - IP List
c0 ip list # IP 리스트 관리
# IDENTITY : 1(host), 2(world), 4(health), 6(remote), 파드마다 개별 ID를 가지는 것으로 보인다!
c0 ip list -n # 네임스페이스별 ID 관리
# 엔드포인트 설정 확인 및 변경
c0 endpoint config <엔트포인트ID> # 엔드포인트 설정 확인 및 변경
# 엔드포인트 상세 정보 확인
c0 endpoint get <엔트포인트ID> # 엔드포인트 상세 정보 확인
# 엔드포인트 로그 확인
c0 endpoint log <엔트포인트ID> # 엔드포인트 로그 확인
# Show bpf filesystem mount details
c0 bpf fs show # BPF 파일 시스템 마운트 정보 확인
# bfp 마운트 폴더 확인
tree /sys/fs/bpf # 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 # BPF 데이터 경로 트래픽 메트릭 확인
# Manage the IPCache mappings for IP/CIDR <-> Identity
c0 bpf ipcache list # IP/CIDR와 Identity 매핑 관리
# Manage compiled BPF template objects
c0 bpf sha list # 컴파일된 BPF 템플릿 객체 관리
# Get datapath SHA header
c0 bpf sha get <Datapath SHA> # 데이터 경로 SHA 헤더 확인
# Retrieve information about an identity
c0 identity list # Identity 정보 확인
# 엔드포인트 기준 ID
c0 identity list --endpoints # 엔드포인트 기준 ID 정보 확인
# Access metric status
c0 metrics list # 메트릭 상태 접근
실습 - worker노드의 네트워크 기본정보 확인
# 네트워크 인터페이스 정보 확인
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 소개
링크 - https://cilium.io/blog/2019/11/19/announcing-hubble
Hubble은 네트워크 및 서비스의 통신과 네트워킹 인프라의 동작을 완전히 투명한 방식으로 심층적으로 가시성을 제공하는 모니터링 플랫폼이며 다음과 같은 특징을 가집니다
- 네트워크/보안 모니터링을 위한 완전 분산된 관찰성 플랫폼입니다.
- Cilium과 eBPF를 기반으로 하여 애플리케이션의 코드 수정 없이도 서비스 통신과 네트워킹 인프라를 심층적으로 모니터링할 수 있습니다.
- 컨테이너화된 워크로드뿐만 아니라, 가상 머신(VM)과 표준 Linux 프로세스 같은 전통적인 워크로드도 모니터링이 가능합니다.
- 전통적인 IP 기반 접근이 아닌 서비스/파드/컨테이너 ID 기반의 모니터링 및 통제를 제공합니다. 또한, HTTP 같은 애플리케이션 레이어에서도 필터링이 가능합니다.
- Hubble API는 기본적으로 Cilium 에이전트가 실행되는 각 노드 내에서 동작하며, 로컬 Cilium 에이전트가 관찰한 트래픽에 대해 네트워크 통찰력을 제공합니다. Hubble CLI를 통해 로컬 Unix Domain Socket을 쿼리할 수 있으며, CLI 바이너리는 Cilium 에이전트 파드에 기본으로 설치되어 있습니다.
- Hubble Relay를 배포하면 전체 클러스터 또는 ClusterMesh 시나리오의 여러 클러스터에 대한 네트워크 가시성을 제공할 수 있습니다. 이 모드에서는 Hubble CLI를 통해 Hubble Relay 서비스에 접근하거나, Hubble UI를 통해 L3/L4 및 L7 계층에서 서비스 종속성 그래프를 자동으로 탐색할 수 있는 웹 인터페이스를 제공합니다. 이 UI는 사용자 친화적인 시각화와 데이터 흐름 필터링을 지원합니다.
Hubble의 가시성은 grafana, prometheus에서 정보를 확인할 수 있기 때문에 가시성이 좋고, 커스텀화가 가능합니다.
실습 - 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
노드간 파드 통신
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 -- "
이제 실제로 통신을 발생시켜 Hubble 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
마지막으로 eBPF동작방식을 확인합니다.
# 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
...
서비스 통신 확인
Socket-Based LoadBalancing을 살펴봅니다. 그림 왼쪽은 DNAT을 거치는 네트워크 기반 로드밸런싱이고, 오른쪽은 DNAT 없이 통신하는 소켓쪽에서 바로 NAT 처리를 해보는 로드밸런싱입니다.
출처 - https://velog.io/@haruband/K8SCilium-Socket-Based-LoadBalancing-기법
소켓 namespace에서 바로 목적지 주소를 바꿔버립니다.
실습 - Socket-Based LoadBalancing
# 서비스 생성
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
strace 시스템 콜 트레이싱 도구를 통해 파드 내에서 동작을 확인해 봅니다.
# syacall 호출 확인
kubectl exec netpod -- strace -c curl -s $SVCIP
# 출력 내용을 편집기에서 확인(검색)
kubectl exec netpod -- strace -s 65535 -f -tt curl -s $SVCIP
------------------------------------------------------------
15:49:48.336836 connect(5, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("10.10.214.64")}, 16) = -1 EINPROGRESS (Operation in progress)
15:49:48.337352 getsockname(5, {sa_family=AF_INET, sin_port=htons(34530), sin_addr=inet_addr("172.16.0.235")}, [128 => 16]) = 0# 소켓 주소 가져오기
...
15:49:48.339291 getsockopt(5, SOL_SOCKET, SO_ERROR, [0], [4]) = 0
15:49:48.339406 getsockname(5, {sa_family=AF_INET, sin_port=htons(34530), sin_addr=inet_addr("172.16.0.235")}, [128 => 16]) = 0# 소켓 주소 한번더 확인
...
# 특정 이벤트 확인 : -e
kubectl exec netpod -- strace -e trace=connect curl -s $SVCIP
kubectl exec netpod -- strace -e trace=getsockname curl -s $SVCIP
Prometheus와 Grafana를 사용하여 Cilium 모니터링
cilium 공식 가이드라인을 참고하여 prometheus, grafana를 적용해 보았습니다.
링크 - https://docs.cilium.io/en/stable/observability/grafana/
# 배포
kubectl apply -f https://raw.githubusercontent.com/cilium/cilium/1.16.3/examples/kubernetes/addons/prometheus/monitoring-example.yaml
kubectl get all -n cilium-monitoring
# 파드와 서비스 확인
kubectl get pod,svc,ep -o wide -n cilium-monitoring
# NodePort 설정
kubectl patch svc grafana -n cilium-monitoring -p '{"spec": {"type": "NodePort"}}'
kubectl patch svc prometheus -n cilium-monitoring -p '{"spec": {"type": "NodePort"}}'
# Grafana 웹 접속
GPT=$(kubectl get svc -n cilium-monitoring grafana -o jsonpath={.spec.ports[0].nodePort})
echo -e "Grafana URL = http://$(curl -s ipinfo.io/ip):$GPT"
# Prometheus 웹 접속 정보 확인
PPT=$(kubectl get svc -n cilium-monitoring prometheus -o jsonpath={.spec.ports[0].nodePort})
echo -e "Prometheus URL = http://$(curl -s ipinfo.io/ip):$PPT"
Network Policy (L3, L4, L7)
Cilium Security는 3가지 타입의 보안을 제공합니다.
ID 기반 (Identity-Based) 연결 정책
엔드포인트 간의 연결 정책을 ID를 기반으로 설정하는 접근 방식입니다. 주로 Layer 3에서 동작하며, 엔드포인트의 IP 주소보다는 엔드포인트의 레이블과 ID에 의존합니다. 예를 들어, 엔드포인트가 특정 레이블을 가지고 있는 경우, 그 레이블을 기준으로 연결 정책을 설정할 수 있습니다.
예를들어 레이블이 role=frontend인 모든 엔드포인트는 레이블이 role=backend인 모든 엔드포인트에 연결할 수 있습니다.
포트 기반 제한 (Layer 4)
엔드포인트 간의 통신을 특정 포트로 제한하는 연결 정책입니다. 이 정책은 Layer 4에서 동작하며, 엔드포인트의 레이블과 함께 포트 번호를 기반으로 접근을 제어합니다. 이를 통해 수신 및 송신 연결에 대해 접근할 수 있는 포트를 명확히 정의할 수 있습니다.
예시는 다음과 같습니다.
- role=frontend 레이블을 가진 엔드포인트는 송신 연결을 오직 포트 443 (https)으로만 할 수 있음
- role=backend 레이블을 가진 엔드포인트는 수신 연결을 오직 포트 443 (https)으로만 받을 수 있음
애플리케이션 (HTTP) 기반 제어
애플리케이션 프로토콜 수준에서의 세밀한 접근 제어를 의미합니다. 이를 통해 HTTP 및 원격 프로시저 호출(RPC) 프로토콜에 대한 세밀한 보안 정책을 설정할 수 있습니다.
예시는 다음과 같습니다.
- role=frontend 레이블이 붙은 엔드포인트는 REST API 호출에서 오직 GET /userdata/[0-9]+만 허용되며, 그 외 모든 API 상호작용은 role=backend 레이블의 엔드포인트에서 제한됨
프록시 주입 (Proxy Injection) - Envoy
Cilium은 Layer 4 프록시(예: Envoy)를 이용하여 L7 레벨 네트워크 정책을 적용할 수 있습니다.
Network Policy 관련 eBPF Datapath
Cilium이 eBPF를 사용하여 Kubernetes 네트워크 정책을 효율적으로 적용하고, 서비스 간의 통신을 제어합니다. 사용자는 Cilium에 Pod Identity, 네트워크 정책, 로깅 구성을 제공하고, Cilium은 이를 기반으로 eBPF를 통해 커널 레벨에서 정책을 적용하며, 모든 정책 적용 결과는 로깅 시스템으로 전달됩니다.
1. Prefilter
- Prefilter는 XDP (eXpress Data Path) 프로그램을 기반으로 동작합니다.
- XDP는 매우 빠른 데이터 경로 처리를 위해 설계된 Linux 커널 기능으로, 네트워크 인터페이스 카드(NIC)에서 직접 실행됩니다.
- Prefilter는 네트워크에서 들어오는 트래픽에 대해 사전에 정의된 필터링 규칙을 사용하여 트래픽을 선별합니다.
- 이 작업은 성능을 극대화하기 위해 설계되었으며, 성능 향상에 기여합니다. 즉, Cilium은 불필요한 트래픽을 애초에 들어오지 못하게 차단하여, 보다 중요한 트래픽만 처리하도록 만듭니다.
2. Endpoint Policy
- Endpoint Policy는 Cilium 데이터 경로의 핵심 요소로, 패킷의 전달을 제어하는 역할을 합니다.
- 정책에 따라, 패킷이 다음과 같은 동작을 할 수 있습니다:
- 차단: 특정 조건에 따라 패킷을 차단.
- 전달: 패킷을 서비스나 목적지로 전달.
- L7 정책 전달: L7 레벨의 정책을 수행하기 위해 패킷을 L7 프록시로 전달.
- Cilium의 데이터 경로는 패킷을 ID로 매핑하고, 이를 통해 L3 (IP 기반) 및 L4 (포트 기반) 정책을 적용하여 보안을 강화합니다.
3. L7 Policy
- L7 Policy는 Layer 7 에서 트래픽을 제어하는 정책입니다.
- 이 정책은 트래픽을 Cilium의 유저 스페이스 프록시 인스턴스로 리다이렉트합니다.
- Cilium은 Envoy를 유저 스페이스 프록시로 사용하며, 이는 L7 트래픽의 필터링 및 제어를 담당합니다.
- Envoy는 설정된 L7 정책에 따라 트래픽을 전달하거나, 적절한 거부 메시지를 생성하여 보안 정책을 시행합니다.
실습 - Deploy the Demo Application - Docs
스타워즈에서 영감 받은 예제.. 디플로이먼트(웹 서버, 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
NAME SECURITY IDENTITY ENDPOINT STATE IPV4 IPV6
deathstar-689f66b57d-9mbhb 19092 ready 172.16.2.21
deathstar-689f66b57d-dqgxq 19092 ready 172.16.1.10
tiefighter 49569 ready 172.16.1.252
xwing 22863 ready 172.16.1.104
c1 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
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
# endpoint list 에서 정책 적용 확인
c1 endpoint list | grep deathstar
c2 endpoint list
Identity-Aware and HTTP-Aware Policy Enforcement (L7)
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
Oct 23 16:50:02.603: default/tiefighter:59092 (ID:49569) -> default/deathstar-689f66b57d-dqgxq:80 (ID:19092) http-request DROPPED (HTTP/1.1 PUT http://deathstar.default.svc.cluster.local/v1/exhaust-port)
hubble observe --pod deathstar --protocol http
Oct 23 16:50:02.603: default/tiefighter:59092 (ID:49569) -> default/deathstar-689f66b57d-dqgxq:80 (ID:19092) http-request DROPPED (HTTP/1.1 PUT http://deathstar.default.svc.cluster.local/v1/exhaust-port)
Bandwidth Manager
네트워크 트래픽 상한치를 통제하는 장치로 설명할 수 있습니다.
- 대역폭 관리: TCP 및 UDP 워크로드를 최적화하고 개별 파드에 대한 효율적인 속도 제한을 위해 **EDT(Earliest Departure Time)**와 eBPF를 사용합니다.
- kubernetes.io/egress-bandwidth 파드 어노테이션은 기본 호스트 네트워킹 장치에서 이그레스(출구) 트래픽에 대해 적용됩니다.
- kubernetes.io/ingress-bandwidth 어노테이션은 지원되지 않습니다.
- 직접 라우팅 모드와 터널링 모드 모두 지원합니다.
- 제한 사항: L7 Cilium 네트워크 정책
설정
# 인터페이스 tc qdisc 확인
tc qdisc show dev ens5
qdisc mq 0: root
qdisc fq_codel 0: parent :4 limit 10240p flows 1024 quantum 1514 target 5ms interval 100ms memory_limit 32Mb ecn drop_batch 64
qdisc fq_codel 0: parent :3 limit 10240p flows 1024 quantum 1514 target 5ms interval 100ms memory_limit 32Mb ecn drop_batch 64
qdisc fq_codel 0: parent :2 limit 10240p flows 1024 quantum 1514 target 5ms interval 100ms memory_limit 32Mb ecn drop_batch 64
qdisc fq_codel 0: parent :1 limit 10240p flows 1024 quantum 1514 target 5ms interval 100ms memory_limit 32Mb ecn drop_batch 64
# 설정
helm upgrade cilium cilium/cilium --namespace kube-system --reuse-values --set bandwidthManager.enabled=true
# 적용 확인
cilium config view | grep bandwidth
enable-bandwidth-manager true
# egress bandwidth limitation 동작하는 인터페이스 확인
c0 status | grep BandwidthManager
BandwidthManager: EDT with BPF [CUBIC] [ens5]
# 인터페이스 tc qdisc 확인 : 설정 전후 옵션값들이 상당히 추가된다
tc qdisc
tc qdisc show dev ens5
qdisc mq 8002: root
qdisc fq 8005: parent 8002:2 limit 10000p flow_limit 100p buckets 32768 orphan_mask 1023 quantum 18030b initial_quantum 90150b low_rate_threshold 550Kbit refill_delay 40ms timer_slack 10us horizon 2s horizon_drop
qdisc fq 8003: parent 8002:4 limit 10000p flow_limit 100p buckets 32768 orphan_mask 1023 quantum 18030b initial_quantum 90150b low_rate_threshold 550Kbit refill_delay 40ms timer_slack 10us horizon 2s horizon_drop
qdisc fq 8004: parent 8002:3 limit 10000p flow_limit 100p buckets 32768 orphan_mask 1023 quantum 18030b initial_quantum 90150b low_rate_threshold 550Kbit refill_delay 40ms timer_slack 10us horizon 2s horizon_drop
qdisc fq 8006: parent 8002:1 limit 10000p flow_limit 100p buckets 32768 orphan_mask 1023 quantum 18030b initial_quantum 90150b low_rate_threshold 550Kbit refill_delay 40ms timer_slack 10us horizon 2s horizon_drop
동작 확인
# 테스트를 위한 트래픽 발생 서버/클라이언트 파드 생성
cat <<EOF | kubectl apply -f -
---
apiVersion: v1
kind: Pod
metadata:
annotations:
# Limits egress bandwidth to 10Mbit/s.
kubernetes.io/egress-bandwidth: "10M"
labels:
# This pod will act as server.
app.kubernetes.io/name: netperf-server
name: netperf-server
spec:
containers:
- name: netperf
image: cilium/netperf
ports:
- containerPort: 12865
---
apiVersion: v1
kind: Pod
metadata:
# This Pod will act as client.
name: netperf-client
spec:
affinity:
# Prevents the client from being scheduled to the
# same node as the server.
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app.kubernetes.io/name
operator: In
values:
- netperf-server
topologyKey: kubernetes.io/hostname
containers:
- name: netperf
args:
- sleep
- infinity
image: cilium/netperf
EOF
# egress BW 제한 정보 확인
kubectl describe pod netperf-server | grep Annotations:
Annotations: kubernetes.io/egress-bandwidth: 10M
# egress BW 제한이 설정된 파드가 있는 cilium pod 에서 제한 정보 확인
c1 bpf bandwidth list
c2 bpf bandwidth list
IDENTITY EGRESS BANDWIDTH (BitsPerSec)
904 10M
c1 endpoint list
c2 endpoint list
ENDPOINT POLICY (ingress) POLICY (egress) IDENTITY LABELS (source:key[=value]) IPv6 IPv4 STATUS
ENFORCEMENT ENFORCEMENT
904 Disabled Disabled 21565 k8s:app.kubernetes.io/name=netperf-server 172.16.2.153 ready
# 트래픽 발생 >> Hubble UI 에서 확인
# egress traffic of the netperf-server Pod has been limited to 10Mbit per second.
NETPERF_SERVER_IP=$(kubectl get pod netperf-server -o jsonpath='{.status.podIP}')
kubectl exec netperf-client -- netperf -t TCP_MAERTS -H "${NETPERF_SERVER_IP}"
Recv Send Send
Socket Socket Message Elapsed
Size Size Size Time Throughput
bytes bytes bytes secs. 10^6bits/sec
131072 16384 16384 10.00 9.54 # 10Mbps 제한 확인!
# 5M 제한 설정 후 테스트
kubectl get pod netperf-server -o json | sed -e 's|10M|5M|g' | kubectl apply -f -
c1 bpf bandwidth list
c2 bpf bandwidth list
kubectl exec netperf-client -- netperf -t TCP_MAERTS -H "${NETPERF_SERVER_IP}"
Recv Send Send
Socket Socket Message Elapsed
Size Size Size Time Throughput
bytes bytes bytes secs. 10^6bits/sec
131072 16384 16384 10.09 4.56 # 4.5Mbps 제한 확인!
# 20M 제한 설정 후 테스트
kubectl get pod netperf-server -o json | sed -e 's|5M|20M|g' | kubectl apply -f -
kubectl exec netperf-client -- netperf -t TCP_MAERTS -H "${NETPERF_SERVER_IP}"
Recv Send Send
Socket Socket Message Elapsed
Size Size Size Time Throughput
bytes bytes bytes secs. 10^6bits/sec
131072 16384 16384 10.00 18.95 # 19Mbps 제한 확인!
tc qdisc show dev ens5
# 삭제
kubectl delete pod netperf-client netperf-server
c2에 나갈때 대역폭 설정이 되어있음
L2 Announcements / L2 Aware LB (Beta) ( MetalLB 같은 로드밸런서 대체..)
- L2 Announcements는 로컬 영역 네트워크에서 서비스를 표시하고 도달 가능하게 만드는 기능입니다. 이 기능은 주로 사무실 또는 캠퍼스 네트워크와 같이 BGP 기반 라우팅이 없는 네트워크 내에서 온프레미스 배포를 위해 고안되었습니다.
- 이 기능을 사용하면 ExternalIP 및/또는 LoadBalancer IP에 대한 ARP 쿼리에 응답합니다. 이러한 IP는 여러 노드의 가상 IP(네트워크 장치에 설치되지 않음)이므로 각 서비스에 대해 한 번에 한 노드가 ARP 쿼리에 응답하고 MAC 주소로 응답합니다. 이 노드는 서비스 로드 밸런싱 기능으로 로드 밸런싱을 수행하여 북쪽/남쪽 로드 밸런서 역할을 합니다.
- NodePort 서비스에 비해 이 기능의 장점은 각 서비스가 고유한 IP를 사용할 수 있으므로 여러 서비스가 동일한 포트 번호를 사용할 수 있다는 것입니다. NodePort를 사용할 때 트래픽을 보낼 호스트를 결정하는 것은 클라이언트에게 달려 있으며 노드가 다운되면 IP+Port 콤보를 사용할 수 없게 됩니다. L2 공지를 사용하면 서비스 VIP가 다른 노드로 간단히 마이그레이션되고 계속 작동합니다.
참고 - https://isovalent.com/blog/post/migrating-from-metallb-to-cilium/
실습
#설치
helm upgrade cilium cilium/cilium --namespace kube-system --reuse-values \
--set l2announcements.enabled=true --set externalIPs.enabled=true \
--set l2announcements.leaseDuration=3s --set l2announcements.leaseRenewDeadline=1s --set l2announcements.leaseRetryPeriod=200ms
c0 config --all |grep L2
EnableL2Announcements : true
EnableL2NeighDiscovery : true
# CiliumL2AnnouncementPolicy 생성
cat <<EOF | kubectl apply -f -
apiVersion: "cilium.io/v2alpha1"
kind: CiliumL2AnnouncementPolicy
metadata:
name: policy1
spec:
serviceSelector:
matchLabels:
color: blue
nodeSelector:
matchExpressions:
- key: node-role.kubernetes.io/control-plane
operator: DoesNotExist
interfaces:
- ^ens[0-9]+
externalIPs: true
loadBalancerIPs: true
EOF
# 확인
kubectl get ciliuml2announcementpolicy
kc describe l2announcement
#
cat <<EOF | kubectl apply -f -
apiVersion: "cilium.io/v2alpha1"
kind: CiliumLoadBalancerIPPool
metadata:
name: "cilium-pool"
spec:
allowFirstLastIPs: "No"
blocks:
- cidr: "10.10.200.0/29"
EOF
# cilium ip pool 조회
kubectl get CiliumLoadBalancerIPPool
NAME DISABLED CONFLICTING IPS AVAILABLE AGE
cilium-pool false False 3 3m5s
# 테스트용 파드, 서비스 생성
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: webpod1
labels:
app: webpod
spec:
nodeName: k8s-w1
containers:
- name: container
image: traefik/whoami
terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Pod
metadata:
name: webpod2
labels:
app: webpod
spec:
nodeName: k8s-w2
containers:
- name: container
image: traefik/whoami
terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Service
metadata:
name: svc1
spec:
ports:
- name: svc1-webport
port: 80
targetPort: 80
selector:
app: webpod
type: LoadBalancer # 서비스 타입이 LoadBalancer
---
apiVersion: v1
kind: Service
metadata:
name: svc2
spec:
ports:
- name: svc2-webport
port: 80
targetPort: 80
selector:
app: webpod
type: LoadBalancer
---
apiVersion: v1
kind: Service
metadata:
name: svc3
spec:
ports:
- name: svc3-webport
port: 80
targetPort: 80
selector:
app: webpod
type: LoadBalancer
EOF
kubectl get svc,ep
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.10.0.1 <none> 443/TCP 5h20m
service/svc1 LoadBalancer 10.10.226.228 10.10.200.1 80:32693/TCP 5m30s
service/svc2 LoadBalancer 10.10.166.59 10.10.200.2 80:30107/TCP 5m30s
service/svc3 LoadBalancer 10.10.106.144 10.10.200.3 80:31564/TCP 5m30s
NAME ENDPOINTS AGE
endpoints/kubernetes 192.168.10.10:6443 5h20m
endpoints/svc1 172.16.1.52:80,172.16.2.196:80 5m30s
endpoints/svc2 172.16.1.52:80,172.16.2.196:80 5m30s
endpoints/svc3 172.16.1.52:80,172.16.2.196:80 5m30s
#
curl -s 10.10.200.1
curl -s 10.10.200.2
curl -s 10.10.200.3
마치며
Cilium CNI.. 엄청나네요. 지금까지 배운 CNI를 총 망라한 기능과, 여러 오픈소스 기반 사이드기능을 일괄적으로 최적화하여 제공합니다.
실습을 통해 확인해보니 정말 기능이 많네요.. 정리한 기능외에도 Egress Gateway기능, Service Mesh 등을 모두 사용할 수 있으니, 이해만 제대로 하면 엄청난 성능과 간소화된 기능들을 사용할 수 있을 것 같네요. 하지만 스터디에서 언급했던대로, 기존 iptables나 네트워크 정책을 스킵하여 가시성이나 거버넌스를 만족할 수 없는 상황이 생기진 않을지 잘 검토해봐야할 것 같습니다.
이상으로 이번 스터디 포스팅을 마치도록 하겠습니다.
감사합니다 :)
'클라우드 컴퓨팅 & NoSQL > [KANS] 쿠버네티스 네트워크 심화 스터디' 카테고리의 다른 글
[9주차 - AWS EKS: VPC CNI] KANS 스터디 (24.10.27) (7) | 2024.11.03 |
---|---|
[7주차 - Service Mesh(Istio)] KANS 스터디 (24.10.13) (0) | 2024.10.17 |
[6주차 - Ingress & Gateway API] KANS 스터디 (24.10.06) (7) | 2024.10.13 |
[5주차 - Service : LoadBalancer] KANS 스터디 (24.09.29) (3) | 2024.10.05 |
[4주차 - Service : ClusterIP, NodePort] KANS 스터디 (24.09.22) (2) | 2024.09.26 |