연재 시리즈

쿠버네티스 네트워크 스터디 2주차 1편: Flannel CNI

악분 2022. 1. 17. 19:13
반응형

스터디 목차

 

안녕하세요. 이 글은 facebook 쿠버네티스 그룹에서 올라온 "쿠버네티스 네트워크 스터디" 2주차 내용을 정리했습니다.

스터디 모집글: https://www.facebook.com/groups/k8skr/posts/3202691746679143

 

1. 2주차 첫번째 주제

2주차 첫번째 주제는 Flannel 통신을 이해하는 시간을 가졌습니다. 스터디 진행 목차는 아래 그림과 같습니다.

 

2. 실습환경

vagrant로 실습환경을 구성했습니다. 쿠버네티스 클러스터는 ControlPlane 노드 1대, Worker 노드 2대로 구성되어 있습니다.

 

 

3. 쿠버네티스 CNI(Conatiner Network Interface)

3.1 CNI이란?

쿠버네티스는 컨테이너 네트워크 구조와 통신방법을 인터페이스(표준)로 4개[1]로 정의했습니다. 4가지 인터페이스와 함께 공통적으로 지켜야하는 규약(POD는 고유한 IP를 가져야한다 등)이 있습니다.

  ① 컨테이너와 컨테이너 통신

  ② pod와 pod 통신

  ③ pod와 service 통신

  ④ pod와 외부 통신

 

3.2 네트워크 모델이란?

4가지 인터페이스를 준수하여 구현한 것을 쿠버네티스 네트워크 모델이라고 부릅니다. Flannel은 쿠버네티스 네트워크 모델 중 하나입니다. 쿠버네티스 네트워크 모델은 사용자가 상황에 맞게 선택하여 고를 수 있도록 애드온으로 취급합니다. 쿠버네티스 API, 스케쥴러처럼 쿠버네티스 컴퍼넌트가 아닙니다.

출처: https://twitter.com/learnk8s/status/1142703983909384193

 

3.3 overlay이란?

노드들의 네트워크 망을 한개의 네트워크 망처럼 인식하는 것을 overlay라고 부릅니다.

 

CNI의 재미있는 부분은 물리적으로 떨어져 있는 노드 네트워크 망을 마치 하나의 네트워크 망처럼 인식하여 통신해야 한다는 점입니다. 노드의 컨테이너 네트워크 구성은 결국 가상 네트워크이고, 물리적으로 떨어져 있는 가상 네트워크가 한 개의 가상 네트워크처럼 동작해야한다는 점이 매우 흥미롭습니다.

 

4. Flannel 상세내용

4.1 Flannel pod

Flannel 네트워크 모델은 데몬셋으로 실행됩니다. 노드 갯수만큼 flannel pod가 실행됩니다. 

 

아래 예제는 controlplane노드를 포함해서 노드가 총 3개여서 flannel pod 3개가 있습니다.

kubectl get po -n kube-system | grep flannel

 

4.2 추가된 인터페이스 확인

flannel은 flannel.1과 cni0인터페이스를 노드에 추가합니다. 아래 예제는 controlplane 노드에서 인터페이스를 확인했습니다. cni0인터페이스는 flannel pod이외에 pod가 1개이상 존재해야 생성되므로 워커노드에 없을 수 있습니다. 

ip -c -br addr

 

각 노드는 아래처럼 네트워크 구조를 가집니다. enp0s8은 노드가 가지고 있는 물리 인터페이스입니다.

 

4.3 노드 네트워크 대역 확인

각 flannel.1인터페이스는 노드의 네트워크 대역을 설정합니다. 노드 네트워크 대역은 flannel을 설치할 때 FLANNEL_NETWORK필드 값을 따릅니다.

cat /run/flannel/subnet.env

 

각 노드의 flannet.1인터페이스 네트워크 대역은 ip명령어로 확인할 수 있습니다. 

ip -c -d addr show flannel.1

controlplane flannet.1

 

worker1 flannet.1

 

worker2 flannet.1

 

cni인터페이스는 pod끼리 통신하기 위한 가상 브릿지역할을 담당합니다. IP대역은 flannel.1에 영향을 받습니다. cni인터페이스는 pod가 1번이라도 생성되야 자동으로 생성됩니다.

ip -c -d addr show cni0

controlplane cni0 인터페이스

 

지금까지 내용을 정리하면 아래 그림과 같습니다.

 

4.4 pod IP대역 확인

pod IP대역은 노드의 flannel.1 네트워크 대역에 영향받습니다. controlplane에서 노드정보에서 podCIDR을 필터링하면 각 노드의 pod IP대역을 확인할 수 있습니다.

kubectl get node k8s-m -o json | jq '.spec.podCIDR'
kubectl get node k8s-w1 -o json | jq '.spec.podCIDR'
kubectl get node k8s-w2 -o json | jq '.spec.podCIDR'

 

4.5 iptables 확인

flannel은 가상브릿지를 이용한 내부통신(FORWARD)과 외부통신(POSTROUTING)을 위해 iptables이 설정되어 있습니다.

 

iptables -t filter -S | grep 172.16.

 

iptables -t nat -S | grep 'A POSTROUTING'

 

4.6 노드 라우팅 테이블

pod가 다른 노드의 pod로 통신하기 위한 라우팅 테이블이 설정되어 있습니다. 다른 노드로 라우팅될 때 flannel.1 인터페이스를 거쳐가는 것을 확인할 수 있습니다. pod 자신 네트워크 대역은 cni0 인터페이스를 거쳐갑니다.

 

ip -c route | grep 172.16.

controlplane 라우팅 테이블

 

worker1 라우팅 테이블

 

worker2 라우팅 테이블

 

4.6 Overlay구현: VXLAN

다른 노드에 있는 pod와 통신하기 위해서 VXLAN을 사용합니다. VXLAN은 Flannel인터페이스를 이용해서 UDP프로토콜로 출발지, 도착지 pod식별정보(IP 등)을 캡슐화&디캡슐화합니다.

 

5. 실습준비 - pod생성

nodeselector을 이용해서 worker1에 pod-1을 worker2에 pod-2를 생성합니다.

cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
  name: pod-1
  labels:
    app: pod
spec:
  containers:
  - name: netshoot-pod
    image: nicolaka/netshoot
    command: ["tail"]
    args: ["-f", "/dev/null"]
  terminationGracePeriodSeconds: 0
  nodeSelector:
    kubernetes.io/hostname: k8s-w1
---
apiVersion: v1
kind: Pod
metadata:
  name: pod-2
  labels:
    app: pod
spec:
  containers:
  - name: netshoot-pod
    image: nicolaka/netshoot
    command: ["tail"]
    args: ["-f", "/dev/null"]
  terminationGracePeriodSeconds: 0
  nodeSelector:
    kubernetes.io/hostname: k8s-w2
EOF

 

pod가 생성되면 워커노드는 아래 그림과 같이 구성됩니다.

 

6. 실습

6.1 pod IP확인

pod-1은 worker1노드에 생성되었으므로 172.16.1.0/24 네트워크 대역 IP를 갖습니다. pod-2는 worker노드에 생성되었으므로 172.16.2.0/24 네트워크 대역 IP를 갖습니다.

 

kubectl명령어로 확인하면 예상한 대로 POD IP가 할당되었습니다.

kubectl get po -o wide

 

6.2 cni인터페이스 관점 확인

cni(가상브릿지)관점에서는 새로 생성된 pod의 인터페이스가 연결되어 있습니다. 연결된 인터페이스는 brctl명령어로 확인할 수 있고 연결된 IP 는 cbr0디렉터리에서 확인할 수 있습니다.

brctl show
tree /var/lib/cni/networks/cbr0

worker1노드 인터페이스 확인

 

6.3 host에 pod ping테스트

pod-1를 실행하는 worker1노드에서 pod-1에게 ping을 실행할 때 패킷을 캡쳐해보겠습니다. worker1노드는 자신이 실행하는 pod에 대해서는 cni0인터페이스를 통해서 pod와 통신합니다.

 

화면을 분할하고 tcpdump로 ICMP패킷을 캡처합니다. 그리고 pod-1 IP인 172.16.1.2에게 ping을 실행하면 172.16.1.1(cni0) <-> 172.168.1.2(pod-1)간 ICMP패킷이 주고받고하는 것을 확인할 수 있습니다. 

tcpdump -i cni0 -nn icmp
ping <pod-1 IP>

 

6.4 다른 노드의 POD와 통신

worker1에 실행되고 있는 pod-1에서 worker2에 실행되고 있는 pod-2에 ping을 실행했을 때 흐름을 이해해봅시다.

 

enp0s8에 tcpdump를 실행합니다.

 

그리고 pod-1에서 172.16.2.2로 ping을 날립니다.

kubectl exec -it pod-1 -- /bin/bash
bash-5.1# ping 172.16.2.2

 

cni0인터페이스 입장에서는 라우팅 테이블에 설정에 따라 flannel.1에게 패킷을 전달합니다.

 

flannel.1인터페이스 입장에서는 (아마도 flannel 데몬셋이 제어) 다른 노드로 가는 패킷이므로 enp0s8인터페이스로 패킷을 전달합니다. 이때, VXLAN이 적용되어 POD식별정보가 UDP패킷으로 캡슐화됩니다.

 

flannel은 VXLAN통신을 할 때 UDP프로토콜 8472을 사용합니다. tcpdump에 같은 조건으로 필터링하면 worker1(192.168.100.102) -> worker2(192.169.100.102)통신하는 과정을 볼 수 있습니다.

 

worker2의 flannel.1인터페이스는 pod식별정보를 디캡슐화해서 cni0인터페이스로 패킷을 전송하고 pod까지 패킷이 전달됩니다.

 

6.5 pod의 외부통신

pod의 외부통신은 외부와 연결되어 있는 host인터페이스를 이용해서 외부통신을 합니다. 이 예제에서는 enp0s3가 외부와 연결되어 있는 인터페이스입니다.

 

enp0s3에 tcpdump를 실행하고 pod-1에서 구글서버에 ping을 날리면, tcpdump에서 패킷이 캡처됩니다. 즉, host enp0s3인터페이스를 이용해서 pod가 외부와 통신하고 있습니다.

tcpdump -i enp0s3 -nn icmp
kubectl exec -it pod-1 -- /bin/bash
bash-5.1# ping 8.8.8.8

 

7. pod 갯수 제한

pod는 IP를 할당받아야하기 때문에 pod생성 갯수는 노드의 CNI 인터페이스 네트워크 대역과 동일합니다. 192.168.0.0/24 네트워크 대역을 가지고 있다면 pod는 최대 255개 생성될 수 있습니다. 

 

실험해보고 싶었는데 실습환경 컴퓨터 사양이 낮아 진행을 못했습니다. ㅜ.ㅜ

 

참고자료

[1] [쿠버네티스 공식문서] 쿠버네티스 CNI - https://kubernetes.io/ko/docs/concepts/cluster-administration/networking/

[2] [쿠버네티스 공식문서] Pod unique IP - https://kubernetes.io/docs/concepts/workloads/pods/#pod-networking

[3] [블로그] https://gruuuuu.github.io/cloud/docker-rhel02/

반응형