Tech Artifacts/K8s Networking

K8s Networking - 14. multi-cluster

클러스터 경계를 넘는 서비스 — 멀티클러스터 네트워킹의 두 접근

한 조직이 리전별로 클러스터 3개(서울/도쿄/버지니아)를 운영한다. 각 클러스터는 독립적이다 — 그런데 "서울 클러스터의 서비스가 도쿄 클러스터의 서비스를 불러야 한다"는 요구가 생겼다. 인터넷 경유 공개 API로만 부르자니 지연과 보안이 문제다. 멀티클러스터 네트워킹이 이 문제를 푼다 — 클러스터 경계를 넘어 Pod가 마치 같은 클러스터에 있는 것처럼 통신하게.

이 글이 푸는 것은: 클러스터 간 Pod/Service 통신을 어떻게 만드는가, SubmarinerCilium 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)

  1. 게이트웨이 노드: 각 클러스터에서 지정된 노드들이 서로 터널(IPsec)로 연결된다.
  2. 라우팅: 클러스터 A의 Pod가 클러스터 B의 Pod IP 대역으로 보내면, 게이트웨이가 터널로 넘긴다.
  3. 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)

  1. 각 클러스터의 Cilium 에이전트가 다른 클러스터의 etcd/apiserver를 읽기로 연결(클러스터 간 Pod/Service 정보 동기화).
  2. 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의 정확한 동작 — 언제 원격으로 넘어가나

로컬 우선에서 한 클러스터의 서비스가 죽으면:

  1. 로컬 EndpointSlice가 비어간다(헬스체크 실패한 Pod 제거).
  2. 트래픽이 남은 로컬 Pod로. 전부 죽으면 → 원격 클러스터로.
  3. 복구되면 다시 로컬 우선으로 복귀.

이 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.

생각해 볼 문제

  1. 클러스터 A(Calico)와 클러스터 B(Cilium)를 연결하려 한다. 어느 도구가 적합한가, 왜?
  2. Submariner 게이트웨이 노드 1개가 죽었다. 클러스터 간 통신은 어떻게 되나?
  3. Cilium Cluster Mesh에서 한 클러스터의 etcd가 일시 단절됐다. 다른 클러스터의 라우팅은?
  4. 글로벌 서비스를 "로컬 우선"으로 설정했다. 클러스터 A의 Service가 죽으면 A의 Pod는 어디로 가나?
  5. 멀티클러스터에서 NetworkPolicy가 클러스터 경계를 넘어 적용되지 않는다면 생기는 보안 구멍은?
  6. 단일 거대 클러스터 vs 다중 클러스터의 trade-off를 "장애 격리 vs 운영 복잡성"으로 정리하라.

참고

은 관리자 편집기 위젯 라벨이 됨 -->