CKA를 준비해보자 31일차 - Container Networking Interface (CNI)

0

CKA

목록 보기
31/43

Container Networking Interface (CNI)

이전까지 linux에서 network namespace를 만들고, 설정하는 방법과 docker에서 network namespace를 만들어 container에 할당하고 network를 구성하는 방법에 대해서 배웠다.

docker container가 만들어지고 network를 설정하는 부분에 대한 정리를 해보면 다음과 같다.

  1. container가 만들어지면 network namespace를 생성한다.
  2. 다음으로 bridge network를 생성하고 virtual ethernet interface를 만든다.
  3. bridge network가 있다면 veth pair를 container와 bridge를 생성한다.
  4. veth를 namespace에 연결한다.
  5. 다른 쪽 veth를 namespace로 연결한다.
  6. network namespace의 veth에 IP를 할당해준다.
  7. interface를 setup시켜준다.
  8. 외부와의 통신을 위해서 NAT를 통해 IP Masquerade를 해준다.

이러한 동작은 docker도 rkt, mesos, kubernetes 모두 똑같이 동작해야하는 부분이다.

그렇다면 누군가 이 부분에 대해서 표준을 만들어줬으면 좋을 것이다. 또한, 누군가 표준대로 구현한 plugin을 구현하여 container runtime이 container network를 구성할 때, plugin을 사용해 공통화된 방법으로 container network를 구성하고 싶다.

사용하는 측에서는 표준 인터페이스를 기준으로 사용하기만 하면 되기 때문이다.

이것이 바로 'CNI(Container Network Interface)'이다. CNI는 container runtime이 container network를 어떻게 구성하는 가에 대한 표준 집합이다. 즉, 프로그램을 어떻게 정의해야하는 지에 대한 스펙인 것이다.

이렇게 CNI를 준수하여 container network를 구성해주는 tool을 하나의 CNI plugin이라고 한다.

CNI는 CNI plugin들이 어떻게 개발되어야 하는 지 정의하고, container runtime이 어떻게 CNI plugin을 사용해서 container network를 구성해야하는 지 정의한다.

CNI는 다음과 같은 spec들이 있기 때문에 CNI plugin들이 이를 반드시 지켜야한다.

  1. container runtime은 container 생성 시에 network namespace를 만들어야 한다.
  2. container에 부착될 network를 식별해야한다.
  3. container가 Add될 때, container runtime은 Network plugin의 'add'를 호출해야한다.
  4. container가 del될 때, container runtime은 Network plugin의 'del'를 호출해야한다.
  5. network configuration은 JSON foramt이다.

CNI plugin은 또한, 이러한 add, del, check와 같은 command line을 제공하고 지정된 argument들을 제공해야하며, container id, network ns 등을 파라미터로 받아야 한다.

CNI plugin은 pod에 IP를 할당하는 방법, container에 필요한 routes를 설정해 같은 network 내에 있는 다른 container에 접근할 수 있도록 하는 방법들을 구현해놓아야 한다.

container runtime과 plugin이 해당 spec들을 같이지켜 같이 동작하는 것이다.

CNI에는 여러 plugin들이 지원되는데, Bridge, VLAN, IP VLAN, MAC VLAN, Window VLAN 등이 있다.

많이 사용되는 것으로 flannel, calico, cilium 등이 있다.

문제는 docker는 CNI를 지키지 않고 'CNM(container network model)'을 지킨다. 이는 또 다른 표준으로 CNI와 유사하지만 컨테이너 네트워킹 과제를 해결하는 것을 목표로 하고 있다.

때문에 CNI와 docker가 통합되지 않기 때문에 docker를 CNI plugin으로 쓸수가 없다.

kubernetes의 경우는 docker container를 생성할 때, none network로 생성한다. 그런 다음 CNI plugin을 호출하여 구성하도록 하는데, CNI의 구성은 누가 담당하고 호출하는 지에 대해서 알아보도록 하자.

Cluster Networking

kubernetes는 여러 node들로 이루어져 있는데, 각 node마다 하나씩 interface를 가지고 있다.

192.168   192.168   192.168
.1.10     1.11      1.12
---M---   ---W---   ---W---
|     |   |     |   |     |
|     |   |     |   |     |
--eth0-   -eth0--   --eth0-
   |        |          |
  --------Network--------
  |     192.168.1.0     |
  -----------------------

각 node들은 각자의 IP를 독자적으로 갖고 있고, network interface를 통해서 network에 접속해있을 수 있다.

이제 각각의 node들은 자신의 node에 배포된 service들을 port로 열어두어야 하는데, 가령 kube-apiserver6443으로 열려야한다.

이를 정리하면 다음과 같다.

  • master node
  1. kube-apiserver: 6443
  2. kubelet: 10250
  3. kube-scheduler: 10259
  4. kube-controller-manager: 10257
  5. etcd: 2379

참고로 kubelet은 Master node에서도 사용이 가능하다.

  • worker node
  1. services: 30000~32767
  2. kubelet: 10250

service는 pod의 service로 30000~32767까지 port를 사용할 수 있다. 필요한 경우 port range를 더 열어둘 수도 있다.

이러한 kubernetes component들 중 하나라도 제대로 port가 열려있지 않는다면, kubernetes cluster 자체가 제대로 동작하지 않을 수 있다. 따라서 다음의 명령어들을 외워두도록 하자.

ifconfig
ip link
ip addr
ip addr add 192.168.1.10/24 dev eth0
ip route
ip route add 192.168.1.0/24 via 192.168.2.1
cat /proc/sys/net/ipv4/ip_forward
arp
netstat -plnt
route
  • 특정 node에 할당된 IP를 보는 방법은 다음과 같다.
kubectl get nodes -o wide

NAME           STATUS   ROLES           AGE     VERSION   INTERNAL-IP    EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION   CONTAINER-RUNTIME
controlplane   Ready    control-plane   7m8s    v1.30.0   192.33.114.3   <none>        Ubuntu 22.04.4 LTS   5.4.0-1106-gcp   containerd://1.6.26
node01         Ready    <none>          6m19s   v1.30.0   192.33.114.6   <none>        Ubuntu 22.04.4 LTS   5.4.0-1106-gcp   containerd://1.6.26

node01에서의 internal IP는 192.33.114.6이다.

  • 특정 node의 mac 주소를 알아보는 방법은 다음과 같다. 가령 node01의 경우 먼저 ssh에 들어가면 된다.
ssh 192.33.114.6
ip link show eth0

10891: eth0@if10892: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP mode DEFAULT group default 
    link/ether 02:42:c0:21:72:06 brd ff:ff:ff:ff:ff:ff link-netnsid 0

02:42:c0:21:72:06이다.

  • containerd에 의해서 생성된 interface/bridge가 무엇인가??
ip link
...
3: cni0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1400 qdisc noqueue state UP mode DEFAULT group default qlen 1000
...

cni0이다.

  • cni0의 상태는?
ip link
3: cni0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1400 qdisc noqueue state UP mode DEFAULT group default qlen 1000

UP이다.

  • google에 검색할 때 어떻게 route되는가?
ip route

default via 172.25.0.1 dev eth1 
10.244.0.0/24 dev cni0 proto kernel scope link src 10.244.0.1 
10.244.1.0/24 via 10.244.1.0 dev flannel.1 onlink 
172.25.0.0/24 dev eth1 proto kernel scope link src 172.25.0.88 
192.33.114.0/24 dev eth0 proto kernel scope link src 192.33.114.3 

google과 같은 public network는 default DNS를 지난다. default172.25.0.1을 통해서 라우팅된다.

  • kube-scheduler는 controlplane에서 어떤 port로 열려있는가?
netstat -plnt | grep kube-scheduler
tcp        0      0 127.0.0.1:10259         0.0.0.0:*               LISTEN      3765/kube-scheduler 

10259이다.

  • etcd는 두 개의 port를 열고 있는데 어떤 port가 더 많은 connection을 가지고 있는가?
netstat -anp | grep etcd
N      3815/etcd           
tcp        0      0 127.0.0.1:2379          0.0.0.0:*               LISTEN      3815/etcd           
tcp        0      0 192.33.114.3:2380       0.0.0.0:*               LISTEN      3815/etcd           
tcp        0      0 127.0.0.1:2381          0.0.0.0:*               LISTEN      3815/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:53136         ESTABLISHED 3815/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:53302         ESTA
...
BLISHED 3815/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:53458         ESTA

2379 port가 많이 열려있는 것을 볼 수 있다.

왜냐하면 2379는 모든 control plane component들이 연결되는 port이기 때문이다. 반면에 2380이는 etcd에 peer-to-peer로 연결되기 때문에 얼마 없다.

0개의 댓글