보안

Kubernetes NetworkPolicy, 왜 적용해도 통신이 막히는 거죠?

강코의 코딩 일기 2026. 7. 16. 15:07
반응형

쿠버네티스 NetworkPolicy 적용 후 컨테이너 간 통신이 안 되는 문제로 고민이신가요? 시니어 개발자가 겪는 흔한 오류 원인과 체계적인 해결 과정을 깊이 있게 다룹니다.

안녕하세요, 개발자 여러분! 쿠버네티스 환경에서 서비스를 운영하다 보면 NetworkPolicy는 선택이 아닌 필수가 되어가고 있죠. 그런데 말입니다, 분명 정책을 잘 적용했다고 생각했는데, 엉뚱하게 컨테이너 간 통신이 막혀 버리는 황당한 경험, 다들 한두 번쯤 해보셨을 거예요. 답답하시죠? 특히 프로덕션 환경에서 이런 문제가 발생하면 등줄기에 식은땀이 흐르기 마련입니다. 오늘은 시니어 개발자분들이 자주 겪는 Kubernetes NetworkPolicy 적용 오류와 그 해결 과정을 깊이 있게 파고들어 볼게요. 단순히 '이렇게 해라'가 아니라, '왜 이런 문제가 생기는지', 그리고 '어떻게 체계적으로 접근해야 하는지'에 대한 저의 경험을 공유해 드리려고 합니다.

Kubernetes NetworkPolicy 적용 오류: 컨테이너 간 통신 문제 해결 가이드 - candies, sweetmeats, jar, glass jar, container, glass container, confections, confectionery, treats, nibble, sweets, dessert, food, colorful, multicolored, delicious, sugar, candies, candies, jar, sweets, sweets, sweets, sweets, sweets, dessert, food, food, food, sugar, sugar, sugar

Image by Daria-Yakovleva on Pixabay

문제 발생: 예상치 못한 컨테이너 통신 단절

저희 팀에서는 마이크로서비스 아키텍처(MSA) 기반의 복잡한 서비스를 쿠버네티스에 배포해서 운영하고 있었어요. 최근 보안 강화를 위해 서비스 간 통신 제어를 NetworkPolicy로 구현하기로 했죠. 핵심 백엔드 서비스인 order-servicepayment-service와만 통신할 수 있도록 정책을 적용했습니다. 개발 환경에서 충분히 테스트했고, 잘 동작하는 것을 확인했거든요? 그런데 스테이징 환경에 배포하는 순간, order-servicepayment-service에 접근하지 못하는 문제가 발생한 겁니다. kubectl logs를 확인해보니, connection refusedtimeout 같은 메시지가 수도 없이 쏟아지더라고요. 분명히 정책을 허용했는데 왜 이런 문제가 생기는 걸까요? 심지어 다른 서비스들은 정상적으로 동작하는 상황이라 더욱 혼란스러웠습니다.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-order-to-payment
  namespace: default
spec:
  podSelector:
    matchLabels:
      app: order-service
  policyTypes:
    - Egress
  egress:
    - to:
        - podSelector:
            matchLabels:
              app: payment-service
      ports:
        - protocol: TCP
          port: 8080

위 정책은 order-servicepayment-service의 8080 포트로 아웃바운드 통신하는 것을 허용하는 정책입니다. 이 정책만 보면 아무런 문제가 없어 보였죠.

어디서부터 잘못된 걸까?

처음에는 단순히 오타를 의심했어요. 라벨이나 포트 번호가 틀렸을 수도 있으니까요. 하지만 여러 번 확인해도 정책 YAML 파일에는 문제가 없었습니다. 이쯤 되면 '혹시 쿠버네티스 자체의 버그인가?' 하는 생각까지 들기도 하죠. 하지만 대부분의 경우, 문제는 우리 생각보다 훨씬 더 기본적인 곳에 숨어있을 때가 많습니다.

원인 분석: NetworkPolicy 적용의 함정과 오해

저희 팀의 경험을 토대로 몇 가지 가능성을 좁혀 나갔습니다. NetworkPolicy는 생각보다 미묘한 동작 방식을 가지고 있거든요. 특히 다음 세 가지 지점에서 오류가 자주 발생하곤 합니다.

1. CNI 플러그인의 NetworkPolicy 지원 여부

가장 기본적인 전제부터 확인해야 합니다. Kubernetes NetworkPolicy는 모든 CNI(Container Network Interface) 플러그인에서 기본적으로 지원하는 기능이 아니에요. Calico, Cilium, Weave Net 등 대부분의 프로덕션 CNI는 지원하지만, kube-proxy만으로는 NetworkPolicy가 동작하지 않습니다. 혹시라도 사용 중인 CNI 플러그인이 NetworkPolicy를 지원하지 않거나, 해당 기능이 제대로 활성화되지 않았다면 아무리 정책을 적용해도 소용이 없겠죠. 저희는 Calico를 사용하고 있었고, Calico Pod들이 정상적으로 동작하는 것을 확인했습니다.

kubectl get pods -n kube-system | grep calico

이 명령어로 Calico Pod들이 Running 상태인지 확인하는 것이 첫 번째 단계입니다.

2. 기본 Deny 정책의 이해 부족

NetworkPolicy의 가장 중요한 특징 중 하나는 "화이트리스트" 방식이라는 겁니다. 즉, 아무런 NetworkPolicy도 적용되어 있지 않은 파드는 모든 통신을 허용하지만, NetworkPolicy가 하나라도 적용되는 순간, 명시적으로 허용되지 않은 모든 통신은 기본적으로 차단됩니다. 특히 policyTypes 필드를 눈여겨봐야 해요.

정책 유형 설명 기본 동작
Ingress 파드로 들어오는 인바운드 통신 정책 적용 시, 명시적으로 허용되지 않은 모든 인바운드 통신 차단
Egress 파드에서 나가는 아웃바운드 통신 정책 적용 시, 명시적으로 허용되지 않은 모든 아웃바운드 통신 차단

저희 사례에서 order-servicepayment-service로 나가는 Egress 정책만 가지고 있었죠. 그런데 order-service가 외부 DNS 서버나 다른 내부 인프라 서비스(예: 로깅, 모니터링)와 통신해야 하는 상황이라면 어떻게 될까요? NetworkPolicy를 적용하는 순간, Egress 정책에 명시적으로 허용되지 않은 모든 아웃바운드 통신은 차단됩니다. payment-service로의 통신만 허용하고 다른 곳으로의 통신을 막아버린 셈이죠. 특히 DNS 쿼리도 아웃바운드 통신이기 때문에, DNS 서버로의 Egress 통신이 막히면 서비스 디스커버리 자체가 실패할 수 있습니다. 이것이 바로 저희 문제의 핵심 원인이었죠!

3. Selector의 오해와 누락

NetworkPolicypodSelector를 이용해 어떤 파드에 정책을 적용할지 결정합니다. 그리고 ingressegress 규칙 내의 from/to 필드 또한 podSelector, namespaceSelector, ipBlock을 이용해 통신 대상을 지정하죠. 만약 podSelector가 잘못 지정되었거나, 의도치 않게 다른 파드에 정책이 적용된다면 예상치 못한 통신 문제를 야기할 수 있습니다.

kubectl describe networkpolicy allow-order-to-payment -n default

이 명령어를 통해 적용된 정책의 세부 사항, 특히 Pods affected by this policy 섹션을 면밀히 검토해야 합니다. 우리가 의도한 파드에만 적용되고 있는지, 그리고 정책이 어떤 통신을 허용하고 차단하는지 명확하게 이해하는 것이 중요하죠.

Kubernetes NetworkPolicy 적용 오류: 컨테이너 간 통신 문제 해결 가이드 - yellow, blue, container, window, color, metal, geometry

Image by ValterM on Pixabay

해결 과정: 체계적인 트러블슈팅과 정책 개선

원인을 파악했으니 이제 해결 방법을 찾아야겠죠? 저희는 다음과 같은 단계로 문제를 해결했습니다.

1. DNS 통신 허용 정책 추가

가장 먼저, 파드 내부에서 DNS 쿼리가 가능하도록 Egress 정책에 DNS 서버(일반적으로 kube-dns/CoreDNS)로의 통신을 허용하는 규칙을 추가했습니다. 보통 kube-system 네임스페이스에 있는 DNS 파드로의 UDP 53 포트 통신을 허용해 주면 됩니다.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-order-service-egress
  namespace: default
spec:
  podSelector:
    matchLabels:
      app: order-service
  policyTypes:
    - Egress
  egress:
    # payment-service로의 통신 허용
    - to:
        - podSelector:
            matchLabels:
              app: payment-service
      ports:
        - protocol: TCP
          port: 8080
    # DNS 서버로의 통신 허용 (UDP 53)
    - to:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: kube-system # kube-system 네임스페이스
          podSelector:
            matchLabels:
              k8s-app: kube-dns # CoreDNS 또는 kube-dns 파드
      ports:
        - protocol: UDP
          port: 53

이 정책을 적용하자마자 order-servicepayment-service를 찾고 통신하는 데 성공했습니다. DNS 문제였던 거죠! 이처럼 NetworkPolicy를 적용할 때는 서비스 간 통신뿐만 아니라, 파드가 외부 리소스(DNS, 메트릭 서버, 로깅 서버 등)와 통신해야 하는 기본적인 요구사항도 함께 고려해야 합니다.

2. 단계적 적용 및 테스트

한 번에 모든 NetworkPolicy를 적용하기보다는, "Allow All" 정책으로 시작하여 점진적으로 제한적인 정책을 추가하는 방식이 훨씬 안전합니다. 예를 들어, 특정 서비스에 NetworkPolicy를 처음 적용할 때는 Egress 정책만 먼저 적용하고, 이후 Ingress 정책을 추가하는 식으로 말이죠. 각 정책을 적용할 때마다 관련 서비스의 통신이 정상적인지 꼼꼼하게 테스트해야 합니다.

# 임시로 모든 Egress 통신을 허용하는 정책 (디버깅용)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all-egress-debug
  namespace: default
spec:
  podSelector:
    matchLabels:
      app: order-service
  policyTypes:
    - Egress
  egress:
    - {} # 모든 아웃바운드 통신 허용

이런 식으로 임시 'Allow All' 정책을 적용하여 문제가 NetworkPolicy 때문인지 아니면 다른 설정 때문인지 빠르게 구분할 수 있습니다. 문제가 NetworkPolicy 때문이라면, 이 정책을 제거하고 세부적인 정책을 하나씩 추가하면서 어느 부분에서 통신이 막히는지 찾아낼 수 있죠.

교훈 및 재발 방지 전략: 안전하고 유연한 네트워크 설계

이번 경험을 통해 저희 팀은 NetworkPolicy를 다룰 때 몇 가지 중요한 교훈을 얻었습니다. 시니어 개발자라면 이러한 트레이드오프와 설계 원칙에 대한 깊이 있는 이해가 필수적이라고 생각합니다.

1. 최소 권한 원칙과 기본 통신 요구사항의 균형

"최소 권한 원칙(Principle of Least Privilege)"은 보안의 기본이지만, 이를 맹목적으로 적용하다 보면 서비스의 기본 동작을 방해할 수 있습니다. 모든 NetworkPolicy를 설계할 때는 다음과 같은 질문을 스스로에게 던져보세요.

  • 이 파드가 동작하기 위해 어떤 외부 리소스(DNS, NTP, 메트릭 수집기, 로깅 서버, 클라우드 메타데이터 서비스 등)와의 통신이 필수적인가?
  • 이 파드와 통신해야 하는 내부 서비스는 무엇인가?
  • 이 파드가 외부로 요청을 보내야 하는 경우는 무엇인가?

이러한 질문에 대한 답을 바탕으로 필요한 최소한의 통신을 먼저 허용하고, 그 다음으로 특정 서비스 간 통신을 제어하는 방식으로 접근해야 합니다.

2. 네임스페이스 기반의 정책 설계

파드 단위로 세밀하게 정책을 제어하는 것도 좋지만, 복잡도가 증가할 수 있습니다. 서비스의 특성에 따라 네임스페이스 단위로 NetworkPolicy를 적용하면 관리 포인트를 줄이고 가독성을 높일 수 있습니다. 예를 들어, 특정 네임스페이스 내의 모든 파드는 다른 특정 네임스페이스로만 Egress 통신을 허용하도록 설정하는 것이죠.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-all-ingress-in-app-namespace
  namespace: my-app-namespace
spec:
  podSelector: {} # 이 네임스페이스의 모든 파드
  policyTypes:
    - Ingress
  ingress:
    # 아무런 ingress 규칙이 없으므로, 이 네임스페이스의 모든 파드로의 인바운드 통신을 기본적으로 차단합니다.
    # (단, 다른 NetworkPolicy에서 명시적으로 허용하는 규칙이 있다면 해당 규칙이 우선합니다.)
    - from:
        - podSelector: {} # 이 규칙은 사실상 아무것도 허용하지 않습니다.
          # 만약 특정 네임스페이스로부터의 통신을 허용하려면 여기에 namespaceSelector를 추가합니다.

위와 같이 podSelector: {}를 사용하고 ingress 규칙을 비워두거나 특정 규칙만 명시하면, 해당 네임스페이스의 모든 파드에 대해 기본적으로 인바운드 트래픽을 차단하는 강력한 기본 Deny 정책을 구현할 수 있습니다. 이후 필요한 통신만 명시적으로 허용하는 것이죠.

3. 지속적인 모니터링 및 자동화된 테스트

NetworkPolicy는 배포 후에도 지속적으로 모니터링해야 합니다. kubectl describe networkpolicy와 같은 명령어로 현재 적용된 정책을 확인하고, 실제 통신 흐름을 추적할 수 있는 툴(예: Calico의 calicoctl, Cilium의 Hubble)을 활용하면 좋습니다. 또한, CI/CD 파이프라인에 NetworkPolicy의 유효성을 검사하는 자동화된 테스트를 포함하여, 정책 변경이 예상치 못한 부작용을 일으키지 않도록 사전에 방지하는 것이 중요합니다.

NetworkPolicy는 쿠버네티스 환경에서 보안을 강화하는 강력한 도구이지만, 그만큼 섬세한 접근이 필요합니다. 단순히 문법을 아는 것을 넘어, 쿠버네티스 네트워킹의 전반적인 이해와 함께 기본 Deny 동작 방식, CNI 플러그인의 특성까지 고려해야만 의도한 대로 동작하는 정책을 만들 수 있죠.

이번 경험을 통해 여러분도 Kubernetes NetworkPolicy 적용 오류에 직면했을 때 당황하지 않고 체계적으로 문제를 해결할 수 있는 인사이트를 얻으셨기를 바랍니다. 혹시 여러분만의 NetworkPolicy 트러블슈팅 노하우나 재미있는 경험담이 있으시다면 댓글로 공유해 주세요! 함께 성장하는 개발 커뮤니티가 되었으면 좋겠습니다.

감사합니다!

📌 함께 읽으면 좋은 글

  • [보안] DNS 보안에 대한 치명적 오해: DNSSEC과 DoH/DoT 미적용이 초래할 위험
  • [개발 책 리뷰] 예상치 못한 런타임 오류, Advanced TypeScript로 시스템 안정성을 확보하는 법
  • [튜토리얼] 복잡한 웹 UI, 게임 개발 ECS 패턴으로 50% 이상 관리 효율 높이는 3가지 방법

이 글이 도움이 되셨다면 공감(♥)댓글로 응원해 주세요!
궁금한 점이나 다루었으면 하는 주제가 있다면 댓글로 남겨주세요.

반응형