이번 주는 Cilium CNI에 대해서 공부한 내용을 정리해보고자 합니다.
실습 환경 구성
배포
실습환경 구성은 이전에 한 방법과 같이 cloudformation template를 배포합니다.
#cloudformation template url
https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/kans/kans-8w.yaml
clouformation template은 3분이내에 배포가 완료되지만, 생성된 ec2의 userdata가 모두 구성되는데 시간이 소요될 수 있습니다.
배포 후 5~7분 정도 지난 후 접속해보시는 걸 권장드립니다.
배포확인
배포가 완료되었는지 확인을 위해 ssh 접속한 뒤, 환경을 확인해보겠습니다.
클러스터 기본 정보 확인
kubectl cluster-info
# 노드 상태 확인
kc get node -owide
# 모든 파드 정보를 확인합니다.
kc get pod -A
hostnamectl
# cilium이 제대로 동작하기 위해서는 커널 버전을 5.8 이상으로 설정해야합니다.
uname -a
간단한 명령어로 클러스터 상태를 확인할 수 있습니다.
Cilium 소개
- cilium은 eBPF 기반으로 Pod Network 환경 + 보안을 제공하는 CNI plugin 입니다.
- 추가적인 App이나 설정 변경 없이 리눅스 커널을 자유롭게 프로그래밍하여 동작합니다.
- 모든 패킷을 가로채기 위해 수신 NIC의 Ingress TC Hook을 사용합니다.
- 네트워크 모드는 2가지 지원합니다. 터널모드 (VXLAN, GENEVE), 네이티브 라우팅 모드
- kube-proxy 가 없습니다.
BPF, eBPF
전통적인 리눅스 네트워크 스택에서는 netfilter / iptables를 기반으로 동작합니다. iptables를 활용한 방식은 쿠버네티스 클러스터가 동작하는 동안 패킷을 처리하는 데 레이어를 건너뛰기 어렵습니다. (규칙이 일치될 때까지 모든 규칙을 평가합니다.) 이를 해결하기 위해 BPF, eBPF가 등장하였습니다.
eBPF의 동작은 아래 그림과 같습니다.
기존의 iptables에서 패킷필터를 처리하는 게 아닌 eBPF는 커널 샌드박스에서 구성된 dynamic programming에 의해 필터링 되어 더 나은 성능을 제공할 수 있습니다.
eBPF에서 제공하는 기능은 대표적으로 4가지 입니다.
성능 비교
영역에 따른 패킷 차단 성능 (10GB)
네트워크 구조
성능 표
Userspace: Device Driver - Ingress - Proto Handler - Routing - Input - Upper Layer (L4 ~)
Netfilter : Device Driver - Ingress - Proto Handler - Routing - Input (L4)
TC : Device Driver - Ingress (L3)
XDP: Device Driver (L2)
Architecture
cilium architecture에 대한 자세한 설명은 https://www.youtube.com/watch?v=LCQ89uBB7zE 링크에 자세한 설명이 포함되어 있습니다.
그밖에 cilium
- Cilium Agent : 데몬셋으로 실행, K8S API 설정으로 부터 '네트워크 설정, 네트워크 정책, 서비스 부하분산, 모니터링' 등을 수행하며, eBPF 프로그램을 관리한다.
- Cilium Operator : K8S 클러스터에 대한 한 번씩 처리해야 하는 작업을 관리.
- Hubble : 네트워크와 보안 모니터링 플랫폼 역할을 하여, 'Server, Relay, Client, Graphical UI' 로 구성되어 있다.
- Data Store : Cilium Agent 간의 상태를 저장하고 전파하는 데이터 저장소, 2가지 종류 중 선택(K8S CRDs, Key-Value Store)
또한 cilium 커맨드라인 도구로 cilium-cli가 있습니다.
배포
# cilium helm repo 추가
helm repo add cilium https://helm.cilium.io/
helm repo update
# cilium helm chart 설치
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 적용이 가능
실제 환경에 적용하면 다음 영상과 같이 cilium을 helm chart로 설치하고 파드가 생성되는 것을 확인할 수 있습니다.
cilium을 설치하게 되면 이전에 pending 상태였던 파드들이 배포되는 것을 알 수 있습니다. 이는 cilium이 kube-proxy를 사용하지 않으므로 처음에 배포했던 cloudformation template에서는 kube-proxy를 제외하고 설치하였기 때문에 kube-proxy가 없어 pod가 배포되지 않았었고, kube-proxy를 대체할 수 있는 cilium CNI가 구성되어 생성 대기중이던 pod가 배포되기 시작하였기 때문입니다.
cilium 설치시 생성되는 파드중 cilium envoy는 최소한의 Envoy 확장 및 사용자 지정 정책 적용 필터로 구축됩니다. Cilium은 이 최소한의 배포를 클러스터의 네트워크 정책에 지정된 대로 HTTP 및 기타 L7 정책을 적용하기 위한 호스트 프록시로 사용합니다. 실리움 프록시는 실리움 이미지 내에 배포됩니다.
다른 cni와 다르게 cilium을 구성하면 iptables가 한 눈에 보일정도로 짧아지는 것을 확인할 수 있습니다.
CLI 설치
# 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 cli를 설치할 수 있습니다. 이때 architecture를 잘 설정하여 설치하시기 바랍니다.
cilium이 어떻게 설치되어있는지 한 눈에 볼 수 있으니 파악이 쉽습니다.
설치 정보 확인
# 확인
cilium status --wait
cilium config view
명령어 한 줄로 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
데몬셋 파드 내에서 cilium cli 명령어로 설정을 확인할 수 있습니다.
kubeProxyReplacement 즉 kube-proxy 없이 동작을 대처한다는 옵션이 잘 설정되어 있음을 확인할 수 있습니다.
아래에는 관련 설정 값들을 확인할 수 있습니다.
IPAM이 설정되어 각 노드별로 등록되어있음을 확인할 수 있습니다.
BPF 맵의 사이즈를 확인할 수 있습니다.
agent 기반의 ip masquerading을 켜기 위해 아래 명령어를 실행해줍니다.
helm upgrade cilium cilium/cilium --namespace kube-system --reuse-values --set ipMasqAgent.enabled=true
이렇게 cilium 설정이 변경되면 자동으로 cilium 파드가 재생성되는 것을 확인할 수 있습니다.
export CILIUMPOD0=$(kubectl get -l k8s-app=cilium pods -n kube-system --field-selector spec.nodeName=k8s-s -o jsonpath='{.items[0].metadata.name}')
alias c0="kubectl exec -it $CILIUMPOD0 -n kube-system -c cilium-agent -- cilium"
c0 status --verbose | grep Masquerading
설정을 확인하기 위해 다시 명령어를 입력해보면 다음과 같은 내용을 확인할 수 있습니다.
변수 및 약어 설정
# 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의 cheat sheet도 공유하겠습니다. 원본 파일은 https://isovalent.com/blog/post/cilium-cheat-sheet/에서 다운로드 받을 수 있습니다.
앞서 설정한 단축어를 활용하면 다음과 같은 방식으로 활용할 수 있습니다.
# 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
cilium 데몬셋 파드의 cilium 상태 확인
ip 목록 조회
c0에 해당하는 파드에 설정된 서비스 목록 등을 확인할 수 있습니다.
Hubble UI
통신 및 서비스와 네트워킹 인프라의 동작에 대한 심층적인 가시성을 완전히 투명한 방식으로 제공하는 관찰성을 제공하는 도구
애플리케이션 코드 수정 등 추가 설정 없이 동작합니다.
서비스, 파드, id 기반으로 모니터링/제어를 제공합니다.
Hubble relay를 활용하여 전체 클러스터, 클러스터 메시 환경에서 클러스터에 대한 가시성을 제공합니다.
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"
Cilium 구성요소중 Hubble의 UI 접근을 위해 service type을 nodeport로 변경하여 접속합니다.
url로 접속하면 다음과 같은 페이지에 접근가능합니다.
네임스페이스를 선택하여 hubble UI에 접근 가능합니다. 원하는대로 트래픽을 필터링할 수 있습니다.
네트워크
패킷 흐름
Endpoint to Endpoint
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
위 코드블럭을 사용하여 파드를 생성합니다.
3개의 파드가 배포됩니다.
c0 status --verbose | grep Allocated -A5
c1 status --verbose | grep Allocated -A5
c2 status --verbose | grep Allocated -A5
앞에서 설정한 단축어(alias)로 cilium 의 상태를 확인해보겠습니다.
# cilium endpoint 조회
kubectl get ciliumendpoints
kubectl get ciliumendpoints -A
# c0 cilium pod의 endpoint 목록 조회
c0 endpoint list
# c0 cilium pod의 bpf endpoint 목록 조회
c0 bpf endpoint list
# c0 cilium pod bpf map 중 cilium_lxc 조회
c0 map get cilium_lxc
# c0 ip 조회
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 -- "
이후, 파드에 통신을 보내보겠습니다.
# 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
파드 0 에서 파드 1로 요청하는 경우, 파드 0 에서 파드 2로 요청하는 경우,
파드 0에서 파드 1로 8080포트로 요청하는 경우, 파드 0에서 파드 2로 8080포트로 요청하는 경우
파드 0에서 8.8.8.8 (google dns)로 요청하는 경우, 파드0에서 wttr.in/seoul (날씨 사이트로 요청하는 경우)
를 한번에 보내봤습니다. 통신이 잘 되는 것을 확인할 수 있습니다.
Hubble에서 마찬가지로 각 파드간 통신 기록을 즉각적으로 확인할 수 있습니다. 상세정보를 확인하고자 하는 내역을 클릭하면 자세한 정보가 추가로 나타납니다.
서비스 통신
(좌) 네트워크 기반 로드밸런싱 vs (우) 소켓 기반 로드밸런싱
동작하는 앱이 connect() 시스템콜을 이용하여 소켓을 연결할 때 목적지 주소가 서비스 주소(10.10.8.55)이면 소켓의 목적지 주소를 바로 백엔드 주소(10.0.0.31)로 설정합니다.
이후 앱에서 해당 소켓을 통해 보내는 모든 패킷의 목적지 주소는 이미 백엔드 주소(10.0.0.31)로 설정되어 있기 때문에 중간에 DNAT 변환 및 역변환 과정이 필요없어집니다. (network hop이 줄어듬)
socket send/recv: socket send/recv hook은 TCP 소켓의 모든 송/수신 작업에서 실행되고 hook에서 검사/삭제/리다이렉션 하게 되빈다. 즉, Pod 내에서 Syscall에 Hook을 걸어 요청을 변경해버립니다. (아주 빠름)
실습
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
코드 블록를 실행해줍니다.
# 서비스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
netpod 파드에서 (pod1) 서비스의 IP에 요청을 보내보겠습니다.
실제로 내부에서 어떻게 동작하는지 자세히 살펴봅시다.
kubectl exec netpod -- strace -s 65535 -f -tt curl -s $SVCIP
strace 명령어로 curl 명령이 어떻게 동작했는지 살펴보겠습니다.
302번 라인에서 connect call이 발생합니다. 대상은 10.10.201.234 이 ip는 서비스의 ip입니다. 이때는 서비스의 ip로 요청을 하는 걸 알 수 있습니다.
하지만 바로 다음 라인인 getsockname 에서는 172.16.0.182 아이피로 변경되었습니다. 바로 도착지 pod의 ip인 것을 확인할 수 있습니다.
앞선 그림의 이부분에 해당하는 과정입니다. 이를 통해 service ip (clusterIP)로 요청을 했으나 실제로는 도착지 파드 ip로 바로 연결되어 있음을 알 수 있습니다.
코멘트: network stack을 말도 안되게 간소화 시켜버리는 것 같습니다.
리소스 삭제
kubectl delete pod --all && kubectl delete svc svc
모니터링 (Prometheus & 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
# 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 에서 제공하는 monitoring example 헬름차트에는 cilium dashboard가 기본구성으로 포함되어 있습니다.
cilium metric 을 확인할 수 있는 대시보드 cilium cni의 모니터링을 하기위한 목적으로 사용할 수 있습니다.
Hubble UI에서 확인할 수 있는 지표를 Grafana에서도 Prometheus를 통해 함께 확인할 수 있습니다.
Network Policy (L3, L4, L7)
ID 기반 (L3)
- 파드의 label을 활용하여 엔드포인트간의 연결을 제어합니다.
- label role=frontend가 있는 모든 엔드포인트는 label role=backend가 있는 모든 엔드포인트에 연결할 수 있습니다.
Port 기반 (L4)
- 들어오는 요청/나가는 요청에 대해서 허용할 포트를 제한할 수 있습니다.
- 예를 들어, 위 그림에서 label이 role=frontend인 엔드포인트는 포트 443(https)에서만 나가는 연결을 만들 수 있습니다.
- 엔드포인트 role=backend는 포트 443(https)에서만 연결을 허용할 수 있습니다.
애플리케이션 기반 (L7)
- 암호화된 HTTP 및 원격 프로시저 호출(RPC) 프로토콜을 위해 애플리케이션 수준에서 세분화된 액세스 제어를 제공합니다.
- label role=frontend이 있는 엔드포인트는 REST API 호출 GET/userdata/[0-9]+만 수행할 수 있습니다.
- role=backend가 있는 다른 모든 API 상호 작용은 제한됩니다.
실습
스타워즈 데모
# 리소스 배포
kubectl create -f https://raw.githubusercontent.com/cilium/cilium/1.16.3/examples/minikube/http-sw-app.yaml
배포 후 파드의 label을 확인합니다.
리소스가 모두 배포된 뒤 실습 전 endpoint 정보를 확인하겠습니다.
# xwing 파드에서 deathstar service로 요청
kubectl exec xwing -- curl -s -XPOST deathstar.default.svc.cluster.local/v1/request-landing
# tiefighter 파드에서 deathstar service로 요청
kubectl exec tiefighter -- curl -s -XPOST deathstar.default.svc.cluster.local/v1/request-landing
이후 xwing 파드에 접속하여, 데스스타 service로 요청을 보내보겠습니다.
곧바로 tiefighter에서도 deathstar 서비스로 요청을 보내보겠습니다.
이제, hubble ui를 접속하면 xwing, tiefighter 가 데스스타에 잘 착륙(landing)한 것을 확인할 수 있습니다.
이제 xwing이 착륙하지 못하도록 해보겠습니다.
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
데스스타에 착륙(요청) 가능한 파드는 org=empire 라벨이 할당된 tiefighter 가 될겁니다.
다시 curl을 사용하여 데스스타에 착륙 요청을 시도합니다.
예상과 동일하게 tiefighter는 landing(request) 가능한 반면, xwing은 요청이 차단됩니다. 이 또한 hubble ui에서 확인 가능합니다.
L2 Announcements / L2 Aware LB (beta)
소개
- L2 Announcements는 로컬 영역 네트워크에서 서비스를 표시하고 도달 가능하게 만드는 기능입니다. 이 기능은 주로 사무실 또는 캠퍼스 네트워크와 같이 BGP 기반 라우팅이 없는 네트워크 내에서 온프레미스 배포를 위해 고안되었습니다.
- 이 기능을 사용하면 ExternalIP 및/또는 LoadBalancer IP에 대한 ARP 쿼리에 응답합니다. 이러한 IP는 여러 노드의 가상 IP(네트워크 장치에 설치되지 않음)이므로 각 서비스에 대해 한 번에 한 노드가 ARP 쿼리에 응답하고 MAC 주소로 응답합니다. 이 노드는 서비스 로드 밸런싱 기능으로 로드 밸런싱을 수행하여 북쪽/남쪽 로드 밸런서 역할을 합니다.
- NodePort 서비스에 비해 이 기능의 장점은 각 서비스가 고유한 IP를 사용할 수 있으므로 여러 서비스가 동일한 포트 번호를 사용할 수 있다는 것입니다. NodePort를 사용할 때 트래픽을 보낼 호스트를 결정하는 것은 클라이언트에게 달려 있으며 노드가 다운되면 IP+Port 콤보를 사용할 수 없게 됩니다. L2 공지를 사용하면 서비스 VIP가 다른 노드로 간단히 마이그레이션되고 계속 작동합니다.
실습
helm upgrade cilium cilium/cilium --namespace kube-system --reuse-values \
--set l2announcements.enabled=true --set externalIPs.enabled=true \
--set l2announcements.leaseDuration
# 설정 확인
c0 config --all |grep L2
# 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
# CiliumL2AnnouncementPolicy 생성 확인
kubectl get ciliuml2announcementpolicy
kc describe l2announcement
# CiliumLoadBalancerIPPool 생성
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
예제 코드를 실행하여 간단히 구성한 뒤, 설정을 확인할 수 있습니다.
테스트 파드 생성
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
이렇게 클라우드 환경이 아닌 온프레미스 환경에서 loadbalancer 유형의 서비스를 활용할 수 있습니다.
XDP
Cilium에서 XDP를 활용하기 위해서는 NIC (Network Interface Controller) 혹은 ENI (Elastic Network Interface, AWS)에서 XDP처리를 지원해야 합니다.
aws ec2 에서 xdp 지원여부 확인하기
터미널 내에서 xdp를 지원하는 nic인지 확인하기 위해서는 ethtool을 사용하여 확인할 수 있습니다.
먼저 ifconfig 명령어로 네트워크 인터페이스를 확인해야합니다.
이후 ethtool 명령어로 네트워크 인터페이스 정보를 조회합니다.
AWS에서 향상된 네트워킹 기능 활성화하는 가이드 문서
ENA 활성화 여부 확인하기
# ubuntu
apt upgrade
apt install -y -q awscli
# ena 지원하는 인스턴스 여부 확인
AMI_ID=$(curl 169.254.169.254/latest/meta-data/ami-id)
aws ec2 describe-images --image-id $AMI_ID --query "Images[].EnaSupport"
# nic driver 확인
ethtool -i ens5
AWS EC2 인스턴스에 장착된 네트워크 인터페이스는 3종류입니다. (ENI, ENA, EFA) 기본적으로는 ENI가 장착되어있고 성능은 ENI < ENA < EFA 순으로 좋습니다.
aws에서 xdp를 지원하기 시작한 시점은 2020년 1월로 보입니다. ena driver 2.2.0 부터 지원하는 것으로 확인됩니다.
https://github.com/amzn/amzn-drivers/issues/8
최신 인스턴스에는 당연히 이전 드라이버가 있진 않을거라 문제되진 않을 것 같습니다.
XDP 사용을 위해 ENA이상의 네트워크 어댑터를 지원하는 인스턴스를 활용하면 될 것으로 보입니다.
AWS Nitro network system에 구축된 인스턴스는 모두 ENA를 지원한다고 합니다.
Nitro System에 포함된 인스턴스 목록
Nitro System의 이전 세대 지원
https://aws.amazon.com/ko/ec2/faqs/#Nitro_System_Support_for_Previous_Generation
회고
스터디가 마친 뒤에는 온프레미스 클러스터를 Cilium 으로 구성하여 다양한 작업을 진행해보고 싶어졌습니다.
혼자 공부하기에 어려웠던 네트워킹 지식을 가시다님 스터디를 통해서 시간 절약을 많이 했습니다. 모든 내용을 이해하진 못했지만 필요한 내용을 찾아서 공부할 정도는 되었다고 생각하여 알차게 복습 또 복습하고자 합니다.
'스터디' 카테고리의 다른 글
[KANS 3기] 9주차 VPC CNI (0) | 2024.10.31 |
---|---|
[KANS 3기] 6주차 GatewayAPI (7) | 2024.10.12 |
[KANS 3기] 6주차 Ingress (4) | 2024.10.12 |
[KANS 3기] 5주차 MetalLB (6) | 2024.10.05 |
KCD 참석후기 - 쿠버네티스에서 스케줄링 작동 방식 (0) | 2024.09.28 |