Kubernetes 멀티 클러스터 네트워크 정책 설계 및 트러블슈팅 완벽 가이드

Kubernetes 멀티 클러스터 네트워크 정책 설계 및 트러블슈팅 완벽 가이드

Kubernetes 멀티 클러스터 네트워크 정책 설계는 단일 클러스터 운영과는 완전히 다른 차원의 복잡도를 가집니다. 클러스터가 하나일 때는 NetworkPolicy 몇 줄로 해결되던 트래픽 격리 문제가, 멀티 클러스터 환경에서는 CNI 선택, 클러스터 간 인증, IP 대역 충돌, 서비스 디스커버리 레이어까지 얽히면서 걷잡을 수 없이 커집니다.

실제로 3개 클러스터를 운영하는 프로젝트에서 특정 네임스페이스의 트래픽이 다른 클러스터로 예상치 못하게 흘러 들어가는 이슈를 경험한 적이 있습니다. 원인을 찾는 데만 이틀이 걸렸고, 결국 CNI 플러그인의 기본 설정이 멀티 클러스터 시나리오를 전혀 고려하지 않고 있었다는 것을 발견했습니다.

이 글에서는 멀티 클러스터 환경에서 Kubernetes 네트워크 정책을 올바르게 설계하는 방법, 자주 발생하는 트러블슈팅 패턴, 그리고 실무에서 바로 적용 가능한 NetworkPolicy YAML 예제를 상세히 다룹니다.

이 글을 읽고 나면: CNI 별 멀티 클러스터 지원 범위를 이해하고, 클러스터 간 트래픽을 세밀하게 제어하는 NetworkPolicy를 직접 작성하며, 흔히 빠지는 트러블슈팅 함정을 사전에 피할 수 있습니다.

Kubernetes NetworkPolicy가 멀티 클러스터에서 다른 이유

Kubernetes NetworkPolicy는 기본적으로 클러스터 내부 범위에서만 동작합니다. 즉, 표준 NetworkPolicy 리소스는 동일 클러스터 내 Pod 간 트래픽 제어만을 정의하며, 클러스터 경계를 넘는 트래픽에 대해서는 아무런 권한이 없습니다.

멀티 클러스터 환경에서 트래픽 격리를 달성하려면 두 가지 레이어를 별도로 설계해야 합니다. 첫째, 클러스터 내부 정책 — 각 클러스터에서 개별적으로 적용하는 표준 NetworkPolicy. 둘째, 클러스터 간 정책 — CNI 확장 기능이나 서비스 메시(Istio, Linkerd)를 통해 구현하는 크로스 클러스터 트래픽 제어.

Calico의 GlobalNetworkPolicy, Cilium의 CiliumClusterwideNetworkPolicy 같은 CRD(Custom Resource Definition)가 바로 두 번째 레이어를 채우기 위해 등장한 것입니다. 이 CRD들은 표준 Kubernetes API 범위 밖에 있기 때문에, 어떤 CNI를 선택하느냐에 따라 설계 방식이 근본적으로 달라집니다.

주의: 표준 NetworkPolicy만으로 멀티 클러스터 격리가 가능하다고 착각하는 경우가 많습니다. 특히 VPN이나 클러스터 피어링으로 여러 클러스터의 Pod CIDR이 라우팅 가능해진 환경에서는, NetworkPolicy가 없으면 모든 클러스터의 모든 Pod가 서로 통신할 수 있습니다.

CNI별 멀티 클러스터 네트워크 정책 지원 비교

CNI(Container Network Interface) 플러그인마다 멀티 클러스터 네트워크 정책 지원 범위가 크게 다릅니다. 아래 비교표는 실무에서 가장 많이 사용하는 3가지 CNI를 기준으로 정리했습니다.

기능 Cilium Calico Flannel
표준 NetworkPolicy ✅ (타 CNI 필요)
클러스터 범위 정책 CiliumClusterwideNetworkPolicy GlobalNetworkPolicy
멀티 클러스터 지원 ClusterMesh (자체 구현) Federation (BGP 기반)
L7 정책 (HTTP/gRPC) ✅ (eBPF 기반) 제한적
암호화 (WireGuard/IPsec)
클러스터 간 서비스 디스커버리 ✅ (GlobalService) 별도 구성 필요

실무에서 멀티 클러스터 네트워크 정책이 핵심 요구사항이라면, Cilium ClusterMesh 또는 Calico BGP Federation 중 하나를 선택하는 것이 현실적입니다. Flannel은 멀티 클러스터 시나리오에서 사실상 사용이 불가능합니다.

저는 3개 이상의 클러스터를 운영하는 환경에서 Cilium ClusterMesh를 선택했는데, eBPF 기반의 관찰성(Hubble UI)과 L7 정책 지원이 운영 중 발생하는 문제를 빠르게 진단하는 데 결정적인 도움이 됐습니다.

Cilium ClusterMesh 기반 네트워크 정책 설계 패턴

Cilium ClusterMesh를 사용하면 클러스터 간 Pod를 하나의 논리적 네트워크처럼 다룰 수 있습니다. 핵심은 cluster-idcluster-name 레이블을 활용한 선택자(selector) 설계입니다.

아래는 cluster-afrontend 네임스페이스에서만 cluster-bapi 서비스로 접근을 허용하는 CiliumNetworkPolicy 예시입니다.

# cluster-b에 배포하는 정책: cluster-a의 frontend만 허용
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: allow-cross-cluster-frontend
  namespace: api
spec:
  endpointSelector:
    matchLabels:
      app: api-server
  ingress:
    - fromEndpoints:
        - matchLabels:
            # cluster-a의 frontend Pod만 허용
            k8s:app: frontend
            # Cilium이 자동으로 주입하는 클러스터 식별 레이블
            io.cilium.k8s.policy.cluster: cluster-a
      toPorts:
        - ports:
            - port: "8080"
              protocol: TCP
          rules:
            http:
              - method: "GET"
                path: "/api/v1/.*"

이 정책의 핵심은 io.cilium.k8s.policy.cluster 레이블입니다. Cilium ClusterMesh가 활성화되면 각 클러스터의 엔드포인트에 이 레이블이 자동으로 주입되므로, 클러스터 원산지를 정책에서 명시적으로 지정할 수 있습니다.

한 가지 주의할 점은, GlobalService를 사용할 때 serviceAffinity 설정을 반드시 검토해야 한다는 것입니다. 기본값인 local로 설정하면 로컬 클러스터의 서비스를 우선하고, none으로 설정하면 모든 클러스터의 엔드포인트를 균등하게 사용합니다. 장애 조치(failover) 시나리오에 맞는 값을 골라야 합니다.

멀티 클러스터 네트워크 정책 트러블슈팅 — 흔한 오진 패턴 3가지

멀티 클러스터 환경에서 네트워크 정책 문제를 디버깅할 때 가장 자주 마주치는 오진 패턴들이 있습니다. 이걸 미리 알아두면 수 시간의 디버깅 시간을 절약할 수 있습니다.

오진 패턴 1: NetworkPolicy는 맞는데 연결이 안 되는 경우 — DNS 문제 혼동

NetworkPolicy에서 egress를 허용했는데 연결이 안 된다면, DNS 포트(UDP/TCP 53) egress가 빠진 경우가 대부분입니다. DNS 조회 자체가 차단되면 IP 통신이 가능해도 도메인 이름으로 연결할 수 없습니다.

# DNS egress를 반드시 포함
egress:
  - toEndpoints:
      - matchLabels:
          k8s:io.kubernetes.pod.namespace: kube-system
          k8s:k8s-app: kube-dns
    toPorts:
      - ports:
          - port: "53"
            protocol: UDP
          - port: "53"
            protocol: TCP

오진 패턴 2: 정책 우선순위 충돌 — Calico GlobalNetworkPolicy와 네임스페이스 정책 간 충돌

Calico는 order 필드로 정책 우선순위를 지정합니다. GlobalNetworkPolicy의 order가 낮을수록(=숫자가 작을수록) 먼저 평가됩니다. Deny 규칙이 높은 우선순위(낮은 order 값)로 설정된 GlobalNetworkPolicy에 있을 때, 네임스페이스 수준의 허용 정책이 무시되는 경우가 있습니다.

오진 패턴 3: Pod CIDR 겹침

여러 클러스터의 Pod CIDR이 동일하면(예: 두 클러스터 모두 10.244.0.0/16 사용), VPN이나 피어링 환경에서 라우팅 충돌이 발생합니다. 클러스터 생성 시 Pod CIDR을 반드시 분리해야 합니다. Cilium ClusterMesh는 클러스터 간 Pod CIDR이 겹치지 않아야 한다는 것을 공식 문서에서 명시하고 있습니다.

Cilium 환경에서는 cilium monitorhubble observe 명령으로 실시간 패킷 드롭 이유를 확인할 수 있습니다.

# 특정 Pod에서 드롭되는 패킷과 이유 확인
hubble observe --pod api/api-server-xxx --verdict DROPPED --follow

# NetworkPolicy 관련 드롭만 필터
hubble observe --verdict DROPPED --protocol tcp --port 8080

멀티 클러스터 트래픽 격리 — Zero Trust 설계 원칙 적용

멀티 클러스터 네트워크 정책 설계의 궁극적인 목표는 Zero Trust 원칙 적용입니다. 모든 트래픽은 명시적으로 허용될 때까지 차단(Default Deny)되어야 합니다.

아래는 클러스터 전체에 Default Deny를 적용하는 Cilium 정책입니다.

# 모든 ingress/egress를 기본 차단하는 클러스터 범위 정책
apiVersion: "cilium.io/v2"
kind: CiliumClusterwideNetworkPolicy
metadata:
  name: default-deny-all
spec:
  endpointSelector: {}  # 모든 엔드포인트에 적용
  ingress:
    - {}  # 아무것도 허용하지 않음
  egress:
    - {}  # 아무것도 허용하지 않음

이 정책을 적용한 뒤, 필요한 통신 경로만 허용 정책을 추가하는 방식으로 구성합니다. Kubernetes 공식 문서는 NetworkPolicy를 지원하는 CNI가 없으면 이 정책이 적용되지 않는다고 명시하므로, CNI 설치 여부를 반드시 사전에 확인해야 합니다.

Zero Trust 모델에서 클러스터 간 인증도 중요합니다. Cilium ClusterMesh는 상호 TLS(mTLS)로 클러스터 간 연결을 인증하며, Istio를 서비스 메시로 사용하는 경우 PeerAuthentication 정책으로 클러스터 간 mTLS를 강제할 수 있습니다. 이와 관련해 Kubernetes Pod OOMKilled 트러블슈팅 가이드에서도 클러스터 리소스 관리와 관찰성 도구 활용법을 다루고 있으니 함께 참고하면 좋습니다.

자주 묻는 질문 (FAQ)

Q. 표준 Kubernetes NetworkPolicy만으로 멀티 클러스터 트래픽 격리가 가능한가요?

표준 NetworkPolicy는 단일 클러스터 내 트래픽만 제어할 수 있어 멀티 클러스터 격리에는 충분하지 않습니다. 클러스터 경계를 넘는 트래픽 제어는 Cilium ClusterMesh, Calico GlobalNetworkPolicy, 또는 Istio 같은 서비스 메시의 추가 기능이 반드시 필요합니다. 단순히 NetworkPolicy만 적용하고 멀티 클러스터 격리가 됐다고 가정하면 보안 사고로 이어질 수 있습니다.

Q. Cilium ClusterMesh와 Istio 멀티 클러스터를 함께 사용할 수 있나요?

함께 사용할 수 있으며, 상호 보완적입니다. Cilium은 L3/L4 레이어의 네트워크 정책과 eBPF 기반 성능 최적화를, Istio는 L7 레이어의 트래픽 관리와 mTLS 인증을 담당합니다. 단, 두 레이어가 모두 L7 정책을 처리하도록 중복 설정하면 예상치 못한 트래픽 드롭이 발생할 수 있으므로, 책임 영역을 명확히 분리해야 합니다.

Q. 멀티 클러스터에서 NetworkPolicy 디버깅 시 가장 효과적인 도구는 무엇인가요?

Cilium 환경에서는 hubble observe --verdict DROPPED가 가장 빠른 방법입니다. Calico 환경에서는 calicoctl get networkpolicy -o yaml로 정책을 확인하고, kubectl exec로 테스트 Pod를 실행해 curl이나 nc로 연결 테스트를 진행합니다. 공통적으로는 kubectl describe pod의 이벤트 로그와 CNI 플러그인의 로그(/var/log/calico/ 또는 Cilium Pod 로그)를 우선 확인하는 것을 권장합니다.

Q. Pod CIDR이 겹치는 클러스터를 ClusterMesh에 추가하면 어떻게 되나요?

Cilium ClusterMesh는 Pod CIDR이 겹치는 클러스터를 공식적으로 지원하지 않습니다. 추가 시도 자체는 가능하지만, 라우팅 충돌로 인해 크로스 클러스터 통신이 비정상적으로 동작합니다. 기존 클러스터의 CIDR을 변경하려면 클러스터를 재생성하거나, NAT 기반 우회 경로를 설계해야 하는데 운영 복잡도가 크게 올라가므로 처음 클러스터 생성 시 CIDR 계획을 반드시 수립하는 것이 맞습니다.

Kubernetes 멀티 클러스터 네트워크 정책 설계는 한 번에 완성되는 작업이 아닙니다. 지금 당장 시작할 수 있는 첫 번째 액션은 현재 운영 중인 클러스터의 CNI가 클러스터 범위 정책을 지원하는지 확인하는 것입니다. kubectl get crds | grep -E "cilium|calico" 명령으로 어떤 CRD가 설치되어 있는지 먼저 파악하세요. 그다음 CiliumClusterwideNetworkPolicy 또는 GlobalNetworkPolicy로 Default Deny 정책부터 적용하고, 필요한 경로를 하나씩 열어가는 방식으로 진행하면 가장 안전하게 멀티 클러스터 트래픽 격리를 완성할 수 있습니다.