NetBird 라우팅 피어를 쿠버네티스에 — hostNetwork 가 필요한 이유
VPC 안의 관리형 DB 에 로컬에서 바로 붙을 수 있게 NetBird 라우팅 피어를 쿠버네티스(NKS) 클러스터에 올렸습니다. 그동안은 SSL VPN 이 유일한 길이었고, 다른 환경은 이미 NetBird 로 같은 일을 하고 있었습니다.
NetBird 는 WireGuard 로 오버레이 네트워크를 만들어 주는 도구입니다. 피어 하나를 라우팅 피어로 지정하면 그 뒤에 있는 사설 대역까지 오버레이로 들어갈 수 있고, 이 라우팅 피어를 클러스터에 Deployment 로 띄웠습니다.
먼저 확인한 것 — 파드에서 어디까지 나가는지
라우팅 피어는 NetBird 관리 서버에 붙고 이미지도 받아야 해서, 클러스터 파드에서 바깥으로 어디까지 나가는지가 먼저였습니다.
NetBird 관리 서버 200 NAT 를 거쳐 닿음
Docker Hub 401 이미지 pull 가능
NetBird 패키지 저장소 차단
패키지 저장소가 막혀 있어 컨테이너 안에서 netbird 를 설치하는 방식은 쓸 수 없고, 미리 빌드된 이미지를 그대로 써야 했습니다. 클라이언트는 관리 서버와 같이 쓴 이력이 있는 0.77.1 로 고정했습니다.
hostNetwork 가 필요한 이유 — 응답이 돌아올 길
라우팅 피어는 오버레이에서 들어온 트래픽을 VPC 로 내보내면서 출발지를 자기 주소로 바꿉니다 (masquerade). 파드 네트워크로 띄우면 그 자기 주소가 파드 IP 가 됩니다.
파드 IP 는 NKS 기본값인 198.18.0.0/16 에서 나오고, 이 대역은 VPC 라우팅 테이블에 없습니다. 요청은 나가지만 관리형 DB 쪽에서 그 출발지로 돌려보낼 경로가 없어 응답이 돌아오지 않고, 어디에도
로그가 남지 않습니다.
hostNetwork 로 띄우면 출발지가 노드 IP 가 되고 노드는 같은 VPC 안에 있으니 회신 경로가 처음부터 있습니다.
파드 네트워크 출발지 = 파드 IP(198.18.x) VPC 라우팅에 없음 응답 못 돌아옴
hostNetwork 출발지 = 노드 IP(VPC 대역) 같은 VPC 응답 돌아옴
NCP 의 ACG 는 AWS 의 보안 그룹에 해당하고, 관리형 DB·캐시의 ACG 에는 이미 VPC 대역 전체가 인바운드로 열려 있었습니다. 출발지가 노드 IP 면 그 규칙에 그대로 걸려 ACG 를 하나도 고칠 필요가 없었습니다. 파드 IP 였다면 ACG 마다 파드 대역을 따로 열어야 했으니, 방화벽 쪽에서도 hostNetwork 가 나은 선택이었습니다.
Deployment 에서 이 판단이 들어간 자리는 이렇습니다.
hostNetwork: true # 출발지를 노드 IP 로
dnsPolicy: ClusterFirstWithHostNet
strategy: { type: Recreate } # wt0/TUN 인터페이스는 노드에 하나만
capabilities: [NET_ADMIN, NET_RAW, SYS_ADMIN]
volumes: /dev/net/tun (hostPath, CharDevice) + emptyDir
hostNetwork 라 wt0 인터페이스가 노드에 바로 생기고 같은 노드에 둘이 뜨면 서로 부딪히니 롤링
업데이트 대신 Recreate 로 둡니다.
광고하지 않는 대역 — 파드와 서비스 CIDR
라우팅 피어는 dev 와 prd 두 클러스터에 각각 올렸고, 오버레이에 광고하는 건 각 VPC 대역뿐입니다. 두
클러스터 모두 NKS 기본값을 써서 파드 대역(198.18.0.0/16)과 서비스 대역(198.19.128.0/17)이 똑같기
때문에, 이걸 둘 다 광고하면 198.18.0.5 가 어느 클러스터의 파드인지 오버레이가 정할 수 없습니다.
라우트는 피어가 아니라 Peer Group 에
라우트를 라우팅 피어 하나에 직접 걸면, 파드가 재시작할 때마다 새 피어로 등록되면서 peer ID 가 바뀌어 경로가 끊깁니다. 그래서 라우트는 Peer Group 에 걸고, setup key 에 auto-group 을 지정해 새로 등록된 피어가 자동으로 그 그룹에 들어가게 했습니다. 파드가 갈려도 그룹 소속은 유지되니 경로가 안 끊기고 상태를 남길 PVC 도 필요 없습니다.
setup key 는 Reusable 에 Ephemeral 로 만들었습니다. 한 번만 쓰는 키는 먼저 뜬 클러스터가 소진하면 나머지가 등록을 못 하고, 노드가 교체되거나 파드가 재스케줄돼도 다시 등록해야 하기 때문입니다.
setup key is invalid 의 정체 — 피어 이름 충돌
등록이 setup key is invalid 로 계속 튕겼지만 키는 멀쩡했고, 16일 전에 남겨 둔 피어가 같은 이름
(NB_HOSTNAME)을 쥐고 있던 게 원인이었습니다. 그 피어를 지우자 재시도 루프가 바로 성공했으니, 이
메시지가 나오면 키를 다시 발급하기 전에 Peers 목록에서 같은 이름부터 찾아봅니다.
같은 충돌이 다시 안 나게 NB_HOSTNAME 을 고정 문자열 대신 노드 이름에서 받도록 바꿨습니다. 노드
이름은 클러스터마다 다르고 노드를 다시 만들면 바뀌어 겹칠 일이 없습니다.
env:
- name: NB_HOSTNAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
붙인 뒤 확인한 것과 남은 것
로컬에서 노드의 kubelet(10250)과 관리형 PostgreSQL(5432)로 붙어 보니 dev·prd 둘 다 연결됐고 로컬 라우팅 테이블에도 두 VPC 대역이 NetBird 인터페이스로 잡혔습니다.
라우팅 피어가 떠 있는 노드 자신은 오버레이에서 닿지 않는데, 정상 동작입니다. masquerade 는 피어가 다른 곳으로 넘겨 주는 트래픽에만 걸려서 목적지가 피어 자신이면 출발지가 오버레이 IP 그대로 도착합니다.
이 구조로 바꾸면 접근 제어가 ACG 에서 NetBird 정책으로 넘어갑니다. 오버레이로 들어온 트래픽은 VPC 안에서 출발한 것처럼 보여서 ACG 가 걸러 주지 않고 누가 어디까지 닿는지는 NetBird 정책 하나로 정해집니다. 라우팅 피어가 하나뿐인 클러스터는 그 노드가 죽으면 VPC 접근이 통째로 끊긴다는 것도 같이 알고 있어야 합니다.
댓글 0