Kubernetes/네트워크

CNI 리서치 : containerd 로 생성한 conatiner에 CNI 사용

이쿠우우 2024. 8. 6. 21:29
반응형

 

containerd 로 생성한 conatiner에 CNI 사용

CNI 리서치: CNI 개념  글의 실습을 통해서 CNI가 어떻게 동작하는지 파악 완료함.
이제 Container를 생성한 경우 CNI가 어떻게 적용되는지 확인해보도록 함.
 

test 과정

kubernetes에서 pod 생성 시 container에 IP를 할당하는 과정을 최대한 비슷하게 진행해봄.
test는 nerdctl tool을 통해 containerd 로 pause container 생성 후
CNI로 network interface를 할당해봄.
이후 nginx container를 배포하는데 pause container의 network namespace를 공유하도록 해서
pause container IP로 접근 시 nginx에 정상접근 되는지 확인함
 

test 환경

containerd version : 1.6.10
nerdctl version : 1.0.0

 

containerd에 CNI 설정 : cat /etc/containerd/config.toml
disabled_plugins = ["cri"]
 
 

CNI Plugin 배포

mkdir -p /opt/cni/bin
cd /opt/cni/bin
curl -O -L https://github.com/containernetworking/plugins/releases/download/v1.1.1/cni-plugins-linux-amd64-v1.1.1.tgz
tar xvf cni-plugins-linux-amd64-v1.1.1.tgz
 

CNI 구성 파일 생성

cat > /root/cni/2-iksoontest.conf <<"EOF"
{
  "cniVersion": "0.4.0",
  "name": "iksoonnet",
  "type": "bridge",
  "bridge": "iksoon0",
  "isGateway": true,
  "ipMasq": true,
  "ipam": {
    "type": "host-local",
    "subnet": "10.0.10.0/24",
    "routes": [
        { "dst": "0.0.0.0/0" },
        { "dst": "1.1.1.1/32", "gw":"10.0.10.1"}
    ]
  }
}
EOF
 

container 생성

nerdctl run --name $container_id -d --network none kubernetes/pause
nerdctl 버그로 --network none 옵션 사용 안함
container_id=pause_demo
nerdctl run --name $container_id -d  kubernetes/pause
nerdctl ps

 

 

 

 

network namespace 확인

network namespace 를 확인해야함
여기서 docker의 경우 아래의 명령으로 확인 가능하지만
container_netns=$(docker inspect ${container_id} --format "{{ .NetworkSettings.SandboxKey }}")
containerd의 경우 inspect명령으로 network namespace key값이 나오지 않음
수동으로 확인 필요
target_pid=$(nerdctl inspect -f '{{.State.Pid}}' ${container_id})
cd /proc/$target_pid/ns
해당 경로의 "net" 이 linux network namespace를 뜻함.

 

[nerdctl 없이 container의 process id 확인하는 법]
container는 결국 low level container runtime이 생성함.
containerd의 경우 runc를 사용함으로 runc directory에서 확인 가능
/run/containerd/runc/k8s.io
해당 경로에서 container id directory로 들어가서
"state.json" 파일 내용을 확인해 보면 아래와 같이 namespace_paths 항목에서 확인 가능.
"namespace_paths":{"NEWIPC":"/proc/10463/ns/ipc","NEWNET":"/proc/10463/ns/net","NEWNS":"/proc/10463/ns/mnt","NEWPID":"/proc/10463/ns/pid","NEWUSER":"/proc/10463/ns/user","NEWUTS":"/proc/10463/ns/uts"},

 

 

 

network namespace 파일 링크 생성

위에서 생성한 container의 network namespace는
ip netns ls 명령으로 확인이 불가능함.
linux namespace 를 관리하는 "ip netns ls" 명령은 default로
"/var/run/netns" directory에서 네트워크 네임스페이스 파일을 찾음.
[해당 정보 참고]
ex) CNI의 경우 "CNI_NETNS" 환경변수에 network namespace 경로를 설정함.
 
ip netns 명령으로 network namespace를 확인하기 위해
아래 명령어로 network namespace를  bind mount함
 
[명령어]
mkdir -p /var/run/netns
touch /var/run/netns/$container_id
mount -o bind /proc/$target_pid/ns/net /var/run/netns/$container_id
해당 명령을 수행하면 ip netns ls 명령으로 network namespace를 확인할 수 있음.

 

 
 

생성한 pause container의 network interface 확인

생성한 container의 network interface 상태를 확인해보면
아래와 같이 loopback interface와 
nerdctl로 container 생성 시 default로 연결되는 eth0 interface만 존재하는 것을 확인할 수 있음.
ip netns exec $container_id ip addr show

[nerdctl 관련 cni 특이 사항]
nerdctl 로 container 생성 시 " --network none " 옵션을 사용하지 않으면
nerdctl  tool 자체의 default CNI config 가 적용되어서
host에 nerdctl0 birdge interface가 추가되고
생성되는 container에도 해당 bridge의 network interface가 추가됨.
 
 
 

생성한 pause container에 CNI를 사용하여 network interface 추가

cd /opt/cni/bin
CNI_CONTAINERID=$container_id CNI_IFNAME=iksoonifname0 CNI_COMMAND=ADD CNI_NETNS=/var/run/netns/$container_id CNI_PATH=/opt/cni/bin ./bridge < /root/cni/2-iksoontest.conf

[결과 리턴 값 확인]
CNI로 network interface 할당 성공 시 JSON 객체가 반환 됨.

 

pause container의 network namespace interface 확인

ip netns exec pause_demo ip addr show
pause container에 iksoon0 bridge와 연결되는 iksoonifname0 interface가 생성된 것을 확인할 수 있음.

host에는 iksoon0 bridge 가 생성됨

 

 
 

nginx container를 pause container와 network namespace를 공유하도록 생성

nerdctl run --name nginx_test  -d  --network container:$container_id nginx

 

pause container의 IP로 접속하여 nginx page 확인

 
/var/lib/cni/networks/iksoonnet/
경로로 이동해보면 pause_test container에 할당된 ip 정보가 나옴

실제 생성된 pause, nginx container의 network namespace를 확인해보면 아래와 같이 동일함을 알 수 있음

해당 10.0.10.5 로 curl을 해보면
nginx container 접근 가능

 

test 결과 정리

이와 같은 과정으로 CNI를 통해 Container의 network namespace에 network interface를 할당함.
 
 

test 진행 사항 제거 script

container_id=pause_demo
nerdctl stop $container_id
nerdctl rm $container_id
nerdctl stop nginx_test
nerdctl rm nginx_test
cd /var/run/netns/
umount pause_demo
rm -rf pause_demo
cd /opt/cni/bin
CNI_CONTAINERID=$container_id CNI_IFNAME=iksoonifname0 CNI_COMMAND=DEL CNI_NETNS=/var/run/netns/$container_id CNI_PATH=/opt/cni/bin ./bridge < /root/cni/2-iksoontest.conf
ip link delete iksoon0

 

 


 

 

test 진행중 발견한 error 정리

 

[에러 1]
nerdctl run --name nginx_test  -d  --network container:$container_id nginx
FATA[0000] needs CNI plugin &{"bridge" "nerdctl0" %!q(bool=true) %!q(bool=false) %!q(bool=false) %!q(bool=true) '\x00' %!q(bool=true) %!q(bool=false) '\x00' map["ranges":[[map["gateway":"10.4.0.1" "subnet":"10.4.0.0/24"]]] "routes":[map["dst":"0.0.0.0/0"]] "type":"host-local"]} to be installed in CNI_PATH ("/opt/cni/bin"), see https://github.com/containernetworking/plugins/releases: exec: "/opt/cni/bin/bridge": stat /opt/cni/bin/bridge: no such file or directory

--network 옵션으로 기존 container의 network namespace를 공유하려고 햇는데
위 error가 발생함

 

containerd의 경우 아래 설정을 통해 CNI가 설정됨.
/etc/containerd/config.toml
[plugins."io.containerd.grpc.v1.cri".cni]
      bin_dir = "/opt/cni/bin"
      conf_dir = "/etc/cni/net.d"
      max_conf_num = 1
      conf_template = ""
상위 설정이 없다면 CNI plugin 경로가 "/opt/cni/bin/"으로 고정되어있음.

 

(해결책)
CNI Plugin을 /opt/cni/bin directory로 이동.
mkdir -p /opt/cni/bin
mv ~/cni/*  /opt/cni/bin/
nerdctl도 network는 CNI plugin을 사용함을 알 수 있음.

 

[에러 2]
nerdctl run --name nginx_test  -d  --network container:$container_id nginx

대체 이건 어떻게 해결해야하는가....
[관련 PR]
PR을 보아하니 v0.22.1 version 부터 --network container 옵션이 제공됨.
코드를 분석하니 --network 옵션도 결국 /proc/$target_pid/ns/net 를 공유하도록 되어있음.
test 진행은 nerdctl version이 v0.19.0이여서 상위 에러가 발생함.
 
버전을 최신으로 변경하고
nerdctl run --name nginx_test  -d  --network container:$container_id nginx
진행하니 정상적으로 container 생성됨.

 
[에러 3]
nginx container 생성 시 기존에 생성된 pause container의 network namespace를 공유하도록 함
사용 명령어는 아래와 같음
nerdctl run --name nginx_test  -d  --network container:$container_id nginx
결과는 아래와 같은 오류가 발생함
FATA[0000] failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: error during container init: error mounting "/var/lib/nerdctl/1935db59/containers/k8s.io/c6dd7d7468f8273d9e705c27d4842fb68621c1573784f9ff4822fc0c9ab9b6d7/resolv.conf" to rootfs at "/etc/resolv.conf": stat /var/lib/nerdctl/1935db59/containers/k8s.io/c6dd7d7468f8273d9e705c27d4842fb68621c1573784f9ff4822fc0c9ab9b6d7/resolv.conf: no such file or directory: unknown

nerdctl 의 경우 --network=none 옵션 사용 시 생성된 container 정보의 /etc/resolv.conf 파일을 참고하는데
network none 설정으로 생성된 container의 경우 해당 설정이 없음.
그래서 network container 설정으로 network namespace 공유를 못함.
이건 버그로 예상됨. nerdctl 의 --network 설정이 2022년도 7월에 merge된거라 아직 test가 많이 필요해보임
해당 오류 때문에 test는 --network none 설정 없이 진행함.

 

 


참고 
nerdctl cni
calico cni 모니터링 코드 참고
반응형