1 · 걸리는 순간 기본 거부
정책이 하나라도 붙으면 그 Pod은 기본 거부가 된다. 명시적으로 허용한 것 외에는 전부 막힌다.
Pod 사이의 통신을 막는 법
Kubernetes 네트워크 모델의 전제 —
즉 아무것도 안 하면 클러스터 안은 완전히 평평하다.
NetworkPolicy는 이 기본값을 선택적으로 좁히는 도구다.
쿠버네티스는 위의 네트워크 모델을 요구사항으로 정의만 하고 직접 구현하지 않는다. 구현이 없으면 Pod은 IP조차 받지 못한다. 그 구현을 맡아 “평평한 네트워크”를 실제로 만드는 주체가 CNI(Container Network Interface — 컨테이너 런타임이 Pod 네트워크를 만들 때 호출하는 표준 인터페이스)다.
/etc/cni/net.d/ 에 있고, 바이너리는 /opt/cni/bin/ 에 있다 — 이 경로를 읽는 쪽도 런타임이다ContainerCreating 에서 멈춘다| CNI | 특징 | NetworkPolicy |
|---|---|---|
| Calico | 널리 쓰인다. BGP(라우터 간 경로 교환 프로토콜) 또는 오버레이 | 지원 (확장 정책도) |
| Cilium | eBPF(커널 내장 프로그래밍 기술) 기반. L7 정책까지 | 지원 (강력) |
| Flannel | 가장 단순한 오버레이 | 미지원 |
| Weave | 오버레이 | 지원 |
표의 내부 구현 방식 — BGP·eBPF·오버레이(노드 사이 패킷을 캡슐화해 터널로 나르는 방식) — 은 깊게 팔 필요 없다. 시험 관점에서는 어느 CNI가 NetworkPolicy를 지원하는지와 아래 파일 경로 정도면 충분하다.
덱의 축으로 보면 NetworkPolicy도 선언(spec)일 뿐이고, 실제 상태로 만드는 쪽은 CNI다. 다만 다른 리소스와 달리 이 루프가 비어 있어도 — CNI가 정책을 지원하지 않아도 — 아무 에러가 나지 않는다. 첫머리 함정의 “조용히 실패한다”가 여기서 나온다.
kubectl get pods -n kube-system | grep -Ei 'calico|cilium|flannel|weave'ls /etc/cni/net.d/apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: api-policy namespace: prod # ← 정책은 네임스페이스 안에서만 적용된다spec: podSelector: # 누구에게 적용할 것인가 matchLabels: { app: api } policyTypes: # 어느 방향을 통제할 것인가 - Ingress - Egress ingress: - from: - podSelector: matchLabels: { app: web } ports: - protocol: TCP port: 8080 egress: - to: - podSelector: matchLabels: { app: db } ports: - protocol: TCP port: 5432NetworkPolicy는 kubectl create로 못 만든다 — 시험 중에는 공식
Network Policies 문서의
예시 YAML을 복사해 셀렉터·포트만 바꾸는 게 가장 빠르다.
네 부분이 각각 다른 질문에 답한다.
1 · 걸리는 순간 기본 거부
정책이 하나라도 붙으면 그 Pod은 기본 거부가 된다. 명시적으로 허용한 것 외에는 전부 막힌다.
2 · 정책은 더해진다
여러 정책이 같은 Pod에 걸리면 허용의 합집합이다.
순서도, 우선순위도, deny 규칙도 없다.
3 · 없는 방향은 통제되지 않는다
policyTypes: [Ingress]만 쓰면
egress는 전혀 제한되지 않는다.
첫 번째 규칙이 가장 중요하다 — 정책 하나가 스위치를 뒤집는다. 따라서 더 좁은 정책을 하나 추가해 기존의 넓은 허용을 덮어쓸 수 없다. “기존 정책을 삭제하지 말라”는 문제에서는 새 후보만 비교하지 말고, 같은 Pod을 고르는 기존 정책이 이미 무엇을 허용하는지도 함께 읽는다.
이 셋을 알면 대부분의 혼란이 정리된다. 특히 “막는 정책”을 쓰려 하지 말 것 — 막는 방법은 허용하지 않는 것뿐이다.
apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: default-deny-all namespace: prodspec: podSelector: {} # 빈 셀렉터 = 이 네임스페이스의 모든 Pod policyTypes: - Ingress - Egress# ingress / egress 규칙이 없다 = 아무것도 허용하지 않는다podSelector | 뜻 |
|---|---|
{} (빈 값) | 네임스페이스의 모든 Pod |
matchLabels: {app: api} | 라벨이 맞는 Pod만 |
표준 패턴 — 화이트리스트
default-deny-all을 먼저 깐다
네임스페이스 전체가 기본 거부로 바뀐다.
필요한 통신만 정책을 추가해 뚫는다
정책은 더해지므로, 허용 규칙을 하나씩 얹으면 된다.
egress를 켰다면 DNS를 잊지 않는다
UDP/TCP 53을 안 열면 이름 해석부터 죽는다. 아래 절에서 다룬다.
ingress: - from: - podSelector: # ① 같은 네임스페이스의 Pod matchLabels: app: web
- namespaceSelector: # ② 다른 네임스페이스의 모든 Pod matchLabels: kubernetes.io/metadata.name: frontend
- ipBlock: # ③ IP 대역 (클러스터 밖 포함) cidr: 10.0.0.0/16 except: - 10.0.5.0/24podSelector 만 쓰면 정책이 있는 네임스페이스 안에서만 찾는다kubernetes.io/metadata.name 라벨이 자동으로 붙어 있다 — 이걸로 지정하면 편하다ipBlock은 Pod IP가 아니라 패킷의 소스 IP 기준이다from: - namespaceSelector: matchLabels: team: frontend - podSelector: matchLabels: app: web“frontend 네임스페이스의 모든 Pod”
또는
“같은 네임스페이스의 app=web Pod”
from: - namespaceSelector: matchLabels: team: frontend podSelector: matchLabels: app: web“frontend 네임스페이스이면서 app=web인 Pod”
spec: podSelector: matchLabels: { app: api } policyTypes: [Egress] egress: - to: - podSelector: matchLabels: { app: db } ports: - protocol: TCP port: 5432
- to: # ★ DNS를 반드시 열어야 한다 - namespaceSelector: # ← 이 둘은 같은 항목 = AND matchLabels: { kubernetes.io/metadata.name: kube-system } podSelector: matchLabels: { k8s-app: kube-dns } ports: - { protocol: UDP, port: 53 } - { protocol: TCP, port: 53 }셀렉터가 가리키는 것은 CoreDNS — 클러스터 내부 DNS 서버 — Pod이다.
kube-system 네임스페이스에서 도는데, 라벨이 k8s-app: kube-dns 인 것은
예전 구현(kube-dns)의 라벨을 호환 목적으로 그대로 쓰기 때문이다.
DNS를 안 열면 증상이 엉뚱한 곳에서 나타난다.
ports: - protocol: TCP port: 8080 - protocol: TCP port: http # Pod의 이름 있는 포트도 가능 - protocol: TCP port: 8000 endPort: 9000 # 범위이름 있는 포트와 endPort 범위는 있다는 것만 알아두면 된다. 이 절의 포인트는 아래 둘이다.
ports를 생략하면 모든 포트가 허용된다port는 Pod의 포트(targetPort) 다. Service의 port가 아니다NetworkPolicy는 Service를 모른다.
같은 이유로 NetworkPolicy로 Service를 막을 수는 없다. 막히는 것은 최종적으로 도달하는 Pod이다.
# db는 api에서 오는 5432만 받는다apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: db-allow-api namespace: prodspec: podSelector: matchLabels: tier: db policyTypes: [Ingress] ingress: - from: - podSelector: matchLabels: tier: api ports: - protocol: TCP port: 5432정책 하나가 어디까지 바꾸는지 보면 감이 온다.
kubectl get netpol -Akubectl describe netpol db-allow-api -n prod # 해석된 규칙을 보여준다
# 실제로 되는지 확인 — 이게 가장 확실하다kubectl run tmp --image=busybox:1.36 --rm -it --restart=Never -n prod -- sh wget -qO- --timeout=3 http://api-svc:8080 nc -zv 10.244.2.7 5432
# 라벨이 정말 맞는지kubectl get pods -n prod --show-labelskubectl get ns --show-labels| 증상 | 원인 후보 |
|---|---|
| 정책을 만들었는데 안 막힌다 | CNI가 NetworkPolicy를 지원하지 않는다 |
| 의도보다 많이 막힌다 | AND/OR 실수, DNS 미허용, 포트를 Service 포트로 씀 |
| 특정 네임스페이스만 안 된다 | namespaceSelector 라벨 불일치 |
| 이름 해석부터 실패 | egress에서 UDP 53 미허용 |
deny 규칙도, 우선순위도 없다policyTypes에 없는 방향은 통제되지 않는다podSelector: {} = 네임스페이스 전체 → default-deny-all 패턴