콘텐츠로 이동
Study NoteCKA

확장 인터페이스 — CRI·CNI·CSI

결론부터
Kubernetes는 런타임·네트워크·스토리지 구현을 인터페이스로 연결하며, 실제 작업은 각 플러그인이 맡는다.

Pod 실행에는 컨테이너 생성, 네트워크 연결, 볼륨 연결이 필요하다. 각 작업을 어느 플러그인이 담당하는지 알면 문제를 진단할 위치도 보인다. 새 API 종류는 CRD에서 다룬다.

핵심 기능 중 상당수가 인터페이스만 정의되어 있고 구현은 밖에 있다.

Kubernetes 코어가 정의한 CRI · CNI · CSI · CRD 네 인터페이스와 각각의 구현체, 그리고 플러그인이 없으면 아무것도 안 된다는 경고
인터페이스무엇을 위임하나구현체 예
CRI (Container Runtime Interface)컨테이너 실행containerd, CRI-O
CNI (Container Network Interface)Pod 네트워크·IPCalico, Cilium, Flannel
CSI (Container Storage Interface)볼륨 생성·마운트EBS CSI, Ceph, local-path
CRD + 컨트롤러새로운 리소스 종류cert-manager, Prometheus Operator

이 구조 덕분에 같은 API로 온프렘과 클라우드가 모두 동작한다. 그리고 “플러그인이 없으면 아무것도 안 된다”는 트러블슈팅 포인트가 생긴다.

kubelet은 컨테이너를 직접 만들지 못한다 — 실행은 전부 런타임에 시킨다. 규약이 없으면 kubelet이 런타임마다 전용 연동 코드를 품어야 하고, 실제로 Docker 전용 코드(dockershim)를 그렇게 품고 있다가 부담이 커져 떼어냈다. 그 규약이 CRI다.

  • kubelet ↔ 런타임 사이의 gRPC 규약이다 — gRPC는 HTTP/2 기반의 원격 프로시저 호출(RPC) 프레임워크로, 여기선 “정해진 함수 목록”이라는 뜻만 알면 된다
  • 소켓 경로: unix:///run/containerd/containerd.sock
  • Docker는 v1.24에서 제거되었다 (dockershim 삭제). 지금은 containerd가 표준이고, Docker Engine을 계속 쓰려면 cri-dockerd 어댑터를 따로 설치한다

이 소켓 경로는 kubelet 설정 파일의 containerRuntimeEndpoint 필드에 있다. 노드에서 값을 확인하는 자리가 여기다 — 옛 자료가 가리키는 --container-runtime 플래그는 v1.27에서 제거됐다.

터미널 창
grep containerRuntimeEndpoint /var/lib/kubelet/config.yaml # 이 노드에 설정된 CRI 소켓
kubectl get nodes -o wide # CONTAINER-RUNTIME 열로도 확인

같은 뜻의 --container-runtime-endpoint 플래그는 설정 파일보다 우선하지만 공식 문서가 deprecated로 표시했다. 설정 파일을 먼저 보고, 값이 다르면 /var/lib/kubelet/kubeadm-flags.env에서 플래그 재정의를 찾는다.

터미널 창
# crictl 설정
sudo crictl config --set runtime-endpoint=unix:///run/containerd/containerd.sock
cat /etc/crictl.yaml
sudo crictl ps -a # 컨테이너
sudo crictl pods # Pod 샌드박스
sudo crictl images # 이미지
sudo crictl logs <container-id>
sudo crictl inspect <container-id>
sudo crictl rmi --prune # 안 쓰는 이미지 정리

Pod마다 IP를 주고 노드 사이를 잇는 방법은 환경마다 다르다 — 온프렘·클라우드· 베어메탈이 전부 다른 답을 쓴다. 그래서 코어는 “인터페이스를 만들고 IP를 붙여라”는 호출 규약만 정하고, 방법은 플러그인에 맡긴다. kubelet은 CRI로 런타임에게 Pod 샌드박스(Pod의 컨테이너들이 공유할 네트워크 네임스페이스 껍데기)를 만들라고 시킬 뿐이고, CNI 플러그인을 실제로 실행하는 쪽은 컨테이너 런타임이다. v1.24에서 kubelet의 cni-bin-dir·network-plugin 플래그가 제거되면서 CNI 관리는 kubelet의 일이 아니게 됐다. 그래서 CNI 설정과 바이너리 경로를 아는 주체도 런타임이다.

kubelet 의 샌드박스 생성 요청을 받은 컨테이너 런타임이 CNI 플러그인을 ADD 호출해 인터페이스 생성 · IP 할당 · 라우팅 3단계를 거치는 흐름과, CNI 가 고장나면 ContainerCreating 에서 멈추는 실패 경로

② 의 IP 할당은 IPAM(IP Address Management — 주소 할당 관리) 플러그인의 몫이다. 이렇게 깔린 Pod 네트워크 위에서 Service의 가상 IP가 어떻게 Pod IP로 번역되는지는 Service의 분업 절에서 본다.

모든 CNI가 지켜야 하는 규칙

  • 모든 Pod은 NAT 없이 서로 통신할 수 있다
  • 노드의 에이전트는 그 노드의 모든 Pod과 통신할 수 있다
  • Pod이 보는 자기 IP = 남이 보는 그 Pod의 IP

이 규칙은 모든 CNI가 지키지만 그 밖의 기능은 CNI마다 다르다. 여러 후보 중 하나를 골라야 한다면 요구 기능으로 가른다. 대표적인 기준이 NetworkPolicy 적용 여부이고, 지원 여부는 CNI별 표에 있다.

볼륨을 만들고 노드에 붙이는 방법은 스토리지 벤더마다 다르다. CSI가 없던 시절엔 벤더 코드가 Kubernetes 코어 안에 들어 있어서(in-tree 드라이버), 새 스토리지를 지원하려면 Kubernetes 릴리스를 기다려야 했다. CSI는 그 연동을 표준 규약으로 떼어내, 벤더가 드라이버만 따로 배포하면 되게 한다.

터미널 창
kubectl get csidrivers
kubectl get csinodes
kubectl get storageclass
kubectl get volumeattachments

CSI 드라이버는 보통 두 부분으로 배포된다.

Controller 플러그인 — Deployment

볼륨 생성·삭제·확장·스냅샷. 클러스터에 하나.

Node 플러그인 — DaemonSet

노드에서 attach·mount. 노드마다 하나.

두 플러그인 주변에는 sidecar 컨테이너들이 붙는다. 벤더는 “볼륨을 만들어라” 같은 실제 동작만 구현하고, API 서버를 지켜보다가 그 동작을 호출하는 쪽은 Kubernetes 프로젝트가 만든 공용 사이드카가 맡는다 — 벤더마다 같은 watch 코드를 다시 짜지 않게 하려는 분업이다. 각 사이드카가 무엇을 감시하는지만 알면 된다.

사이드카무엇을 보고 무엇을 시키나
external-provisionerPVC를 watch → 드라이버에 볼륨 생성·삭제 요청
external-attacherVolumeAttachment를 watch → 노드에 attach·detach 요청
external-resizerPVC의 용량 변경을 watch → 볼륨 확장 요청
external-snapshotterVolumeSnapshot을 watch → 스냅샷 생성
node-driver-registrar노드의 kubelet에 드라이버를 등록한다

이름을 외울 필요는 없다. 다만 PVC가 Pending일 때 어느 로그를 볼지를 이 표가 정해 준다 — 볼륨이 안 만들어지면 external-provisioner, 만들어졌는데 노드에 안 붙으면 external-attacher 쪽이다.

PVC 가 Pending 일 때 StorageClass 유무로 갈라져 프로비저너 Pod 과 로그를 확인하는 진단 순서

PVC가 Pending인데 StorageClass는 있다면 프로비저너 Pod이 살아 있는지부터 확인한다.

터미널 창
kubectl get pods -n kube-system | grep csi
kubectl logs -n kube-system <csi-controller-pod> -c csi-provisioner

그 밖의 확장 지점이름만 알면 된다

섹션 제목: “그 밖의 확장 지점”

CRI·CNI·CSI는 “코어가 아예 구현을 안 갖고 있는” 자리였다. 여기서 볼 것들은 그와 달리 코어가 동작은 하는데, 그 동작에 끼어들거나 덧붙이는 지점이다. 어디에 끼어드는지만 알아 두면 “왜 이 컴포넌트가 있지”라는 의문이 대부분 풀린다. 다섯 중 CKA에서 실제로 손을 대는 것은 admission(Admission)과 다중 스케줄러(스케줄링) 정도이고, 나머지는 이름과 역할만 알아 두면 충분하다 — 직접 구성하는 문제로 나오지는 않는 편이다.

kube-apiserver · kubelet · scheduler 에 각각 붙는 admission webhook, API aggregation, device plugin, scheduler extender 와 cloud controller manager 확장점
확장무엇
Admission webhook요청을 가로채 고치거나 거부 (Admission)
API Aggregation별도 API 서버를 /apis 아래에 붙인다 (metrics-server가 이 방식)
CRI/CNI/CSI이 장에서 본 것들
Device PluginGPU 등 특수 하드웨어를 노드 리소스로 노출
Scheduler Extender / 다중 스케줄러배치 로직 교체 (스케줄링)
Cloud Controller Manager클라우드별 노드·LB·라우트 관리
터미널 창
kubectl get apiservices | grep -v Local # 집계된 외부 API 서버들
# v1beta1.metrics.k8s.io kube-system/metrics-server True

APIService가 False면 그 API 전체가 죽는다 — kubectl top이 안 되는 원인 중 하나다 (오토스케일링).

  • Kubernetes는 런타임·네트워크·스토리지 구현을 인터페이스로 연결하며, 실제 작업은 각 플러그인이 맡는다.
  • CRI는 crictl, CNI는 설정·바이너리, CSI는 Controller·Node 플러그인이 주요 확인 지점이다.
  • API 리소스 종류를 추가하려면 CRD를 사용한다.