노드 간 파드 통신 확인
파드 생성 및 확인
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
$ k get pod -o wide
$ kubectl exec -it $CILIUMPOD0 -n kube-system -c cilium-agent -- cilium status --verbose | grep Allocated -A5
$ kubectl exec -it $CILIUMPOD1 -n kube-system -c cilium-agent -- cilium status --verbose | grep Allocated -A5
$ kubectl exec -it $CILIUMPOD2 -n kube-system -c cilium-agent -- cilium status --verbose | grep Allocated -A5
$ k get ciliumendpoints
$ kubectl exec -it $CILIUMPOD0 -n kube-system -c cilium-agent -- cilium ip list
POD 변수 지정
# 테스트 파드들 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 에서 webpod로 통신 테스트
$ 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
hubble observe 명령어로도 확인할 수 있습니다.
[netpod]
[webpod1]
[webpod2]
BPF map을 통해 forward 정책도 확인할 수 있습니다.
netpod의 변수 지정
$ ip -c a
12: lxc9a60653e33ad@if11: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc noqueue state UP group default qlen 1000
link/ether 6a:98:6e:cc:39:43 brd ff:ff:ff:ff:ff:ff link-netns cni-6ba87f33-86ef-ca80-6009-039478b2907f
inet6 fe80::6898:6eff:fecc:3943/64 scope link
valid_lft forever preferred_lft forever
$ LXC=lxc9a60653e33ad
$ ip -c addr show dev $LXC
eBPF가 어떤 Data Plane Path에 부착되어 있는지 확인 할 수 있습니다.
또, LXC에 할당된 ID를 통해 어떤 eBPF map을 사용하는지 확인할 수 있습니다.
$ c0bpf net show
xdp:
tc:
ens5(2) tcx/ingress cil_from_netdev prog_id 1771 link_id 16
ens5(2) tcx/egress cil_to_netdev prog_id 1767 link_id 17
cilium_net(3) tcx/ingress cil_to_host prog_id 1758 link_id 15
cilium_host(4) tcx/ingress cil_to_host prog_id 1752 link_id 13
cilium_host(4) tcx/egress cil_from_host prog_id 1735 link_id 14
lxc_health(10) tcx/ingress cil_from_container prog_id 1744 link_id 24
lxc_health(10) tcx/egress cil_to_container prog_id 1736 link_id 25
lxc9a60653e33ad(12) tcx/ingress cil_from_container prog_id 1781 link_id 26
lxc9a60653e33ad(12) tcx/egress cil_to_container prog_id 1782 link_id 27
flow_dissector:
netfilter:
$ c0bpf prog show id 1781
1781: sched_cls name cil_from_container tag 833d15b25ed9aa8d gpl
loaded_at 2024-10-26T17:28:11+0000 uid 0
xlated 728B jited 545B memlock 4096B map_ids 235,66
btf_id 437
$ c0bpf map list
서비스 통신 확인
Socket-Based LoadBalancing
- 그림 왼쪽(네트워크 기반 로드밸런싱) vs 오른쪽(소켓 기반 로드밸런싱)
Pod1 안에서 동작하는 앱이 connect() 시스템콜을 이용하여 소켓을 연결할 때 목적지 주소가 서비스 주소(10.10.8.55)이면 소켓의 목적지 주소를 바로 백엔드 주소(10.0.0.31)로 설정한다. 이후 앱에서 해당 소켓을 통해 보내는 모든 패킷의 목적지 주소는 이미 백엔드 주소(10.0.0.31)로 설정되어 있기 때문에 중간에 DNAT 변환 및 역변환 과정이 필요없어진다.
서비스 생성
apiVersion: v1
kind: Service
metadata:
name: svc
spec:
ports:
- name: svc-webport
port: 80
targetPort: 80
selector:
app: webpod
type: ClusterIP
# 서비스 생성 확인
$ k get svc,ep svc
# 지속적으로 접속 트래픽 발생
$ SVCIP=$(kubectl get svc svc -o jsonpath='{.spec.clusterIP}')
$ while true; do kubectl exec netpod -- curl -s $SVCIP | grep Hostname;echo "-----";sleep 1;done
service의 ClusterIP는 보이지 않고, NAT된 pod의 IP만 표시되고 있는 것을 확인할 수 있습니다.
BFP 정보에 로드밸런서에 대한 정보와 서비스 리스트에 forward 정책을 확인 할 수 있습니다.
(⎈|ciliumLab:N/A) root@k8s-s:~# c0 service list | grep 10.10.53.86
12 10.10.53.86:80 ClusterIP 1 => 172.16.2.135:80 (active)
(⎈|ciliumLab:N/A) root@k8s-s:~# c0 bpf lb list | grep 10.10.53.86
10.10.53.86:80 (0) 0.0.0.0:0 (12) (0) [ClusterIP, non-routable]
10.10.53.86:80 (2) 172.16.1.63:80 (12) (2)
10.10.53.86:80 (1) 172.16.2.135:80 (12) (1)
---
$ c0 status --verbose
...
KubeProxyReplacement Details:
Status: True
Socket LB: Enabled
Socket LB Tracing: Enabled
Socket LB Coverage: Full
strace를 통해 시스템 콜을 확인해 봅니다.
(⎈|ciliumLab:N/A) root@k8s-s:~# kubectl exec netpod -- strace -c curl -s $SVCIP
Hostname: webpod2
IP: 127.0.0.1
IP: ::1
IP: 172.16.1.63
IP: fe80::50e7:79ff:fe05:6b90
RemoteAddr: 172.16.0.97:53828
GET / HTTP/1.1
Host: 10.10.53.86
User-Agent: curl/8.7.1
Accept: */*
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
27.90 0.001904 24 79 mmap
21.91 0.001495 55 27 munmap
17.57 0.001199 21 56 32 open
5.39 0.000368 13 27 close
4.81 0.000328 10 31 rt_sigaction
3.77 0.000257 18 14 mprotect
3.21 0.000219 8 27 read
3.02 0.000206 8 23 fcntl
2.24 0.000153 4 31 lseek
1.71 0.000117 9 12 rt_sigprocmask
1.57 0.000107 10 10 readv
1.52 0.000104 8 12 fstat
1.36 0.000093 93 1 1 connect
0.86 0.000059 59 1 socket
0.72 0.000049 12 4 getsockname
0.54 0.000037 7 5 getrandom
0.45 0.000031 7 4 setsockopt
0.42 0.000029 4 6 poll
0.21 0.000014 14 1 pipe
0.21 0.000014 4 3 3 ioctl
0.21 0.000014 7 2 geteuid
0.10 0.000007 7 1 getuid
0.10 0.000007 7 1 getgid
0.10 0.000007 7 1 getsockopt
0.09 0.000006 6 1 getegid
0.00 0.000000 0 1 writev
0.00 0.000000 0 1 sendto
0.00 0.000000 0 1 recvfrom
0.00 0.000000 0 1 execve
0.00 0.000000 0 3 brk
0.00 0.000000 0 1 arch_prctl
0.00 0.000000 0 1 set_tid_address
------ ----------- ----------- --------- --------- ----------------
100.00 0.006824 17 389 36 total
(⎈|ciliumLab:N/A) root@k8s-s:~#
## 시스템콜 전체 출력
$ kubectl exec netpod -- strace -s 65535 -f -tt curl -s $SVCIP
Connect가 이뤄졌을 때 Cluster-IP로 잘 들왔고, 파드 내부에서 soket hook을 통해 303 라인에 나와있는 것과 같이 목적지 pod로 바뀌어 있는 것을 확인할 수 있습니다.
Prometheus & Grafana
설치
# 배포
$ k apply -f https://raw.githubusercontent.com/cilium/cilium/1.16.3/examples/kubernetes/addons/prometheus/monitoring-example.yaml
$ k get all -n cilium-monitoring
# 파드와 서비스 확인
$ k get pod,svc,ep -o wide -n cilium-monitoring
# NodePort 설정
$ k patch svc grafana -n cilium-monitoring -p '{"spec": {"type": "NodePort"}}'
$ k 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"
웹 UI 접속 확인
Network Policy
스타워즈에서 영감 받은 예제 : 디플로이먼트(웹 서버, 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
# 데스스타 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 대신, 파드의 Lables(라벨)을 사용하여 보안 정책을 적용
- IP/Port 필터링을 L3/L4 네트워크 정책이라고 한다.
- 아래 처럼 'org=empire' Labels이 부착된 파드만 허용한다.
- Cilium performs stateful connection tracking 이므로 리턴 트래픽은 자동으로 허용
## L3/L4 정책 생성
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
## 정책 확인
k get cnp
k describe cnp rule1
c0 policy get
## 각 파드로 curl 접속
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 UI와 observe를 통해서도 Drop 되는 것을 확인할 수 있습니다.
Identity-Aware and HTTP-Aware Policy Enforment Apply and Test HTTP-aware L7 Policy
## 데스스타 SVC 접속
kubectl exec tiefighter -- curl -s -XPUT deathstar.default.svc.cluster.local/v1/exhaust-port
## Label 매칭 뿐만 아니라 L7 기반의 정책을 추가합니다.
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"
정책 적용 후 서비스의 v1/request-landing, v1/exhaust-port로 접근하면, L7 영역에서 처리되는 URL 정책에 의해 접근이 차단되는 것을 확인할 수 있습니다.
Method도 정책에 추가하였기 때문에 PUT 메소드에서는 허용된 URL이라도 차단되는 것을 확인해 볼 수 있습니다.
Bandwidth Manager
Bandwidth Manager는 eBPF 기반의 POD 별 네트워크 대역폭 제어 기능으로 bandwidth.enabled=true 설정으로 활성화 할 수 있습니다.
annotation을 통해 대역폭 제한 설정이 가능합니다.
제한 대상은 egress로 Ingress는 아직 지원하지 않습니다.
현재 상태의 tc 확인
(⎈|ciliumLab:N/A) root@k8s-s:~# 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
bandwidth 설정 활성화
$ helm upgrade cilium cilium/cilium --namespace kube-system --reuse-values --set bandwidthManager.enabled=true
# 인터페이스 tc 확인: 설정 전후 옵션 값들이 상당히 추가된다
$ tc qdisc show dev ens5
테스트를 위한 트래픽 발생 서버/클라이언트 파드 생성
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
생성된 POD 정보 확인, annotations의 bandwidth 설정 10M도 확인할 수 있습니다.
BPF에도 설정 값이 추가된 것을 확인할 수 있습니다.
## 생성한 Pod에서 트래픽 발생
$ k exec netperf-client -- netperf -t TCP_MAERTS -H "${NETPERF_SERVER_IP}"
MIGRATED TCP MAERTS TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 172.16.2.133 (172.16.) port 0 AF_INET
Recv Send Send
Socket Socket Message Elapsed
Size Size Size Time Throughput
bytes bytes bytes secs. 10^6bits/sec
131072 16384 16384 10.02 9.88 << 10Mbps 제한을 확인
Bandwidth를 5로 변경해 제한 값 적용을 확인해 봅니다.
## sed로 변경 후 재배포
$ k get pod netperf-server -o json | sed -e 's|10M|5M|g' | kubectl apply -f -
## 트래픽 발생
$ k exec netperf-client -- netperf -t TCP_MAERTS -H "${NETPERF_SERVER_IP}"
MIGRATED TCP MAERTS TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 172.16.2.133 (172.16.) port 0 AF_INET
Recv Send Send
Socket Socket Message Elapsed
Size Size Size Time Throughput
bytes bytes bytes secs. 10^6bits/sec
131072 16384 16384 10.07 4.91
'Cloud > Kubernetes' 카테고리의 다른 글
[KANS] AWS EKS : VPC CNI (4) | 2024.11.03 |
---|---|
[KANS] Cilium & Hubble (0) | 2024.10.26 |
[KANS] Istio - Traffic Management (0) | 2024.10.20 |
[KANS] Istio example (bookinfo) (1) | 2024.10.20 |
[KANS] istio install + expose (0) | 2024.10.20 |