CNI란?

CNI(Container Network Interface)는 CNCF(Cloud Native Computing Foundation)의 프로젝트로 Specification과 리눅스 컨테이너의 네트워크 인터페이스를 구성하기 위한 plugin을 작성하기 위한 라이브러리로 구성됩니다.

CNI는 컨테이너의 네트워크 연결성과 컨테이너가 삭제되었을 때 할당된 리소스를 제거하는 역할에 집중합니다.

참고: https://github.com/containernetworking/cni

 

보통 Kubernetes에 어떤 CNI를 쓰느냐라고 얘기를 하면 의미가 통하기는 하지만, 실제로 calico, cilium, flannel 등은 CNI plugin이라고 할 수 있습니다.

Kubernetes 에서 CNI Plugin의 동작은 간략히 아래와 같이 이뤄집니다.

  1. Kubelet이 Container Runtime에 컨테이너 생성을 요청
  2. Container Runtime이 컨테이너의 Network Namespace를 생성
  3. Container Runtime이 CNI 설정과 환경변수를 표준 입력으로 CNI Plugin 호출
  4. CNI Plugin이 컨테이너의 네트워크 인터페이스를 구성하고, IP를 할당하고, 호스트 네트워크 간의 veth pair 를 생성
  5. CNI Plugin이 호스트 네트워크 네임스페이스와 컨테이너 네트워크 네임스페이스에 라우팅을 구성

 

오래되긴 했지만 CNI와 CNI Plugin에 대해 잘 설명한 영상이 있습니다.

https://www.youtube.com/watch?v=4E_l-B988Ek&t=1341s

 

 

실습

간단히 영상의 실습을 따라해보겠습니다.

먼저 go가 설치된 환경에서 아래와 같이 샘플 CNI plugin을 가져와 빌드를 진행합니다. 빌드가 끝나면 bin 폴더에 바이너리들이 위치합니다.

root@jumpVM:~# git clone https://github.com/containernetworking/plugins.git
Cloning into 'plugins'...
remote: Enumerating objects: 19825, done.
remote: Counting objects: 100% (268/268), done.
remote: Compressing objects: 100% (180/180), done.
remote: Total 19825 (delta 151), reused 86 (delta 86), pack-reused 19557 (from 2)
Receiving objects: 100% (19825/19825), 16.41 MiB | 19.77 MiB/s, done.
Resolving deltas: 100% (11214/11214), done.
root@jumpVM:~# cd plugins/
root@jumpVM:~/plugins# ls
CONTRIBUTING.md  README.md         go.mod       plugins
DCO              RELEASING.md      go.sum       test_linux.sh
LICENSE          build_linux.sh    integration  test_windows.sh
OWNERS.md        build_windows.sh  pkg          vendor
root@jumpVM:~/plugins# ./build_linux.sh
Building plugins
  bandwidth
  firewall
  portmap
  sbr
  tuning
  vrf
  bridge
  dummy
  host-device
  ipvlan
  loopback
  macvlan
  ptp
  tap
  vlan
  dhcp
  host-local
  static
root@jumpVM:~/plugins# ls
CONTRIBUTING.md  README.md       build_windows.sh  pkg              vendor
DCO              RELEASING.md    go.mod            plugins
LICENSE          bin             go.sum            test_linux.sh
OWNERS.md        build_linux.sh  integration       test_windows.sh
root@jumpVM:~/plugins# cd bin
root@jumpVM:~/plugins/bin# ls
bandwidth  dummy        host-local  macvlan  sbr     tuning
bridge     firewall     ipvlan      portmap  static  vlan
dhcp       host-device  loopback    ptp      tap     vrf

 

그리고 3개의 세션을 만들어서 아래의 명령을 실행합니다.

## 좌측 세션 (demons 생성)
$ sudo ip netns add demons
## 우측 상단 세션 (host ns의 ip/route 정보 확인)
$ watch -d -n 1 'ip a; echo ""; ip route'
## 우측 하단 세션 (domons의 ip/route 정보 확인)
$ watch -d -n 1 'ip netns exec demons ip a; echo ""; ip netns exec demons ip route;'

최초 상태는 아래와 같습니다.

 

컨테이너와 호스트간에 veth 디바이스를 생성해주는 ptp plugin을 사용합니다.

https://www.cni.dev/plugins/current/main/ptp/

 

ptp 바이너리를 실행하면 version을 알 수 없다고 합니다. CNI는 단순히 필요한 정보를 환경 변수로, 그리고 spec을 표준 입력으로 전달합니다. CNI_COMMAND로 VERSION을 넣고 다시 바이너리를 실행하면 제공하는 버전을 알려줍니다.

root@jumpVM:~/plugins/bin# ./ptp
CNI ptp plugin version unknown
CNI protocol versions supported: 0.1.0, 0.2.0, 0.3.0, 0.3.1, 0.4.0, 1.0.0, 1.1.0
root@jumpVM:~/plugins/bin# CNI_COMMAND=VERSION ./ptp
{"cniVersion":"1.1.0","supportedVersions":["0.1.0","0.2.0","0.3.0","0.3.1","0.4.0","1.0.0","1.1.0"]}

 

아래와 같이 표준 입력으로 전달할 CNI 설정도 준비합니다. CNI plugin에는 IPAM과 컨테이너 네트워크를 구성하는 역할을 하는 plugin으로 나뉩니다. 여기에서는 ptp와 ipam으로 host-local을 사용했습니다.

{
    "cniVersion": "0.3.1",
    "name": "demonet",
    "type": "ptp", ## CNI binary
    "ipam": {
        "type": "host-local", ## CNI binary for IPAM
        "subnet": "192.168.0.0/24"
    }
}

 

이제 실제로 ADD 명령을 전달합니다. 하지만 여러가지 변수들이 지정되지 않아 에러가 발생하였습니다.

root@jumpVM:~/plugins/bin# CNI_COMMAND=ADD ./ptp < config
{
    "code": 4,
    "msg": "required env variables [CNI_CONTAINERID,CNI_NETNS,CNI_IFNAME,CNI_PATH] missing"
}

 

해당 변수들까지 환경 변수로 지정하고 명령을 다시 수행합니다.

$ CNI_COMMAND=ADD CNI_CONTAINERID=1234 CNI_NETNS=/var/run/netns/demons CNI_IFNAME=domoeth0 CNI_PATH=/root/plugins/bin ./ptp < config

 

명령 수행 결과로 생성된 interface에 대한 정보와 IPAM에서 전달된 ip 정보를 확인할 수 있으며, 우측 세션을 보면 새로운 veth pair와 라우팅이 추가되었음을 알 수 있습니다.

 

마지막으로 DEL 명령으로 테스트 구성을 삭제합니다.

CNI_COMMAND=DEL CNI_CONTAINERID=1234 CNI_NETNS=/var/run/netns/demons CNI_IFNAME=domoeth0 CNI_PATH=/root/plugins/bin ./ptp < config  

이렇게 샘플 CNI plugin을 실행해 봄으로서 앞서 설명한 과정을 이해할 수 있으며, Container Runtime이 어떤 방식으로 CNI plugin을 호출하는지를 이해할 수 있습니다. 실제로 ADD, DEL 과 같은 요청을 합니다.

+ Recent posts