LabHub
배우기 러닝패스 코스

쿠버네티스 네트워크 — 진짜 클러스터에서 · 파드 주소는 누가 주는가 · 이론

API 서버는 파드 주소를 모른다

LabHub 에서 이어서 보기

한 줄 요약

쿠버네티스는 파드에 주소를 직접 주지 않는다. API 서버는 서비스 주소만 관리하고, 파드 주소는 노드 위의 네트워크 플러그인이 준다. 그래서 "파드가 안 뜬다" 와 "파드끼리 안 닿는다" 는 서로 다른 층의 고장이다.

왜 이게 필요했나

컨테이너 여러 개를 한 기계에 올리면 가장 먼저 부딪히는 것이 포트다. 세 팀이 각각 8080 을 쓰고 싶어 하면 누군가는 양보해야 하고, 양보한 쪽은 설정에 포트를 변수로 빼야 하고, 그러면 서비스 디스커버리도 포트를 알아야 한다. 공식 문서는 이 길을 "규모가 커지면 조율이 매우 어렵고 사용자가 통제할 수 없는 클러스터 수준의 문제를 만든다" 고 정리한다.

쿠버네티스는 그 길을 가지 않았다. 대신 파드마다 주소를 하나씩 준다. 파드 안에서는 컨테이너들이 localhost 로 만나고, 파드 밖에서는 주소로 만난다. 포트 조율이라는 문제 자체가 사라진다.

문제는 그 주소를 누가 어떻게 주느냐다. 클라우드마다 다르고, 온프레미스는 또 다르고, 어떤 곳은 라우팅으로 어떤 곳은 터널로 푼다. 쿠버네티스는 이 자리를 비워 두고 규격만 정했다. 그것이 CNI(Container Network Interface)다.

어떻게 동작하나

공식 문서의 표현은 분명하다 — "쿠버네티스 네트워크 모델을 구현하려면 CNI 플러그인이 필요하다." 그리고 플러그인은 CNI 규격 v0.4.0 이상과 호환되어야 하고, 프로젝트는 v1.0.0 호환을 권한다.

누가 부르는가. 1.24 이전에는 kubelet 이 cni-bin-dirnetwork-plugin 옵션으로 플러그인을 관리했다. 그 두 옵션은 1.24 에서 제거됐다. 지금 CNI 를 부르는 것은 kubelet 이 아니라 컨테이너 런타임(containerd, CRI-O)이다. 그래서 "CNI 설정을 바꿨는데 안 먹는다" 는 신고의 절반은 kubelet 을 재시작한 경우다.

어디를 읽는가. 설정은 기본값 /etc/cni/net.d, 바이너리는 기본값 /opt/cni/bin 이다. 설정 파일 하나에 플러그인을 여러 개 적는 형식이 conflist 이고, 앞에서부터 차례로 불린다.

왜 여러 개인가. 한 플러그인이 다 하지 않기 때문이다. 공식 문서가 직접 드는 예가 둘이다.

| 기능 | 맡는 플러그인 | 켜는 법 |
| --- | --- | --- |
| hostPort | portmap | capabilitiesportMappings: true |
| 대역 제한 | bandwidth | capabilitiesbandwidth: true 를 두고 파드에 kubernetes.io/ingress-bandwidth 어노테이션 |
| 루프백 lo | loopback | 런타임이 샌드박스마다 제공해야 한다 |

여기서 운영상 중요한 사실이 나온다. hostPort 를 적었는데 아무 일도 일어나지 않을 수 있다. 매니페스트는 API 검증을 통과하고, 파드는 Running 이 되고, 그런데 포트는 열리지 않는다. portmap 이 체인에 없으면 그렇다. 조용히 무시되는 종류의 고장이다.

주소는 IPAM 이 준다. bridge 같은 본 플러그인 안의 ipam 항목이 그 일을 맡는다. 가장 흔한 host-local 은 이름 그대로 그 노드 안에서만 주소를 관리한다. 노드끼리 서로 무엇을 나눠 줬는지 묻지 않는다. 그래서 노드마다 겹치지 않는 대역을 미리 잘라 줘야 하고, 그 대역이 노드 오브젝트의 spec.podCIDR 이다. 공식 예시가 "subnet": "usePodCidr" 로 적혀 있는 것이 그 뜻이다.

세 종류의 주소. 공식 문서는 클러스터가 파드·서비스·노드 세 종류의 주소를 겹치지 않게 배정해야 한다고 못박고, 각각을 정하는 주체까지 나눠 적는다 — 파드는 네트워크 플러그인, 서비스는 kube-apiserver, 노드는 kubelet 또는 클라우드 컨트롤러다. 세 주체가 서로 상의하지 않으므로 겹침을 막는 것은 설계자의 몫이다.

현장에서 만나는 모습

"노드 하나만 파드를 안 받습니다." 쿠버네티스는 이 상황을 테인트로 표현한다. 공식 문서의 내장 테인트 목록에 node.kubernetes.io/network-unavailable 이 "노드의 네트워크를 쓸 수 없다" 는 뜻으로 들어 있다. 그래서 신고는 "파드가 Pending" 으로 오지만 고칠 곳은 스케줄러가 아니라 그 노드의 플러그인 설정이다. 플러그인 설정 파일이 그 노드에만 없거나, 바이너리 디렉터리가 다르거나, 런타임이 다른 경로를 보고 있다.

"파드 IP 가 두 노드에서 같습니다." host-local 은 노드 밖을 모르기 때문에, 두 노드에 같은 대역을 주면 태연히 같은 주소를 내준다. 증상은 무작위한 연결 실패로 나타나서 원인을 찾기까지 오래 걸린다.

"대역을 잘못 잡았으니 바꾸겠습니다." 노드의 spec.podCIDR 은 한 번 값이 들어가면 다른 값으로 바꿀 수 없다. 클러스터 설계에서 되돌리기 가장 어려운 결정이 주소 대역이다.

공식 문서: [Network Plugins](https://kubernetes.io/docs/concepts/extend-kubernetes/compute-storage-net/network-plugins/) · [Cluster Networking](https://kubernetes.io/docs/concepts/cluster-administration/networking/)

다음 실습에서 할 것

노드 셋에 /24 씩 파드 대역을 못박고, 그 대역으로 담을 수 있는 수와 노드가 받아 주는 파드 수 중 어느 쪽이 먼저 걸리는지 계산한다. 그다음 bridge·portmap·bandwidth 세 플러그인을 이어 붙인 conflist 를 쓰고, hostPort 와 대역 어노테이션을 단 파드를 만들어 어느 플러그인이 그 일을 맡는지 적는다. 마지막에는 플러그인이 없는 노드를 흉내 내 NotReady 조건을 만들어 보고, 네 가지 대역 배치 중 어느 것이 겹치는지 계산으로 판정한다.