K8s Networking - 14. multi-cluster
클러스터 경계를 넘는 서비스 — 멀티클러스터 네트워킹의 두 접근
한 조직이 리전별로 클러스터 3개(서울/도쿄/버지니아)를 운영한다. 각 클러스터는 독립적이다 — 그런데 "서울 클러스터의 서비스가 도쿄 클러스터의 서비스를 불러야 한다"는 요구가 생겼다. 인터넷 경유 공개 API로만 부르자니 지연과 보안이 문제다. 멀티클러스터 네트워킹이 이 문제를 푼다 — 클러스터 경계를 넘어 Pod가 마치 같은 클러스터에 있는 것처럼 통신하게.
이 글이 푸는 것은: 클러스터 간 Pod/Service 통신을 어떻게 만드는가, Submariner와 Cilium Cluster Mesh 두 접근이 어떻게 다른가다. 02-k8s-networking 영역의 마지막 주제로, 규모가 커질 때의 네트워크 진화를 다룬다.
왜 클러스터를 여러 개로 나누나 — 그리고 경계가 만드는 문제
단일 거대 클러스터 대신 여러 클러스터를 두는 이유:
- 리전 분산: 지연 최소화(사용자 가까이).
- 장애 격리: 한 클러스터 장애가 전체로 번지지 않게.
- 규제/데이터 주권: 데이터를 특정 리전에 묶어야 할 때.
- 업그레이드 격리: 한 클러스터 업그레이드 중에 다른 클러스터가 서비스.
이런 이유로 클러스터를 나누면, 부작용이 생긴다 — Pod IP와 Service IP가 각 클러스터마다 별개다. 클러스터 A의 Pod(10.244.1.5)와 클러스터 B의 Pod(10.244.1.5)가 같은 IP를 쓸 수도 있다(각 클러스터가 독립적으로 IPAM을 하니까). 서로의 Service 이름도 모른다(각자 자기 CoreDNS만 앎). 이 경계가 cross-cluster 호출을 막는다. 멀티클러스터 네트워킹은 이 경계를 넘는 것이다.
두 접근 — 게이트웨이형 vs 오버레이형
경계를 넘는 두 가지 방식이 있다. 둘 다 "클러스터 간에 Pod/Service가 통신하게"라는 같은 목표지만, 경로가 다르다.
flowchart TD
subgraph GW["게이트웨이형 (Submariner)"]
C1A["클러스터 A"] --> GA["게이트웨이 노드"]
GA <-. 터널 .-> GB["게이트웨이 노드"]
GB --> C1B["클러스터 B"]
end
subgraph OV["오버레이형 (Cilium Cluster Mesh)"]
C2A["클러스터 A Pod"] -. 직접 eBPF 라우팅 .-> C2B["클러스터 B Pod"]
end
- Submariner: 게이트웨이 노드 쌍 간 터널(IPsec/VXLAN). 클러스터 간 트래픽이 게이트웨이를 경유. CNI 독립적 — 어떤 CNI를 쓰든 동작.
- Cilium Cluster Mesh: Cilium의 eBPF 데이터플레인을 클러스터 간로 확장. 각 클러스터의 Cilium이 서로의 Pod/Service 정보를 공유해 직접 라우팅. Cilium 전용.
Submariner — 게이트웨이 터널 + MultiCluster DNS
Submariner의 핵심 동작 세 가지: (Submariner docs)
- 게이트웨이 노드: 각 클러스터에서 지정된 노드들이 서로 터널(IPsec)로 연결된다.
- 라우팅: 클러스터 A의 Pod가 클러스터 B의 Pod IP 대역으로 보내면, 게이트웨이가 터널로 넘긴다.
- MultiCluster DNS:
service.clusterB.svc.clusterset.local같은 이름으로 클러스터 간 Service를 해석한다.
# 개념: 클러스터 간 Service 호출
kubectl exec <pod-in-A> -- curl http://backend.clusterB.svc.clusterset.local
Submariner가 DNS와 라우팅을 연결해, 클러스터 A의 Pod가 클러스터 B의 Service 이름으로 접근하게 한다.
Submariner는 게이트웨이 노드가 단일 지점이 될 수 있어 고가용성(게이트웨이 다중화) 설정이 중요하다. 그리고 터널로 인한 캡슐화 오버헤드가 있다.
Cilium Cluster Mesh — eBPF 데이터플레인 확장
Cilium Cluster Mesh는 05장의 eBPF 데이터플레인을 클러스터 간로 확장한다: (Cilium Cluster Mesh docs)
- 각 클러스터의 Cilium 에이전트가 다른 클러스터의 etcd/apiserver를 읽기로 연결(클러스터 간 Pod/Service 정보 동기화).
- eBPF가 클러스터 간 라우팅을 처리 — 게이트웨이 경유 없이 직접(또는 캡슐화).
flowchart LR
CA["Cilium(A)<br/>cluster A etcd 읽기"] -. watch .-> ETCD_B["클러스터 B etcd"]
CB["Cilium(B)"] -. watch .-> ETCD_A["클러스터 A etcd"]
PA["Pod A"] -. eBPF 라우팅 .-> PB["Pod B"]
이점: Cilium을 이미 CNI로 쓰면 자연스러운 확장이다. Hubble 가시성도 클러스터 간로 확장된다. 단점: Cilium 데이터플레인 전용(다른 CNI 클러스터와는 안 됨).
두 접근 비교 — CNI 독립이 갈림
| Submariner | Cilium Cluster Mesh | |
|---|---|---|
| 구조 | 게이트웨이 터널 | eBPF 직접 라우팅 |
| CNI 독립 | O(다른 CNI와도) | X(Cilium 클러스터끼리) |
| 데이터플레인 | 커널/IPsec | eBPF |
| 가시성 | 별도 도구 | Hubble 통합 |
| 도입 | 게이트웨이 설정 | Cilium 확장 |
선택 기준이 명확하다: 이기종 CNI 클러스터 연결(Calico + Cilium 등) → Submariner. 이미 Cilium 생태계 → Cluster Mesh.
서비스 디스커버리와 글로벌 서비스 — 어느 클러스터의 Service인가
멀티클러스터에선 "어느 클러스터의 Service를 부를까"가 새로운 문제다. 단일 클러스터의 svc.cluster.local이 멀티클러스터에선 부족하다 — 어느 클러스터인지 알 수 없으니. MultiCluster Services API(MCS)가 clusterset.local 이름 체계를 제공한다: (MCS API)
backend.clusterA.svc.clusterset.local # 클러스터 A의 backend 명시
backend.default.svc.clusterset.local # "가장 가까운" backend (글로벌)
이 이름 체계가 "어느 클러스터"를 명시하거나, 글로벌 이름으로 "가장 가까운"을 자동 선택. Submariner/Cilium이 이 MCS 이름을 해석한다.
글로벌 서비스 패턴 — 로컬 우선과 부하 분산
같은 서비스가 여러 클러스터에 있을 때 트래픽을 어떻게 보낼까. 두 패턴:
| 패턴 | 동작 | 용도 |
|---|---|---|
| 로컬 우선(active-active) | 같은 클러스터의 인스턴스 우선, 장애 시 원격 클러스터로 | 지연 최소화, 정상 시 로컬 유지 |
| 부하 분산 | 여러 클러스터에 트래픽 분산 | 용량 확장, 한 클러스터 부하 분담 |
# Cilium: 글로벌 서비스 (여러 클러스터에 같은 이름, 로컬 우선)
metadata:
annotations:
service.cilium.io/global: "true"
service.cilium.io/shared: "true"
flowchart TD
C1A["클러스터 A 클라이언트"] --> SVC["글로벌 서비스"]
SVC -->|"로컬 우선"| A1["클러스터 A 인스턴스 (정상 시)"]
SVC -. "A 장애 시" .-> B1["클러스터 B 인스턴스 (failover)"]
로컬 우선이 더 흔하다 — 클러스터 간 지연(리전 간 수십~수백 ms)을 피하기 위해 정상 시 로컬 클러스터 안에서 처리. 장애 시에만 원격으로 failover. 이것이 "지연 최소화 + 가용성"의 균형이다.
failover의 정확한 동작 — 언제 원격으로 넘어가나
로컬 우선에서 한 클러스터의 서비스가 죽으면:
- 로컬 EndpointSlice가 비어간다(헬스체크 실패한 Pod 제거).
- 트래픽이 남은 로컬 Pod로. 전부 죽으면 → 원격 클러스터로.
- 복구되면 다시 로컬 우선으로 복귀.
이 failover의 속도는 헬스체크 주기에 좌우된다. 너무 느린 헬스체크면 장애 인지가 늦어 downtime이 길어지고, 너무 빠르면 일시적 흔들림에 과민 반응. 헬스체크 튜닝이 멀티클러스터 가용성의 관건.
멀티클러스터의 진짜 비용 — 네트워크 연결은 시작일 뿐
네트워크 연결(터널/DNS)이 해결돼도, 멀티클러스터의 진짜 비용은 운영에 있다:
- 신뢰/인증: 클러스터 간 mTLS/인증서 신뢰 체인 구축.
- 두 apiserver: 인증/권한/감사를 각각 관리. 또는 페더레이션.
- 모니터링 통합: 두 클러스터의 메트릭/로그를 한곳에서? 또는 분리?
- 버전 관리: 두 클러스터가 다른 K8s 버전이면 — 앱 매니페스트 호환성.
- 정책 일관성: NetworkPolicy/메시 정책을 클러스터 경계 넘어 일관되게.
- 장애 대응: 한 클러스터 장애 시 failover가 자동으로? 사람이 개입?
이것이 "네트워크만 연결하면 멀티클러스터 완성"이 거짓인 이유다. 조직·운영 모델이 따라와야 한다. 특히 failover를 자동으로 신뢰하려면 충분한 테스트가 필요 — "장애 시 자동 failover된다"고 가정하다가 처음 장애에 작동 안 해서 큰 피해를 보는 사례가 흔하다.
Cluster API — 클러스터 자체의 라이프사이클 관리
멀티클러스터의 또 다른 축: 네트워크 연결이 아니라 클러스터 자체의 생성/삭제/업그레이드를 관리. Cluster API(CAPI)가 Kubernetes 클러스터를 Kubernetes 객체로 다룬다:
- "클러스터를 하나 만들어라"를 management 클러스터에 선언 → CAPI가 VM 생성 → kubeadm → 워커 노드까지 자동.
- 버전 업그레이드도 객체 수정 → CAPI가 노드를 하나씩 새 버전으로 교체.
이것이 멀티클러스터의 운영 자동화 층이다. 네트워크(Submariner/Cilium)가 통신을, CAPI가 라이프사이클을. 둘이 합쳐야 "수십 개 클러스터를 사람이 일일이 안 세팅하고 관리"가 가능하다. 대규모 멀티클러스터 운영의 표준.
멀티클러스터 vs 단일 대형 클러스터 — 언제 어느 쪽이
"하나의 거대 클러스터 vs 여러 작은 클러스터"의 선택 기준:
| 요인 | 단일 대형 | 멀티클러스터 |
|---|---|---|
| 장애 격리 | 한 클러스터 장애 = 전체 | 클러스터별 격리 |
| 규제/데이터 주권 | 어려움(한 곳에 몰림) | 리전별 분산 용이 |
| 운영 복잡성 | 단순(하나의 제어) | 복잡(여러 apiserver/인증) |
| 자원 효율 | 좋음(풀 공유) | 낭비(클러스터 오버헤드) |
| 업그레이드 위험 | 전체 동시 위험 | 단계적(한 클러스터씩) |
이 trade-off가 "언제 멀티클러스터인가"의 답이다. 장애 격리/규제/단계적 업그레이드가 중요하면 멀티, 자원 효율/단순함이 중요하면 단일. 정답이 하나가 아니다.
직접 확인하기 (멀티클러스터는 로컬 재현 복잡)
멀티클러스터는 최소 2개 클러스터가 필요해 kind로도 복잡(kind 클러스터 2개 + Cilium/Submariner). 개념 중심으로 이해하고, 필요시 공식 튜토리얼로 실습.
# Cilium Cluster Mesh 개념 (2개 kind 클러스터, cilium clustermesh enable)
cilium clustermesh enable --context cluster1
cilium clustermesh connect --destination-context cluster2
cilium clustermesh status --context cluster1
상세 절차는 Cilium Cluster Mesh docs 실측.
흔히 묻는 것, 흔히 틀리는 것
| 오해 | 정정 |
|---|---|
| "멀티클러스터는 단일 클러스터와 같다" | 네트워크/디스커버리/인증이 별도. 단순 확장 아님 |
| "클러스터 간은 인터넷으로만" | Submariner/Cilium Mesh로 사설 연결 가능 |
| "Cluster Mesh는 Cilium 없어도 된다" | Cilium 데이터플레인 필수. 이기종엔 Submariner |
| "글로벌 서비스는 자동 부하 분산이다" | 로컬 우선/분산 정책 설정에 따라 |
| "네트워크만 연결하면 멀티클러스터 완성" | 인증·디스커버리·정책·운영이 더 어려움 |
| "Submariner 게이트웨이는 1개면 충분" | 단일장애점. 고가용성 다중화 권장 |
요약 — 이 글의 결론
- 멀티클러스터 네트워킹이 클러스터 경계를 넘은 Pod/Service 통신을 만든다 — 리전 분산, 장애 격리, 규제 대응이 동기. 클러스터를 나누면 IP/Service 이름이 별개가 되는 부작용을 넘는 것.
- 두 접근: Submariner(게이트웨이 터널 + MultiCluster DNS, CNI 독립) vs Cilium Cluster Mesh(eBPF 직접 라우팅, Cilium 전용).
- Submariner는 이기종 CNI 클러스터 연결에 유리. 단 게이트웨이 고가용성·캡슐화 오버헤드 고려.
- Cilium Cluster Mesh는 Cilium 생태계 자연 확장. Hubble 가시성 통합. 단 Cilium 전용.
- MultiCluster DNS(MCS)의
clusterset.local이름 체계가 "어느 클러스터"를 해석. 글로벌 서비스로 로컬 우선/failover 패턴. - 진짜 비용은 네트워크 너머 운영 — 인증 신뢰, 두 apiserver 관리, 정책 일관성, failover 자동화 신뢰. 기술 연결만으론 해결 안 됨.
- Cluster API가 클러스터 라이프사이클 자동화 층. 네트워크(통신) + CAPI(라이프사이클)가 대규모 멀티클러스터 운영의 표준.
- 단일 대형 vs 멀티는 장애 격리/규제/단계적 업그레이드 vs 자원 효율/단순함의 trade-off.
생각해 볼 문제
- 클러스터 A(Calico)와 클러스터 B(Cilium)를 연결하려 한다. 어느 도구가 적합한가, 왜?
- Submariner 게이트웨이 노드 1개가 죽었다. 클러스터 간 통신은 어떻게 되나?
- Cilium Cluster Mesh에서 한 클러스터의 etcd가 일시 단절됐다. 다른 클러스터의 라우팅은?
- 글로벌 서비스를 "로컬 우선"으로 설정했다. 클러스터 A의 Service가 죽으면 A의 Pod는 어디로 가나?
- 멀티클러스터에서 NetworkPolicy가 클러스터 경계를 넘어 적용되지 않는다면 생기는 보안 구멍은?
- 단일 거대 클러스터 vs 다중 클러스터의 trade-off를 "장애 격리 vs 운영 복잡성"으로 정리하라.
참고
- Submariner docs - 접근 2026-07-13 (게이트웨이, MultiCluster DNS)
- Cilium Cluster Mesh docs - 접근 2026-07-13
- Kubernetes Multicluster SIG - 개요 - 접근 2026-07-13
- Multi-cluster Services (MCS) API - 접근 2026-07-13 (clusterset.local 등)
- Kubernetes 공식 문서 - 클러스터 네트워킹 개요 - 접근 2026-07-13
'Tech Artifacts > K8s Networking' 카테고리의 다른 글
| K8s Networking - 13. troubleshooting (0) | 2026.07.14 |
|---|---|
| K8s Networking - 12. cilium-service-mesh (0) | 2026.07.14 |
| K8s Networking - 11. istio (0) | 2026.07.14 |
| K8s Networking - 10. service-mesh concepts (0) | 2026.07.14 |
| K8s Networking - 09. gateway-api (0) | 2026.07.14 |