본문 바로가기
IT/k8s

[k8s] Kubernetes 네트워크 정책 완벽 가이드: 보안 강화 및 트래픽 제어

by 수누다 2026. 4. 12.

쿠버네티스 클러스터, 혹시 기본 설정으로 쓰고 계신가요?

솔직히 말씀드리면, 저도 처음 Kubernetes(쿠버네티스)를 운영할 때 네트워크 정책 같은 건 나중에 생각하자 하고 넘어갔었거든요. 일단 배포가 되고 서비스가 올라가면 된 거 아닌가 싶었는데... 보안 감사 받고 나서 등골이 오싹했습니다 ㅎㅎ. 클러스터 내부 Pod(파드)들이 서로 아무 제한 없이 통신하고 있었던 거죠. 쉽게 말해 사무실 내부망에서 모든 직원이 모든 서버에 접근할 수 있는 상황이었던 겁니다.

Kubernetes 네트워크 정책(Network Policy)은 이런 상황을 막아주는 핵심 보안 도구입니다. 이 글에서는 제가 실제로 홈랩과 업무 환경에서 삽질하며 익힌 쿠버네티스 네트워크 정책 설정 방법을 처음부터 끝까지 정리해 드릴게요. Calico(칼리코)나 Cilium(실리움) 같은 CNI 플러그인 선택부터, 실전 YAML 예제, 그리고 자주 빠지는 함정까지 전부 다룹니다.

▲ Kubernetes 네트워크 정책의 전체 아키텍처 개요 — 네임스페이스(Namespace)와 Pod(파드) 간의 트래픽 흐름을 정책으로 제어하는 구조

Kubernetes 네트워크 정책이란? 개념부터 잡고 가요

기본 개념: 화이트리스트 방화벽이라고 생각하세요

NetworkPolicy(네트워크 폴리시)는 쿠버네티스 리소스 중 하나로, Pod 간 또는 Pod와 외부 간의 트래픽을 제어하는 규칙 집합입니다. 중요한 특성이 있는데요, 기본적으로 아무 NetworkPolicy도 없으면 모든 트래픽이 허용됩니다. 이게 바로 제가 처음에 방심했던 이유기도 해요.

Kubernetes 네트워크 정책을 적용하면 동작 방식이 달라집니다. 특정 Pod에 정책이 하나라도 적용되는 순간, 그 정책에서 명시적으로 허용한 트래픽만 통과하고 나머지는 전부 차단되거든요. 전형적인 화이트리스트(Whitelist) 방식입니다.

핵심 용어 정리

  • Ingress(인그레스, 수신 트래픽): Pod로 들어오는 트래픽 규칙
  • Egress(이그레스, 송신 트래픽): Pod에서 나가는 트래픽 규칙
  • podSelector(파드 셀렉터): 정책을 적용할 Pod를 라벨로 선택
  • namespaceSelector(네임스페이스 셀렉터): 특정 네임스페이스의 트래픽만 허용
  • ipBlock(IP 블록): 특정 CIDR 대역의 IP에 대한 규칙

⚠️ 중요! NetworkPolicy는 CNI 플러그인이 지원해야 동작합니다

여기서 많은 분들이 놓치는 포인트가 있어요. Kubernetes 네트워크 정책 리소스를 아무리 만들어도, 클러스터의 CNI(Container Network Interface, 컨테이너 네트워크 인터페이스) 플러그인이 이를 지원하지 않으면 그냥 무시됩니다. Flannel(플란넬)이 대표적인 미지원 CNI인데, 저도 초기에 Flannel 쓰면서 정책 적용이 왜 안 되지? 하고 한참 헤맸었거든요 ㅎㅎ.

CNI 플러그인 NetworkPolicy 지원 특징 추천 환경
Calico ✅ 지원 성숙한 생태계, GlobalNetworkPolicy 확장 지원 온프레미스, 엔터프라이즈
Cilium ✅ 지원 (L7 포함) eBPF 기반, HTTP/gRPC 레벨 정책 가능 클라우드 네이티브, 고성능 환경
Weave Net ✅ 지원 설치 간편, 소규모 클러스터 소규모 클러스터
Flannel ❌ 미지원 단순, 오버헤드 낮음 보안 불필요한 개발 환경
AWS VPC CNI ✅ 지원 (EKS) AWS 네이티브 통합 AWS EKS

저는 현재 홈랩에선 Calico를, 실무 쿠버네티스 클러스터에서는 Cilium을 쓰고 있습니다. 둘 다 좋은데, 처음 시작하신다면 Calico가 자료도 많고 진입장벽이 낮아서 추천드려요.

실전 구현: 단계별로 따라해보세요

1단계: 현재 클러스터 CNI 확인

먼저 내 클러스터가 Kubernetes 네트워크 정책을 지원하는지 확인해야 하거든요.

# kube-system 네임스페이스에서 CNI 관련 Pod 확인
kubectl get pods -n kube-system | grep -E 'calico|cilium|weave|flannel'

# 또는 CNI 설정 파일 직접 확인
ls /etc/cni/net.d/

2단계: 기본 Deny-All 정책 적용 (가장 먼저 해야 할 것)

제가 권장하는 방법은 먼저 모든 트래픽을 차단하는 기본 정책을 만들고, 필요한 것만 하나씩 허용하는 방식입니다. 보안의 기본 원칙이죠.

# deny-all-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: production  # 적용할 네임스페이스
spec:
  podSelector: {}  # 빈 셀렉터 = 네임스페이스 내 모든 Pod 대상
  policyTypes:
  - Ingress
  - Egress
  # ingress, egress 규칙을 명시하지 않으면 전부 차단
kubectl apply -f deny-all-ingress.yaml

💡 : 이 정책을 적용하면 DNS 조회도 막힙니다! CoreDNS(코어DNS)로의 Egress 트래픽도 같이 열어주셔야 해요. 저 처음에 이거 몰라서 Pod들이 서비스 이름 못 찾아서 난리났었습니다 ㅎㅎ.

3단계: DNS 트래픽 허용 정책

# allow-dns-egress.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-dns-egress
  namespace: production
spec:
  podSelector: {}  # 모든 Pod에 적용
  policyTypes:
  - Egress
  egress:
  - to:
    - namespaceSelector:
        matchLabels:
          kubernetes.io/metadata.name: kube-system
    ports:
    - protocol: UDP
      port: 53
    - protocol: TCP
      port: 53

4단계: 특정 서비스 간 통신 허용

실제 서비스 환경에서 가장 많이 쓰는 패턴입니다. 예를 들어 frontend Pod가 backend Pod에만 접근하고, backend Pod는 database Pod에만 접근하는 구조를 만들어볼게요.

▲ Frontend → Backend → Database 계층 구조에서 Kubernetes 네트워크 정책으로 트래픽을 제어하는 설정 다이어그램

# backend-network-policy.yaml
# backend Pod: frontend에서 오는 트래픽만 수신 허용
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: backend-allow-from-frontend
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: backend  # backend 라벨이 붙은 Pod에 적용
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: frontend  # frontend 라벨 Pod에서 오는 트래픽만 허용
    ports:
    - protocol: TCP
      port: 8080
# database-network-policy.yaml
# database Pod: backend에서 오는 트래픽만 수신 허용
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: db-allow-from-backend
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: database
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: backend
    ports:
    - protocol: TCP
      port: 5432  # PostgreSQL 기준

5단계: 네임스페이스 간 트래픽 제어

마이크로서비스 환경에서는 네임스페이스를 팀별, 서비스별로 나누는 경우가 많은데요. 이때 네임스페이스 간 통신도 Kubernetes 네트워크 정책으로 제어할 수 있습니다.

# allow-from-monitoring-namespace.yaml
# monitoring 네임스페이스의 Prometheus가 메트릭 수집할 수 있도록 허용
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-monitoring-scrape
  namespace: production
spec:
  podSelector:
    matchLabels:
      monitoring: "true"  # 모니터링 허용 라벨이 붙은 Pod만
  policyTypes:
  - Ingress
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          purpose: monitoring  # monitoring 네임스페이스에서 오는 트래픽 허용
    ports:
    - protocol: TCP
      port: 9090

💡 네임스페이스에 라벨 붙이는 방법:

kubectl label namespace monitoring purpose=monitoring
kubectl label namespace kube-system kubernetes.io/metadata.name=kube-system

⚠️ 삽질 경험: 이런 함정 조심하세요!

함정 1: podSelector와 namespaceSelector의 AND/OR 차이

이게 진짜 헷갈립니다. 저도 처음에 의도한 대로 동작 안 해서 한참 디버깅했었거든요. YAML에서 하이픈(-)의 위치에 따라 AND 조건과 OR 조건이 달라집니다.

# 잘못된 예: OR 조건 (두 조건 중 하나만 만족해도 허용)
ingress:
- from:
  - namespaceSelector:       # OR
      matchLabels:
        env: production
  - podSelector:             # OR
      matchLabels:
        app: frontend

# 올바른 예: AND 조건 (두 조건을 동시에 만족해야 허용)
ingress:
- from:
  - namespaceSelector:       # AND
      matchLabels:
        env: production
    podSelector:             # AND (하이픈 없음!)
      matchLabels:
        app: frontend

하이픈 하나 차이인데 보안적으로 엄청난 차이가 납니다. 위 예시에서 OR 조건이면 production 네임스페이스의 모든 Pod가 접근 가능하고, AND 조건이면 production 네임스페이스의 frontend 라벨 Pod만 접근 가능하거든요.

함정 2: Egress 정책에서 외부 API 호출 막힘

Egress를 전체 차단하고 필요한 것만 열다 보면, 외부 API 서버 IP가 바뀌거나 CDN을 쓰는 경우 관리가 힘들어집니다. 이럴 때 ipBlock을 활용하면 좋더라고요.

# 특정 외부 IP 대역 허용
egress:
- to:
  - ipBlock:
      cidr: 0.0.0.0/0    # 모든 외부 IP
      except:
      - 10.0.0.0/8       # 내부망 제외
      - 172.16.0.0/12
      - 192.168.0.0/16
  ports:
  - protocol: TCP
    port: 443

함정 3: 정책 적용 후 기존 연결 끊김

Kubernetes 네트워크 정책은 기존 연결에도 즉시 적용됩니다. 운영 중인 서비스에 Deny-All 정책을 먼저 넣으면 서비스 장애가 나거든요. 반드시 허용 정책을 먼저 다 만들어두고, 마지막에 Deny 정책을 적용하세요. 저 이거 운영 환경에서 한 번 실수했다가 진땀 뺐습니다 ㅠㅠ.

정책 검증: 제대로 동작하는지 확인하기

정책 만들었다고 끝이 아니죠. 실제로 의도한 대로 동작하는지 반드시 검증해야 합니다.

netshoot으로 트래픽 테스트

# 테스트용 Pod 실행 (네트워크 디버깅 도구가 모두 들어있는 이미지)
kubectl run test-pod --image=nicolaka/netshoot -n production --rm -it -- /bin/bash

# 접근 가능 여부 테스트
curl -v http://backend-service:8080/health
nc -zv database-service 5432

# DNS 조회 테스트
nslookup kubernetes.default.svc.cluster.local

kubectl 명령어로 정책 확인

# 현재 적용된 NetworkPolicy 목록 확인
kubectl get networkpolicy -n production

# 특정 정책 상세 보기
kubectl describe networkpolicy backend-allow-from-frontend -n production

# 모든 네임스페이스 정책 한번에 보기
kubectl get networkpolicy --all-namespaces

Cilium CLI로 정책 확인 (Cilium 사용 시)

# Cilium CLI 설치 후
cilium connectivity test

# 특정 Pod의 정책 확인
cilium policy get

# 트래픽 모니터링
cilium monitor --type drop

▲ Cilium Hubble UI를 활용한 네트워크 트래픽 흐름 시각화 — 허용/차단된 트래픽을 실시간으로 확인할 수 있는 대시보드

Calico vs Cilium: 어떤 걸 선택할까요?

k8s 네트워크 보안을 강화하려고 CNI를 바꾸려는 분들을 위해 두 가지를 비교해볼게요. 저도 여러 환경에서 둘 다 써봤는데, 각각 장단점이 뚜렷하더라고요.

비교 항목 Calico Cilium
기술 기반 iptables / eBPF (선택) eBPF 전용
L7 정책 제한적 ✅ HTTP, gRPC, Kafka 지원
성능 좋음 매우 좋음 (eBPF 덕분)
학습 난이도 낮음 ~ 중간 중간 ~ 높음
관찰성(Observability) 보통 ✅ Hubble UI로 탁월함
GlobalNetworkPolicy ✅ 지원 (CRD) ✅ CiliumNetworkPolicy
커뮤니티/자료 매우 풍부 빠르게 성장 중
추천 대상 안정성 중시, 온프레미스 성능/보안 중시, 클라우드 네이티브

Cilium의 L7 네트워크 트래픽 제어 기능은 정말 강력합니다. HTTP 메서드(GET/POST), 경로(Path), 헤더까지 기반으로 정책을 만들 수 있거든요. 예를 들어 /admin 경로는 특정 Pod에서만 접근 가능하게 한다든지 하는 게 가능합니다. 이건 Calico 표준 기능으로는 못하는 거라 최근에 저도 Cilium으로 많이 넘어가고 있어요.

자주 묻는 질문 (FAQ)

Q. NetworkPolicy가 없는 네임스페이스는 어떻게 되나요?

A. 아무 NetworkPolicy도 없으면 모든 트래픽이 허용됩니다. 단, 다른 네임스페이스에서 Egress 정책으로 해당 네임스페이스로의 트래픽을 차단하면 막힐 수 있어요.

Q. NetworkPolicy는 Node 간 트래픽도 제어하나요?

A. 아니요, NetworkPolicy는 Pod 레벨에서 동작합니다. Node 레벨 트래픽은 OS 방화벽(iptables, firewalld)이나 클라우드 보안 그룹(Security Group)으로 별도 관리해야 합니다.

Q. 정책이 너무 많아지면 성능에 영향을 주나요?

A. 이건 CNI에 따라 다릅니다. iptables 기반 Calico는 정책이 많아질수록 성능 저하가 있을 수 있는데, eBPF 기반 Cilium은 이 문제에서 상대적으로 자유롭습니다.

Q. Helm으로 배포한 앱에 NetworkPolicy를 쉽게 적용하는 방법이 있나요?

A. 많은 Helm 차트들이 values.yaml에 networkPolicy 섹션을 제공합니다. 해당 섹션을 활성화하면 자동으로 적절한 정책이 생성되거든요. 먼저 차트 문서를 확인해보시는 걸 추천드립니다.

Kubernetes 네트워크 정책 모범 사례 요약 인포그래픽 - 보안 체크리스트와 Calico Cilium 비교

▲ Kubernetes 네트워크 정책 모범 사례 요약 — 보안 체크리스트와 단계별 적용 가이드 인포그래픽

마무리: 네트워크 정책, 이렇게 시작하세요

처음에 언급했던 제 실수처럼, 많은 분들이 쿠버네티스 네트워크 보안을 나중으로 미루는 경향이 있습니다. 근데 사실 나중에 적용하는 게 훨씬 더 어렵더라고요. 이미 돌아가고 있는 서비스들의 통신 패턴을 파악하고 하나씩 허용해줘야 하니까요.

제가 추천하는 시작 순서를 정리해드릴게요:

  1. CNI가 NetworkPolicy를 지원하는지 확인 (필요하면 Calico/Cilium으로 교체)
  2. 개발/스테이징 환경에서 먼저 실험 — 운영에 바로 적용하지 마세요!
  3. 허용 정책을 먼저 다 만들고, 마지막에 Deny-All 적용
  4. DNS Egress는 반드시 열어두기
  5. netshoot이나 Cilium Hubble로 검증
  6. 모니터링 툴에서 메트릭 수집 경로도 열어두기

🎉 Kubernetes 네트워크 정책을 제대로 구성하고 나면 클러스터 보안이 확실히 다른 레벨이 됩니다. 침해 사고가 나더라도 피해 범위를 최소화할 수 있는 게 가장 큰 장점이거든요.

다음 글에서는 Calico GlobalNetworkPolicy를 활용해서 클러스터 전체에 적용되는 베이스라인 보안 정책을 만드는 방법을 다룰 예정입니다. 이전 글에서 쿠버네티스 RBAC(역할 기반 접근 제어) 설정을 다뤘으니 함께 보시면 더 좋을 것 같아요.

궁금한 점이나 다른 삽질 경험 있으시면 댓글로 공유해주세요. 저도 아직 배우는 중이라 함께 이야기 나누는 게 좋거든요 😊