본문 바로가기
K8s/Advanced Network

[KANS3] 2. Pause 컨테이너 + Flannel CNI

by okms1017 2024. 9. 8.
728x90

✍ Posted by Immersive Builder  Seong

 

1. Kind(Kubernetes in docker) 

Kind 란?

Kind(Kubernetes in docker)는 도커 컨테이너를 사용하여 로컬 환경에서 쿠버네티스 클러스터를 실행할 수 있는 도구입니다.

 

쿠버네티스 클러스터 구성 시, kubeadm을 사용하여 클러스터를 생성하고 워커 노드를 조인합니다. 그리고 컨트롤 플레인과 각 워커 노드는 도커 컨테이너로 동작합니다. 도커 컨테이너 내에 도커 데몬이 하나 더 실행되는 구조로, 각 쿠버네티스 컴포넌트들은 컨테이너 내의 컨테이너로 실행이 됩니다. 따라서 호스트 도커에서는 노드 컨테이너 내부에서 실행되는 도커 컨테이너의 정보를 조회할 수 없는 특징이 있습니다. 

 

Kubernetes in docker

 

Kind는 별도의 물리 서버를 추가하거나 클라우드 인프라를 프로비저닝하지 않고도 로컬 환경에서 빠르고 가볍게 클러스터를 설정할 수 있는 장점 때문에 개발 및 테스트 목적으로 사용하기에 적합합니다.

 

반면, 대규모 어플리케이션이나 고성능을 요구하는 경우에는 성능 및 네트워크 구성의 한계가 있을 수 있으므로 실제 운영환경에서 사용하는 것은 권장하지 않습니다. 

 

Kind 설치 

▶ Specification 

 

   Host OS    Windows 10 
   HyperVisor    VirtualBox 7.0.20 
   Guest OS    ubuntu 22.04 
   vCPU    8 
   Mem    16 GiB 
   Installed Tools    docker, kind, kubectl, helm

 

▶ Vagrant 설치 

 

Vagrant는 VirtualBox와 연동성이 좋습니다. 로컬 PC 환경에 맞는 바이너리 파일을 다운받아 설치합니다. 

Install Vagrant 1
Install Vagrant 2

 

▶ Ubuntu VM 배포 (Kind 설치 포함)

 

Vagrantfile을 사용하여 Ubuntu VM을 1대 배포하고 Kind를 설치합니다. 

Vagrant - Ubuntu VM 배포

* 설치 확인 

> vagrant status

Current machine states:
default                   running (virtualbox)

> vagrant ssh

Welcome to Ubuntu 22.04.4 LTS (GNU/Linux 5.15.0-119-generic x86_64)

root@kind:~# kind --version
kind version 0.24.0
root@kind:~# kubectl version --client=true
Client Version: v1.31.0
Kustomize Version: v5.4.2
root@kind:~# helm version
version.BuildInfo{Version:"v3.15.4", GitCommit:"fa9efb07d9d8debbb4306d72af76a383895aa8c4", GitTreeState:"clean", GoVersion:"go1.22.6"}

 

▶ Kind로 쿠버네티스 클러스터 배포 (단일 노드) 

 

# 클러스터 배포 
$ kind create cluster

# 클러스터 배포 확인
$ kind get clusters
kind
$ kind get nodes
kind-control-plane
$ kubectl cluster-info
Kubernetes control plane is running at https://127.0.0.1:44485
CoreDNS is running at https://127.0.0.1:44485/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

# 노드/파드 정보 확인
$ kubectl get node -o wide
NAME                 STATUS   ROLES           AGE     VERSION   INTERNAL-IP   EXTERNAL-IP   OS-IMAGE                         KERNEL-VERSION       CONTAINER-RUNTIME
kind-control-plane   Ready    control-plane   5m57s   v1.31.0   172.18.0.2    <none>        Debian GNU/Linux 12 (bookworm)   5.15.0-119-generic   containerd://1.7.18
$ kubectl get pod -A
NAMESPACE            NAME                                         READY   STATUS    RESTARTS        AGE
kube-system          coredns-6f6b679f8f-fl7n9                     1/1     Running   0               5m43s
kube-system          coredns-6f6b679f8f-tg9d4                     1/1     Running   0               5m43s
kube-system          etcd-kind-control-plane                      1/1     Running   0               5m45s
kube-system          kindnet-jwdft                                1/1     Running   0               5m28s
kube-system          kube-apiserver-kind-control-plane            1/1     Running   2 (6m50s ago)   5m45s
kube-system          kube-controller-manager-kind-control-plane   1/1     Running   0               5m45s
kube-system          kube-proxy-7778d                             1/1     Running   0               5m43s
kube-system          kube-scheduler-kind-control-plane            1/1     Running   0               5m45s
local-path-storage   local-path-provisioner-57c5987fd4-78wl2      1/1     Running   0               5m20s

# 도커 컨테이너 정보 확인
$ docker ps -a
CONTAINER ID   IMAGE                  COMMAND                  CREATED         STATUS         PORTS                       NAMES
6814f88a1db0   kindest/node:v1.31.0   "/usr/local/bin/entr…"   8 minutes ago   Up 8 minutes   127.0.0.1:44485->6443/tcp   kind-control-plane
$ docker images
REPOSITORY     TAG       IMAGE ID       CREATED       SIZE
kindest/node   <none>    b5cb8c3b1441   3 weeks ago   1.03GB

# 컨트롤플레인 Taints 정보 확인
$ kubectl describe node | grep Taints
Taints:             <none>

# 클러스터 삭제 
$ kind delete cluster

 

Kind 기본 사용 

Kind는 Dind(Docker in docker) 방식으로 동작하며, 노드 컨테이너 내부에서 쿠버네티스 관련 파드가 컨테이너로 실행되는 구조입니다. 

 

Kind Architecture

 

클러스터 배포 1 :  Control Plane + Worker Node 1

 

Kind는 브릿지 모드의 도커 네트워크를 별도로 생성하여 사용합니다. 

기본 네트워크 대역은 172.18.0.0/16 으로 해당 범위 내에서 컨트롤 플레인과 워커 노드의 IP가 할당됩니다.  

 

멀티 노드를 구성한다면 매니페스트 파일의 nodes 속성에 role을 추가할 수 있습니다.

여기서는 컨트롤 플레인과 워커 노드 한 대를 지정합니다. 

# kind-2node.yaml 

kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker

 

클러스터를 배포하는 과정에서 워커 노드를 조인하는 단계를 확인할 수 있습니다. 

 

배포 이후, 컨트롤 플레인 컨테이너의 kube-apiserver를 호출하기 위해 44921:6443 포트로 포워딩하고 있습니다. 

여기서 호스트의 포트는 랜덤으로 할당됩니다. 

 

Liveness Probe와 Readiness Probe도 정상적으로 동작하는 모습입니다. 

 

Kind는 경량의 CNI Plugin인 kindnet을 사용합니다. 

 

클러스터의 API 서버 통신에 필요한 인증 정보가  kube-config(/root/.kube/config) 파일에 저장됩니다. 

 

클러스터 관리에 필요한 패키지 설치를 진행합니다. Kind를 사용하면 루트 계정의 권한을 가지고 파드 컨테이너에 접근하므로 apt를 이용한 패키지 설치가 용이합니다. 

 

참고로 노드 컨테이너 내부에서는 docker 명령어 대신 crictl 명령어를 사용합니다. 

 

netshoot과 nginx 두 개의 파드를 생성하고 동작을 확인합니다. VM 환경에 Dind로 컨테이너를 띄우기 때문에 파드가 Running 상태가 되기까지 상당한 시간(약 6분 20초)이 소요됩니다. => "Kind는 mac 실습환경을 추천"

 

netshoot 파드에서 nginx 웹 접속이 되는지 확인합니다. => "정상"

 

Kind는 도커 이미지를 가져올 때 호스트의 CPU Architecture에 부합하는 이미지를 찾아 가져옵니다. 

 

노드 컨테이너의 엔트리포인트는 "/usr/local/bin/entrypoint", "/sbin/init" 이고, 컨테이너 내부에 접속하여 프로세스 정보를 조회하면 Init 프로세스임을 확인할 수 있습니다. 

 

그리고 호스트에서 도커 명령어로 조회하면 내부 파드 컨테이너는 조회되지 않습니다. 

 

 

클러스터 배포 2 : Mapping Ports

 

워커 노드를 배포할 때 호스트의 노드 포트를 지정하여 컨테이너 포트와 매핑할 수 있습니다. 

아래와 같이 매핑하면 호스트 30000-30001/TCP 포트 접속 시 컨테이너의 30000-30001/TCP 포트로 연결한다는 의미입니다. 

# kind-2node-2.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
  extraPortMappings:
  - containerPort: 30000
    hostPort: 30000
    listenAddress: "0.0.0.0" # Optional, defaults to "0.0.0.0"
    protocol: tcp # Optional, defaults to tcp
  - containerPort: 30001
    hostPort: 30001
EOT

 

 

기본적으로 워커 노드를 구성하면 컨트롤 플레인에는 파드가 스케줄링되지 않도록 Taints 설정이 자동 적용됩니다. 

 

워커 노드의 포트 매핑 정보를 확인합니다. 

 

이제 노드 포트 30000/TCP 접속 시 kube-ops-view를 띄우도록 설정해보겠습니다. 

 

# kube-ops-view 설치 
$ helm repo add geek-cookbook https://geek-cookbook.github.io/charts/
$ helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 --set service.main.type=NodePort,service.main.ports.http.nodePort=30000 --set env.TZ="Asia/Seoul" --namespace kube-system

# kube-ops-view 설치 확인
$ kubectl get deploy,pod,svc,ep -n kube-system -l app.kubernetes.io/instance=kube-ops-view
NAME                            READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/kube-ops-view   0/1     1            0           4s

NAME                                 READY   STATUS              RESTARTS   AGE
pod/kube-ops-view-657dbc6cd8-dnqt7   0/1     ContainerCreating   0          4s

NAME                    TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
service/kube-ops-view   NodePort   10.96.123.128   <none>        8080:30000/TCP   4s

NAME                      ENDPOINTS   AGE
endpoints/kube-ops-view   <none>      4s

# kube-ops-view 접속 URL 
$ echo -e "KUBE-OPS-VIEW URL = http://192.168.50.10:30000/#scale=1.5"
echo -e "KUBE-OPS-VIEW URL = http://192.168.50.10:30000/#scale=1.5"

 

노드 포트 30000/TCP로 접속하여 kube-ops-view 페이지를 확인합니다. 

 

나머지 노드 포트 30001/TCP는 Nginx 웹 서비스를 하도록 설정합니다. 

# nginx.yaml 

apiVersion: apps/v1
kind: Deployment
metadata:
  name: deploy-websrv
spec:
  replicas: 2
  selector:
    matchLabels:
      app: deploy-websrv
  template:
    metadata:
      labels:
        app: deploy-websrv
    spec:
      terminationGracePeriodSeconds: 0
      containers:
      - name: deploy-websrv
        image: nginx:alpine
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: deploy-websrv
spec:
  ports:
    - name: svc-webport
      port: 80
      targetPort: 80
      nodePort: 30001
  selector:
    app: deploy-websrv
  type: NodePort
EOF

 

 

노드 포트 30000/TCP로 웹 페이지가 접속되는 모습입니다. 

 


2. 파드와 Pause 컨테이너 

CRI(Container Runtime Interface)

CRI는 Kubelet과 컨테이너 런타임을 연결해주는 인터페이스입니다. CRI 표준 인터페이스를 통해서 Kubelet은 재컴파일할 필요 없이 다양한 컨테이너 런타임을 사용할 수 있습니다. 컨테이너 런타임은 High-Level(Containerd..)과 Lov-Level(RunC..) 단계로 구분됩니다. Kubelet과 Containerd 간에는 CRI 기반으로 통신하고, Containerd와 RunC 간에는 OCI 기반으로 통신합니다.  

 

 

Kubelet은 gRPC 프레임워크를 사용하여 컨테이너 런타임과 유닉스 소켓 통신을 합니다. 여기서 Kubelet은 클라이언트 역할을 하고 Containerd는 서버 역할을 합니다. 프로토콜 버퍼 API에는 ImageService와 RuntimeService 두 가지 gRPC 서비스가 포함되어 있습니다. ImageService는 레지스트리에서 이미지를 가져오고, 이미지를 검사하고, 제거하는 RPC를 제공합니다. RuntimeService는 파드와 컨테이너의 라이프사이클을 관리하는 RPC를 제공하고, 컨테이너와 상호작용하기 위한 호출을 포함합니다. 

 

파드

컨테이너 어플리케이션의 기본 단위를 파드라고 하며, 파드는 1개 이상의 컨테이너로 구성된 컨테이너의 집합입니다. 

 

  • 파드 내 실행되는 컨테이너들은 동일한 라이프 사이클을 가집니다. 
  • 파드 내 컨테이너들은 서로 IP를 공유하고 컨테이너 간에는 localhost를 통해 접근 및 포트로 구분합니다. 
  • 파드는 노드와 별개로 클러스터 내에서 접근 가능한 IP를 할당받으며, CNI를 통해 다른 노드의 파드와 통신이 가능합니다. 
  • 파드 내 컨테이너들은 동일한 볼륨으로 연결이 가능하여 파일 시스템 기반으로 파일을 주고 받을 수 있습니다. 
  • 파드는 리소스 제약이 있는 격리된 환경의 어플리케이션 그룹으로 구성되고, CRI에서 이 환경을 PodSandbox라고 합니다. 
  • 파드를 시작하기 전에 Kubelet은 RuntimeService.RunPodSandbox를 호출하여 환경을 구성합니다. 

 

Pause 컨테이너

파드가 생성될 때 가장 먼저 시작되는 컨테이너로 NET/IPC/UTS 네임스페이스를 파드 내 컨테이너들에게 공유하고 유지하는 역할을 수행합니다. 그리고 SIGINT, SIGTERM, SIGCHLD 등 적절한 신호 처리를 수행합니다. 

 

  • 쿠버네티스에서 Pause 컨테이너는 파드의 모든 컨테이너에 대한 부모 컨테이너 역할을 합니다. 
  • 첫째, 파드 내에서 리눅스 네임스페이스 공유의 기반 역할을 합니다. 
  • 둘째, PID 네임스페이스 공유가 활성화되면 각 파드에 대한 PID 1 역할을 하며 좀비 프로세스를 거둡니다. 

* 워커 노드 진입 

워커 노드 컨테이너에는 Containerd 런타임과 Kubelet 서비스가 실행 중입니다. 

 

하기 명령어로 프로세스 ID, 호스트와 다른 네임스페이스 정보를 출력할 수 있습니다. 

root@myk8s-worker:/# pstree -aclnpsS  

```

  `-containerd-shim,1418 -namespace k8s.io -id 931b6b97674beee2566f601b6bf37a24015929f8d1b1e0a2e57f7435c54576b5 -address /run/containerd/containerd.sock
      |-{containerd-shim},1419
```
      |-pause,1440,ipc,mnt,net,pid,uts
      |-{containerd-shim},1452
      |-python3,1531,cgroup,ipc,mnt,net,pid,uts -m kube_ops_view
```

 

Pause 컨테이너가 실행되면서 새로운 net/mnt/uts/ipc/pid 5개의 네임스페이스를 생성하고, 이후에 실행된 kube_ops_view 컨테이너는 Pause 컨테이너의 net/uts/ipc 네임스페이스를 공유받게 됩니다. 

 

신규 Nginx 파드를 배포하고 확인합니다. 

# nginx-pod.yaml

apiVersion: v1
kind: Pod
metadata:
  name: myweb
spec:
  containers:
  - image: nginx:alpine
    name: myweb-container
    ports:
    - containerPort: 80
      protocol: TCP
  terminationGracePeriodSeconds: 0
EOF

 

Pause 컨테이너의 정보는 kubectl 명령어로 조회되지 않습니다. 

- kubectl get pod myweb (x), kubectl describe pod myweb (x) 

 

워커노드에 다시 진입하여 Pause 컨테이너와 Nginx 컨테이너 간에 네임스페이스 정보를 비교합니다. 

  `-containerd-shim,2026 -namespace k8s.io -id 590e9e83c9a64ad4ed4ca1bd015ff7926f0b7ffd5d0a3894cb2579f1ec2efda8 -address /run/containerd/containerd.sock
      |-{containerd-shim},2027
```
      |-pause,2048,ipc,mnt,net,pid,uts
      |-nginx,2120,cgroup,ipc,mnt,net,pid,uts
```

 

마찬가지로 Pause 컨테이너는 net/mnt/uts/ipc/pid 네임스페이스를 새로 생성하고, 이후 실행된 nginx 컨테이너는 Pause 컨테이너의 net/uts/ipc 네임스페이스를 상속받습니다. 

 

이번에는 Nginx와 netshoot 컨테이너를 포함한 멀티 컨테이너 파드를 배포하고 확인합니다. 

 

# myweb2-nginx.yaml

apiVersion: v1
kind: Pod
metadata:
  name: myweb2
spec:
  containers:
  - name: myweb2-nginx
    image: nginx
    ports:
    - containerPort: 80
      protocol: TCP
  - name: myweb2-netshoot
    image: nicolaka/netshoot
    command: ["/bin/bash"]
    args: ["-c", "while true; do sleep 5; curl localhost; done"] # 포드가 종료되지 않도록 유지합니다

  terminationGracePeriodSeconds: 0

 

파드와 컨테이너의 IP 정보를 조회하면 10.244.1.4 로 모두 동일함을 확인할 수 있습니다.

 

그리고 netshoot 컨테이너에 진입하여 소켓 정보를 조회하였을 때 nginx 컨테이너의 리스닝 포트가 함께 조회됩니다. 그 이유는 파드 내의 두 컨테이너는 동일한 네트워크 네임스페이스를 공유하기 때문입니다. 

 

Nginx 컨테이너에 진입하여 웹 접속 로그를 추적해보면 netshoot 컨테이너에서 접속을 시도할때마다 로그가 쌓이는 것을 확인할 수 있습니다. 참고로 로그 출력의 '::1'은 IPv6 주소체계의 표현법으로 IPv4의 '127.0.0.1'과 동일합니다. 

 

그리고 워커 노드 컨테이너에서 컨테이너 정보를 조회하였을 때 두 컨테이너의 파드와 파드의 ID 값이 동일함을 확인할 수 있습니다. 

 

net/ust/ipc 네임스페이스는 파드 내 컨테이너 간 서로 공유하고, mnt/pid/cgroup 네임스페이스는 컨테이너별로 격리됨을 알 수 있습니다. 

 

마지막으로 두 컨테이너의 상세정보를 조회하여 타입별 네임스페이스의 경로를 확인합니다. 

경로가 /proc/[PID]/ns/{NS_TYPE]으로 표시된 타입의 네임스페이스는 부모 프로세스의 네임스페이스를 그대로 상속받으며, 경로가 표시되지 않은 타입의 네임스페이스는 자신의 네임스페이스를 가집니다. 

 


3. Flannel CNI

CNI 란?

CNI(Container Network Interface)는 쿠버네티스 환경에서 컨테이너 네트워크를 관리하기 위한 표준 인터페이스입니다. 

파드가 생성되면 쿠버네티스는 CNI 플러그인을 통해 파드 네트워크를 설정하도록 지시하며, CNI 플러그인은 각 파드에 IP 주소를 할당하고 네트워크 연결을 설정합니다. 

 

* 주요 CNI 플러그인 

  • Flannel : 간단한 오버레이 네트워크를 구성하는 데 사용되는 플러그인으로 소규모 클러스터에 적합합니다. 
  • Weave: 오버레이 네트워크뿐만 아니라 고급 네트워크 정책 관리 기능도 제공하는 플러그인입니다. 
  • Calico : 네트워크 보안 정책을 강력하게 지원하는 플러그인으로 BGP를 사용하여 네트워크를 관리합니다. 
  • Cilium : eBPF를 기반으로 고성능 네트워킹과 보안 정책을 제공하는 플러그인으로 쿠버네티스에서 마이크로 서비스 간의 통신을 세밀하게 제어합니다. 
  • VPC CNI : AWS EKS에서 쿠버네티스 파드의 네트워크를 관리하기 위해 사용되는 플러그인입니다. 

 

Flannel CNI 

Flannel CNI는 오버레이 네트워크를 구성하여 파드 간 통신을 구현하는 CNI 플러그인입니다. 

 

  • VXLAN 지원 : 터널링을 통해 가상의 네트워크를 생성, 파드의 패킷을 Encapsulation/Decapsulation.
  • 8472/UDP 포트 사용 

Flannel CNI

 

Flannel CNI 배포 

버전 정보

 

Kubernetes v1.30.4
Flannel CNI v0.25.6

 

클러스터 배포 : Control Plane + Worker Node 1,2 (Kindnet 비활성화)

 

Kind를 사용하여 멀티 노드 클러스터를 구성합니다. Flannel CNI 사용을 위해 Kindnet CNI는 비활성화합니다. 

# 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

 

쿠버네티스 버전을 v1.30.4로 지정하여 클러스터를 배포합니다. 

 

"--cluster-cidr=10.244.0.0/16" 은 디폴트로 파드에 할당하는 IP 대역입니다. 

 

노드의 상태가 NotReady 이고, coredns 파드가 Pending 상태입니다. 그 이유는 CNI를 배포하지 않았기 때문입니다. 

 

Flannel CNI 배포 

 

# Flannel CNI 설치
$ kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml
namespace/kube-flannel created
clusterrole.rbac.authorization.k8s.io/flannel created
clusterrolebinding.rbac.authorization.k8s.io/flannel created
serviceaccount/flannel created
configmap/kube-flannel-cfg created
daemonset.apps/kube-flannel-ds created

$ kubectl get ns --show-labels
$ kubectl describe cm -n kube-flannel kube-flannel-cfg
$ kubectl describe ds -n kube-flannel kube-flannel-ds

 

 

Flannel ConfigMap 정보를 조회합니다. Network는 파드에 할당되는 네트워크 대역이며, Backend.Type의 vxlan 값은 오버레이 네트워크 통신 시 사용하는 타입을 의미합니다. 

 

Flannel Daemonset 정보에서 Args의 "--ip-masq"는 파드가 외부로 통신할 때 노드의 IP로 마스커레이딩함을 의미합니다. 

 

Flannel CNI를 배포하였음에도 불구하고 coredns 파드가 동작하지 않습니다. => "FailedCreatePodSandBox"

"/opt/cni/bin" 디렉토리 경로에서 bridge 파일을 찾을 수 없는 것이 원인입니다.

 

bridge 파일을 생성하여 해당 경로(/opt/cni/bin)로 이동시킵니다. (공유파일로 진행이 안되어, 스크립트로 직접 파일을 생성하여 해결함. 생각보다 오랜 시간이 소요됨) 

 

이제 정상적으로 coredns 파드가 실행되는 모습입니다. 

 

워커 노드에 진입하여 네트워크 정보를 확인합니다. 호스트와 Flannel CNI가 네트워크 네임스페이스를 공유하여 노드의 IP로 동일합니다. 

 

Flannel 인터페이스는 VXLAN ID 1 값을 사용합니다. 

 

처음에는 cni0 인터페이스가 보이지 않습니다. 

 

그리고 다른 노드의 파드 IP 대역이 이미 라우팅 테이블에 등록되어 있습니다. 

 

따라서 다른 노드의 Flannel 인터페이스의 Vtep으로 ICMP 통신이 가능한 상태입니다. 

 

IPTables 필터 정책은 파드의 네트워크 대역끼리 통신할 때 허용하도록 정책이 설정되어 있습니다. 

또한, 멀티캐스트(파드 서브넷)를 제외한 목적지로 나가는 트래픽은 마스커레이딩하도록 정책이 설정되어 있습니다. 

 

통신 흐름 이해 

파드 2개 배포

 

다음과 같이 각 워커노드에 파드가 하나씩 배포되도록 합니다. 

# netshoot-pod-2.yaml 

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 정보를 확인합니다. 

 

각 워커 노드에서 네트워크 인터페이스를 모니터링하면 파드의 생성과 동시에 cni0 인터페이스가 생성됩니다. 

 

cbr0는 custom bridge로 CNI bridge 정보를 담고 있습니다. 

 

※ 네트워크 Specification

  Worker Node 1 (pod-1) Worker Node 2 (pod-2) 
Pod IP 10.244.2.2 10.244.1.5
cni0 10.244.2.1 10.244.1.1
flannel.1 10.244.2.0 10.244.1.0
eth0 (Node IP) 172.18.0.4 172.18.0.3

 

 

동일 노드 내 통신 흐름

 

동일 노드 내 위치한 파드 간에 통신 시 cni0 인터페이스를 거치며 flannel.1 인터페이스에서는 패킷이 보이지 않습니다. 

 

* pod-1 (10.244.2.2) → pod-11 (10.244.2.3)

 

1) cni0  =>  "동일 노드의 cni0 인터페이스를 통해서 통신이 완료된다" 

 

2) flannel.1  =>  "패킷 X, 통신은 cni0에서 해결된다" 

 

3) eth0  =>  "패킷 X, 통신은 cni0에서 해결된다" 

 

 

다른 노드 간 통신 흐름 

 

다른 노드에 위치한 파드 간 통신 시에는 cni0와 flannel.1 인터페이스를 모두 거치며 오버레이 통신을 하고 패킷 En/Decapsulation을 합니다. 

 

* pod-1 (10.244.2.2) → pod-2 (10.244.1.5) 

 

1) cni0 

 

2) flannel.1 

 

3) eth0 & tcp  => "UDP 오버레이 통신으로 TCP 패킷 X" 

 

4) eth0 & udp  => "오버레이된 패킷이 보인다" 

 

 

파드에서 외부 통신 흐름

 

파드에서 외부 인터넷으로 통신 시에는 flannel.1 인터페이스를 거치지 않고 나갑니다. 

 

* pod-1 (10.244.2.2) → Internet (8.8.8.8) 

 

1) cni0 

 

2) flannel.1  =>  "패킷 X"

 

3) eth0  =>  "SNAT 노드의 IP가 보인다"  

 

 


[출처]

1) CloudNet@, KANS 실습 스터디 

2) https://kind.sigs.k8s.io/

 

kind

kind is a tool for running local Kubernetes clusters using Docker container “nodes”. kind was primarily designed for testing Kubernetes itself, but may be used for local development or CI. If you have go 1.16+ and docker, podman or nerdctl installed go

kind.sigs.k8s.io

3) https://github.com/flannel-io/flannel

 

GitHub - flannel-io/flannel: flannel is a network fabric for containers, designed for Kubernetes

flannel is a network fabric for containers, designed for Kubernetes - flannel-io/flannel

github.com

728x90

'K8s > Advanced Network' 카테고리의 다른 글

[KANS3] 3. Calico CNI + Mode  (0) 2024.09.22
[KANS3] 1. 컨테이너 격리 + 컨테이너 네트워크  (3) 2024.09.01