새소식

Kubernetes

[Study 2주차] 쿠버네티스 Flannel CNI

  • -

CloudNet@ 팀의 가시다님께서 리딩하시는 KANS Study (Kubernetes Advanced Networking Study) 2주차 스터디 내용 정리

 

이번에는 Flannel 을 공부하면서, 쿠버네티스 CNI 에 대해서 이해해보겠습니다.

 

 

1. CNI (Container Network Interface)

 

CNI 는 컨테이너 간 네트워크 제어 플러그인을 작성하기 위한 표준입니다.

 

 

  • 쿠버네티스 네트워크 모델 요구사항 4가지
  1. 파드와 파드 간 통신 시 NAT 없이 통신 가능
  2. 노드 에이전트는 파드와 통신 가능
  3. 호스트 네트워크 파드는 NAT 없이 파드와 통신 가능
  4. 서비스 클러스터 IP 대역과 파드 IP 대역 간 중복 방지

 

  • CNI 가 해결해야 하는 쿠버네티스 네트워크 통신 문제
  1. 파드 내부 컨테이너는 루프백 (Loopback) 을 통한 통신 가능
  2. 파드 간 통신 가능
  3. 클러스터 내부에서 쿠버네티스 서비스를 통한 통신 가능
  4. 클러스터 외부에서 쿠버네티스 서비스를 통한 통신 가능

 

이러한 요구사항을 충족하는 CNI 는 종류가 다양하지만, 이번에는 가볍고 간단하게 동작할 수 있는 Flannel 을 알아보겠습니다.

 

 

1.1. Flannel CNI 소개 및 설치

 

Flannel 은 단일 바이너리 에이전트인 flanneld 가 각 노드에서 동작하여, 작은 규모의 클러스터 환경에서 파드 간 통신 환경을 구성해주는 CNI 입니다.

 

  • kind 를 통한 실습환경 구축
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


kind 는 kindnet 이라는 기본 CNI 가 같이 배포되는데, Flannel CNI 설치를 위해 배포하지 않겠다는 설정을 하는 것입니다.

networking:
  disableDefaultCNI: true

 

# 클러스터 배포
kind create cluster --config kind-cni.yaml --name myk8s --image kindest/node:v1.30.4

# Node 와 Pods 상태 확인
kubectl get nodes -o wide
kubectl get pods -A -o wide

 

CNI 가 배포되지 않았기 때문에, Node 가 NotReady 상태이며 Static Pod 를 제외한 모든 파드가 배포되지 않았습니다.

 

# 실습을 위한 네트워크 도구 설치
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'

 

  • Flannel CNI 설치
# Flannel 설치
kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml

# Flannel 확인
kubectl get ds,pod,cm -n kube-flannel

 

 

1.2. Bridge 에러 해결

 

Flannel 을 설치하더라도, CoreDNS 가 배포되지 않습니다.

kubectl describe pod -n kube-system -l k8s-app=kube-dns

 

'/opt/cni/bin' 안에 '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

 

# 로컬에 복사한 bridge 파일을 kind 노드에 배포
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

 

문제가 해결되어 파드가 배포되었습니다.



1.3. Flannel 정보 확인

 

# 각 노드의 파드 서브넷 대역
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

# 파드의 Public IP
kubectl describe node | grep -A3 Annotations

 

 

  • 노드 내부에서 Flannel 프로세스 확인
docker exec -it myk8s-worker bash

lsns -p $(pgrep flanneld)
lsns -p $(pgrep kube-proxy)

 

 

# 노드의 라우팅 정보 확인
ip -c route

 

라우팅 정보를 확인해보면, CNI 가 자동으로 내부 네트워크 라우팅을 잡아주는 것을 확인할 수 있습니다.

 

 

1.4. 쿠버네티스 파드 통신

 

파드 간 통신 테스트를 위해 2개의 파드를 배포합니다.

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

 

  • 파드의 네트워크 통신 흐름 파악

스터디 멤버 중 한 분이신 광순님께서 테스트 흐름도를 너무 이해하기 쉽게 만들어 주셨습니다.

출처 :  https://kschoi728.tistory.com/249

 

 

kubectl exec -it pod-1 -- zsh

# 호스트 GW IP 호출 (동일 노드 간 내부 통신)
ping 10.244.1.1

# Pod2 IP 호출 (다른 노드의 파드 간 통신)
ping 10.244.2.2

# 외부 인터넷 IP 호출
ping 8.8.8.8

# 외부 인터넷 도메인 ghcnf
curl -s wttr.in/Seoul

 

 

  • Node 의 eth0 인터페이스에서 패킷 덤프
## Terminal 1번
# Node 1번 접속
docker exec -it myk8s-worker  bash

# eth0 에서 UDP 패킷 덤프
tcpdump -i eth0 -nn udp port 8472 -w /root/vxlan.pcap 

## Terminal 2번
# Pod 접근
kubectl exec -it pod-1 -- zsh

# 다른 노드의 파드로 Ping 전송
ping 10.244.2.2

 

성공적으로 ping 을 전송했다면, Wireshark 를 통해 덤프를 확인해봅니다.

# Node 에 덤프 뜬 패킷을 로컬로 복사
docker cp myk8s-worker:/root/vxlan.pcap .

# Wireshark 로 패킷 확인
wireshark vxlan.pcap

 

Flannel 은 다른 노드의 파드와 통신할 때, UDP 8472 를 사용하여 전송합니다.


만약 Wireshark 에서 Source / Destination IP 정보가 제대로 안보인다면,

Preference 설정에서 VXLAN 프로토콜을 8472로 설정해주면 됩니다.

 

 


 

이번 주차 스터디를 통해서 쿠버네티스의 파드 간 통신과 CNI 에 대해 잘 이해할 수 있었습니다.
처음에는 하나도 몰라서 멘붕이 왔는데, 반복해서 학습을 하다보니 내것이 되어가고 있는 느낌이었습니다.

다음은 어떤 쿠버네티스 네트워크를 배울 지 기대가 되네요!

Contents

포스팅 주소를 복사했습니다