본문 바로가기
K8s/K8S Flannel CNI & PAUSE

Flannel CNI

by 식사법 2024. 9. 8.

Flannel CNI

1. K8S 네트워킹

  • 파드들은 각자 고유한 IP 주소를 가지고 있으며, 네트워크 상에서 라우팅을 통해 직접 접근할 수 있습니다. 파드가 실행 중인 노드에 상관없이, 다른 노드에 있는 파드와 IP 기반으로 통신이 가능합니다.
  • 노드는 해당 노드에서 실행 중인 파드에 직접 접근할 수 있습니다.
  • 노드 내에서 파드와 통신할 때는 특별한 NAT(Network Address Translation) 없이 통신이 이루어집니다.
  • 클러스터 외부에서 파드로 트래픽을 라우팅할 수 있는 메커니즘이 필요합니다. 이를 위해 서비스로드 밸런서와 같은 쿠버네티스 리소스가 사용됩니다.
  1. K8S 네트워킹의 주요 컴포넌트
    • CNI (Container Network Interface)
      •    쿠버네티스 클러스터 내에서 네트워크 인터페이스를 설정하고 관리하기 위해 다양한 CNI 플러그인이 사용됩니다.
      •     CNI 플러그인은 파드가 생성되거나 삭제될 때 네트워크를 설정하고 해제하는 역할을 합니다. 대표적인 CNI 플러그인으로는 Flannel, Calico, Weave 등이 있습니다.
    • 서비스 (Service)
      •     쿠버네티스의 서비스는 파드의 네트워크를 외부로 노출시키는 역할을 합니다.
      •     서비스는 로드 밸런싱을 통해 여러 파드에 트래픽을 분산시키며, 파드의 IP가 동적으로 변경되더라도 안정적인 네트워크 접근을 보장합니다.
    • 로드 밸런서 (Load Balancer)
      •     클러스터 외부에서 트래픽을 클러스터 내부로 라우팅할 수 있도록 지원합니다.
      •     주로 클라우드 환경에서 외부 트래픽을 여러 파드로 분산시키는 데 사용됩니다.

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

image.png

image.png
image.png

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'

image.png

)

image.png

)

image.png

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

image.png

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

image.png

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

image.png

)

image.png

)

image.png

)

image.png

  • 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의 설정을 확인하여 트래픽 흐름을 분석할 수 있습니다.

image.png

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)

image.png

# 기본 네트워크 정보 확인
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'

----------------------------------------

image.png

)

파드 네트워크 통신 및 패킷 캡처 실습

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

image.png

2. 파드 네트워크 상태 확인 및 통신 테스트

  • pod-1에 접속하여, 파드의 네트워크 설정을 확인하고, GW IP와 다른 파드(pod-2)로의 통신을 테스트합니다.
kubectl exec -it pod-1 -- zsh
-----------------------------
ip -c addr show eth0

image.png

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

image.png

  • 외부랑 통신할때 flannel 인터페이스를 안거치고 가는 것을 확인 가능합니다.
  • 즉, flannel 인터페이스는 다른 노드랑 통신할 때만 필요한 것을 알 수 있습니다.

'K8s > K8S Flannel CNI & PAUSE' 카테고리의 다른 글

Pod & PAUSE 컨테이너  (0) 2024.09.08
Kind 소개 및 설치  (0) 2024.09.08