Flannel CNI
1. K8S 네트워킹
- 파드들은 각자 고유한 IP 주소를 가지고 있으며, 네트워크 상에서 라우팅을 통해 직접 접근할 수 있습니다. 파드가 실행 중인 노드에 상관없이, 다른 노드에 있는 파드와 IP 기반으로 통신이 가능합니다.
- 각 노드는 해당 노드에서 실행 중인 파드에 직접 접근할 수 있습니다.
- 노드 내에서 파드와 통신할 때는 특별한 NAT(Network Address Translation) 없이 통신이 이루어집니다.
- 클러스터 외부에서 파드로 트래픽을 라우팅할 수 있는 메커니즘이 필요합니다. 이를 위해 서비스와 로드 밸런서와 같은 쿠버네티스 리소스가 사용됩니다.
- K8S 네트워킹의 주요 컴포넌트
- CNI (Container Network Interface)
- 쿠버네티스 클러스터 내에서 네트워크 인터페이스를 설정하고 관리하기 위해 다양한 CNI 플러그인이 사용됩니다.
- CNI 플러그인은 파드가 생성되거나 삭제될 때 네트워크를 설정하고 해제하는 역할을 합니다. 대표적인 CNI 플러그인으로는 Flannel, Calico, Weave 등이 있습니다.
- 서비스 (Service)
- 쿠버네티스의 서비스는 파드의 네트워크를 외부로 노출시키는 역할을 합니다.
- 서비스는 로드 밸런싱을 통해 여러 파드에 트래픽을 분산시키며, 파드의 IP가 동적으로 변경되더라도 안정적인 네트워크 접근을 보장합니다.
- 로드 밸런서 (Load Balancer)
- 클러스터 외부에서 트래픽을 클러스터 내부로 라우팅할 수 있도록 지원합니다.
- 주로 클라우드 환경에서 외부 트래픽을 여러 파드로 분산시키는 데 사용됩니다.
- CNI (Container Network Interface)
2. Flannel 이란?
- Flannel은 쿠버네티스 클러스터에서 파드 간 네트워크 통신을 지원하는 CNI 플러그인으로, 파드 간 통신을 쉽게 구현할 수 있는 네트워크 오버레이를 제공합니다.
- Flannel은 쿠버네티스 클러스터 내에서 파드가 서로 다른 노드에 있더라도 원활하게 통신할 수 있도록 오버레이 네트워크를 생성합니다.
- 이 네트워크는 VXLAN을 사용해 각 노드에서 생성된 파드가 마치 동일한 네트워크에 있는 것처럼 통신할 수 있도록 지원합니다.
- Flannel은 각 노드에 고유한 서브넷을 할당하고, 파드가 해당 노드에 생성될 때 IP 주소를 할당합니다. 이렇게 할당된 IP 주소는 클러스터 내에서 다른 노드의 파드들과 통신할 수 있게 됩니다.
3. kind & Flannel 배포
- kind(Kubernetes IN Docker)와 Flannel CNI(Container Network Interface)를 사용하여 파드 간의 네트워크 통신을 확인해봅니다.
1. kind & Flannel 배포
cat <<EOF> kind-cni.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
labels:
mynode: control-plane
extraPortMappings:
- containerPort: 30000
hostPort: 30000
- containerPort: 30001
hostPort: 30001
- containerPort: 30002
hostPort: 30002
kubeadmConfigPatches:
- |
kind: ClusterConfiguration
controllerManager:
extraArgs:
bind-address: 0.0.0.0
etcd:
local:
extraArgs:
listen-metrics-urls: http://0.0.0.0:2381
scheduler:
extraArgs:
bind-address: 0.0.0.0
- |
kind: KubeProxyConfiguration
metricsBindAddress: 0.0.0.0
- role: worker
labels:
mynode: worker
- role: worker
labels:
mynode: worker2
networking:
disableDefaultCNI: true
EOF
kind create cluster --config kind-cni.yaml --name myk8s --image kindest/node:v1.30.4
# 배포 확인
kind get clusters
kind get nodes --name myk8s
kubectl cluster-info
- role: control-plane: 이 노드는 클러스터의 마스터 노드 역할을 하며, 여러 포트 매핑 설정이 포함되어 있습니다.
- role: worker: 두 개의 worker 노드를 정의하며, 파드를 실행하는 노드로 사용할 예정입니다.
- disableDefaultCNI: true: 기본적으로 제공되는 CNI 플러그인을 비활성화하여, 나중에 Flannel을 사용할 수 있도록 설정합니다.
2. 네트워크 확인 및 노드 확인
# 네트워크 확인
kubectl cluster-info dump | grep -m 2 -E "cluster-cidr|service-cluster-ip-range"
# 노드 확인 : CRI
kubectl get nodes -o wide
# 노드 라벨 확인
kubectl get nodes myk8s-control-plane -o jsonpath={.metadata.labels} | jq
...
"mynode": "control-plane",
...
kubectl get nodes myk8s-worker -o jsonpath={.metadata.labels} | jq
kubectl get nodes myk8s-worker2 -o jsonpath={.metadata.labels} | jq
3. 컨테이너 상태 확인
# 컨테이너 내부 정보 확인
docker exec -it myk8s-control-plane ip -br -c -4 addr
docker exec -it myk8s-worker ip -br -c -4 addr
docker exec -it myk8s-worker2 ip -br -c -4 addr
#
docker exec -it myk8s-control-plane sh -c 'apt update && apt install tree jq psmisc lsof wget bridge-utils tcpdump iputils-ping htop git nano -y'
docker exec -it myk8s-worker sh -c 'apt update && apt install tree jq psmisc lsof wget bridge-utils tcpdump iputils-ping -y'
docker exec -it myk8s-worker2 sh -c 'apt update && apt install tree jq psmisc lsof wget bridge-utils tcpdump iputils-ping -y'
)
)
4. bridge 실행파일 생성 후 로컬에 복사
- Kind(Kubernetes IN Docker)를 사용하여 쿠버네티스 클러스터를 배포할 때, 네트워크 CNI 플러그인 중 bridge 플러그인이 없어서 네트워크 설정에 실패하는 문제가 발생합니다. 이를 해결하기 위해 미리 bridge 플러그인을 수동으로 복사하는 작업을 진행합니다.
#
docker exec -it myk8s-control-plane bash
---------------------------------------
apt install golang -y
git clone https://github.com/containernetworking/plugins
cd plugins
chmod +x build_linux.sh
#
./build_linux.sh
Building plugins
bandwidth
firewall
portmap
sbr
tuning
vrf
bridge
host-device
ipvlan
loopback
macvlan
ptp
vlan
dhcp
host-local
static
# 파일 권한 확인 755
ls -l bin
-rwxr-xr-x 1 root root 4559683 Sep 3 04:54 bridge
...
exit
---------------------------------------
# 자신의 PC에 복사 : -a 권한 보존하여 복사(755)
docker cp -a myk8s-control-plane:/plugins/bin/bridge .
ls -l bridge
5. Flannel CNI 설치
# Flannel cni 설치
kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml
# namespace 에 pod-security.kubernetes.io/enforce=privileged Label 확인
kubectl get ns --show-labels
NAME STATUS AGE LABELS
kube-flannel Active 2m49s k8s-app=flannel,kubernetes.io/metadata.name=kube-flannel,pod-security.kubernetes.io/enforce=privileged
kubectl get ds,pod,cm -n kube-flannel
kubectl describe cm -n kube-flannel kube-flannel-cfg
6. 플러그인 (bridge) 수동 복사 작업 진행
- CNI 플러그인 경로에 필요한 네트워크 플러그인(bridge)이 존재하지 않아서 발생하는 오류를 해결하기 위해 다음 명령어를 진행합니다.kubectl get pod -A -owide
# failed to find plugin "bridge" in path [/opt/cni/bin]
kubectl get pod -A -owide
kubectl describe pod -n kube-system -l k8s-app=kube-dns
Warning FailedCreatePodSandBox 35s kubelet Failed to create pod sandbox: rpc error: code = Unknown desc = failed to setup network for sandbox "786e9caec9c312a0b8af70e14865535575601d024ec02dbb581a1f5ac0b8bb06": plugin type="flannel" failed (add): loadFlannelSubnetEnv failed: open /run/flannel/subnet.env: no such file or directory
Warning FailedCreatePodSandBox 23s kubelet Failed to create pod sandbox: rpc error: code = Unknown desc = failed to setup network for sandbox "6ccd4607d32cbb95be9ff40b97a436a07e5902e6c24d1e12aa68fefc2f8b548a": plugin type="flannel" failed (add): failed to delegate add: failed to find plugin "bridge" in path [/opt/cni/bin]
docker cp bridge myk8s-control-plane:/opt/cni/bin/bridge
docker cp bridge myk8s-worker:/opt/cni/bin/bridge
docker cp bridge myk8s-worker2:/opt/cni/bin/bridge
docker exec -it myk8s-control-plane chmod 755 /opt/cni/bin/bridge
docker exec -it myk8s-worker chmod 755 /opt/cni/bin/bridge
docker exec -it myk8s-worker2 chmod 755 /opt/cni/bin/bridge
docker exec -it myk8s-control-plane ls -l /opt/cni/bin/
docker exec -it myk8s-worker ls -l /opt/cni/bin/
docker exec -it myk8s-worker2 ls -l /opt/cni/bin/
for i in myk8s-control-plane myk8s-worker myk8s-worker2; do echo ">> node $i <<"; docker exec -it $i ls /opt/cni/bin/; echo; done
bridge flannel host-local loopback portmap ptp
#
kubectl get pod -A -owide
)
)
)
- Bridge 플러그인을 수동 업로드하면 정상적으로 모든 pod가 실행되는 것을 확인 가능합니다.
7. Flannel DaemonSet 및 구성 정보 확인
#
kubectl get ds,pod,cm -n kube-flannel -owide
kubectl describe cm -n kube-flannel kube-flannel-cfg
# iptables 정보 확인
for i in filter nat mangle raw ; do echo ">> IPTables Type : $i <<"; docker exec -it myk8s-control-plane iptables -t $i -S ; echo; done
for i in filter nat mangle raw ; do echo ">> IPTables Type : $i <<"; docker exec -it myk8s-worker iptables -t $i -S ; echo; done
for i in filter nat mangle raw ; do echo ">> IPTables Type : $i <<"; docker exec -it myk8s-worker2 iptables -t $i -S ; echo; done
- Flannel은 iptables를 사용하여 파드 간 트래픽을 라우팅하거나 외부 네트워크로의 트래픽을 처리합니다. 각 노드에서 iptables의 설정을 확인하여 트래픽 흐름을 분석할 수 있습니다.
8. Flannel 네트워크 정보 확인(대역, MTU)
for i in myk8s-control-plane myk8s-worker myk8s-worker2; do echo ">> node $i <<"; docker exec -it $i cat /run/flannel/subnet.env ; echo; done
- Flannel은 각 노드에 고유한 서브넷을 할당하며, 노드 간 VXLAN 오버레이 네트워크를 통해 통신을 처리합니다. 각 노드에서 Flannel의 네트워크 정보를 확인합니다.
- FLANNEL_NETWORK: 전체 네트워크 범위.
- FLANNEL_SUBNET: 해당 노드에 할당된 서브넷으로, 파드에 할당되는 IP는 이 범위 내에서 결정됩니다.
- FLANNEL_MTU: MTU(Maximum Transmission Unit)는 패킷의 최대 크기입니다. VXLAN 오버레이 네트워크를 사용할 때 MTU 설정은 성능에 영향을 미칠 수 있습니다.
- FLANNEL_IPMASQ: 파드가 외부 인터넷으로 나가는 경우, 마스커레이딩 설정을 통해 노드의 IP로 트래픽이 라우팅됩니다.
9. Flannel 네트워크 정보 확인
# 노드마다 할당된 dedicated subnet (podCIDR) 확인
kubectl get nodes -o jsonpath='{.items[*].spec.podCIDR}' ;echo
# 노드 정보 중 flannel 관련 정보 확인 : VXLAN 모드 정보와, VTEP 정보(노드 IP, VtepMac) 를 확인
kubectl describe node | grep -A3 Annotations
# 각 노드(?) 마다 bash 진입 후 아래 기본 정보 확인 : 먼저 worker 부터 bash 진입 후 확인하자
docker exec -it myk8s-worker bash
docker exec -it myk8s-worker2 bash
docker exec -it myk8s-control-plane bash
----------------------------------------
# 호스트 네트워크 NS와 flannel, kube-proxy 컨테이너의 네트워크 NS 비교 : 파드의 IP와 호스트(서버)의 IP를 비교해보자!
lsns -p 1
lsns -p $(pgrep flanneld)
lsns -p $(pgrep kube-proxy)
# 기본 네트워크 정보 확인
ip -c -br addr
ip -c link | grep -E 'flannel|cni|veth' -A1
ip -c addr
ip -c -d addr show cni0 # 네트워크 네임스페이스 격리 파드가 1개 이상 배치 시 확인됨
ip -c -d addr show flannel.1
5: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN group default
link/ether 1e:81:fc:d5:8a:42 brd ff:ff:ff:ff:ff:ff promiscuity 0 minmtu 68 maxmtu 65535
vxlan id 1 local 192.168.100.10 dev enp0s8 srcport 0 0 dstport 8472 nolearning ttl auto ageing 300 udpcsum noudp6zerocsumtx noudp6zerocsumrx numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
inet 172.16.0.0/32 brd 172.16.0.0 scope global flannel.1
brctl show
bridge name bridge id STP enabled interfaces
cni0 8000.663bf746b6a8 no vethbce1591c
vethc17ba51b
vethe6540260
# 라우팅 정보 확인 : 다른 노드의 파드 대역(podCIDR)의 라우팅 정보가 업데이트되어 있음을 확인
ip -c route
default via 172.18.0.1 dev eth0
10.244.0.0/24 via 10.244.0.0 dev flannel.1 onlink
10.244.1.0/24 dev cni0 proto kernel scope link src 10.244.1.1
10.244.2.0/24 via 10.244.2.0 dev flannel.1 onlink
172.18.0.0/16 dev eth0 proto kernel scope link src 172.18.0.3
# flannel.1 인터페이스를 통한 ARP 테이블 정보 확인 : 다른 노드의 flannel.1 IP와 MAC 정보를 확인
ip -c neigh show dev flannel.1
# 브리지 fdb 정보에서 해당 MAC 주소와 통신 시 각 노드의 enp0s8
bridge fdb show dev flannel.1
# 다른 노드의 flannel.1 인터페이스로 ping 통신 : VXLAN 오버레이를 통해서 통신
ping -c 1 10.244.0.0
ping -c 1 10.244.1.0
ping -c 1 10.244.2.0
# iptables 필터 테이블 정보 확인 : 파드의 10.244.0.0/16 대역 끼리는 모든 노드에서 전달이 가능
iptables -t filter -S | grep 10.244.0.0
# iptables NAT 테이블 정보 확인 : 10.244.0.0/16 대역 끼리 통신은 마스커레이딩 없이 통신을 하며,
# 10.244.0.0/16 대역에서 동일 대역(10.244.0.0/16)과 멀티캐스트 대역(224.0.0.0/4) 를 제외한 나머지 (외부) 통신 시에는 마스커레이딩을 수행
iptables -t nat -S | grep 'flanneld masq' | grep -v '! -s'
----------------------------------------
)
파드 네트워크 통신 및 패킷 캡처 실습
1. 파드 2개 생성
# [터미널1,2] 워커 노드1,2 - 모니터링
docker exec -it myk8s-worker bash
docker exec -it myk8s-worker2 bash
-----------------------------
watch -d "ip link | egrep 'cni|veth' ;echo; brctl show cni0"
-----------------------------
# [터미널3] cat & here document 명령 조합으로 즉석(?) 리소스 생성
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
name: pod-1
labels:
app: pod
spec:
nodeSelector:
kubernetes.io/hostname: myk8s-worker
containers:
- name: netshoot-pod
image: nicolaka/netshoot
command: ["tail"]
args: ["-f", "/dev/null"]
terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Pod
metadata:
name: pod-2
labels:
app: pod
spec:
nodeSelector:
kubernetes.io/hostname: myk8s-worker2
containers:
- name: netshoot-pod
image: nicolaka/netshoot
command: ["tail"]
args: ["-f", "/dev/null"]
terminationGracePeriodSeconds: 0
EOF
# 파드 확인 : IP 확인
kubectl get pod -o wide
2. 파드 네트워크 상태 확인 및 통신 테스트
- pod-1에 접속하여, 파드의 네트워크 설정을 확인하고, GW IP와 다른 파드(pod-2)로의 통신을 테스트합니다.
kubectl exec -it pod-1 -- zsh
-----------------------------
ip -c addr show eth0
2-1) cni0 - tcpdump
# [터미널1,2] 워커 노드1,2
docker exec -it myk8s-worker bash
docker exec -it myk8s-worker2 bash
-----------------------------
tcpdump -i cni0 -nn icmp
# [터미널3] pod-1
# GW IP는 어떤 인터페이스인가? (1) flannel.1 (2) cni0
ip -c route
route -n
ping -c 1 <GW IP>
ping -c 1 <pod-2 IP> # 다른 노드에 배포된 파드 통신 확인
ping -c 8.8.8.8 # 외부 인터넷 IP 접속 확인
curl -s wttr.in/Seoul # 외부 인터넷 도메인 접속 확인
ip -c neigh
exit
-----------------------------
2-2) flannel.1 - tcpdump
# [터미널1,2] 워커 노드1,2
docker exec -it myk8s-worker bash
docker exec -it myk8s-worker2 bash
-----------------------------
tcpdump -i flannel.1 -nn icmp
# [터미널3] pod-1
# GW IP는 어떤 인터페이스인가? (1) flannel.1 (2) cni0
ip -c route
route -n
ping -c 1 <GW IP>
ping -c 1 <pod-2 IP> # 다른 노드에 배포된 파드 통신 확인
ping -c 8.8.8.8 # 외부 인터넷 IP 접속 확인
curl -s wttr.in/Seoul # 외부 인터넷 도메인 접속 확인
ip -c neigh
exit
-----------------------------
- 외부랑 통신할때 flannel 인터페이스를 안거치고 가는 것을 확인 가능합니다.
- 즉, flannel 인터페이스는 다른 노드랑 통신할 때만 필요한 것을 알 수 있습니다.
'K8s > K8S Flannel CNI & PAUSE' 카테고리의 다른 글
Pod & PAUSE 컨테이너 (0) | 2024.09.08 |
---|---|
Kind 소개 및 설치 (0) | 2024.09.08 |