목차
1. Kubernetes 네트워킹 모델의 기본 원칙
Kubernetes 네트워킹은 세 가지 기본 원칙 위에 구축됩니다.
1.1 핵심 요구사항
- 모든 Pod는 고유한 IP 주소를 가진다 - NAT 없이 서로 통신 가능
- 모든 Pod는 다른 모든 Pod에 도달할 수 있다 - 같은 노드든 다른 노드든
- Pod가 보는 자신의 IP와 다른 Pod가 보는 IP가 동일하다 - NAT 없음
┌────────────────────────────────────────────────────┐
│ Kubernetes 네트워킹 기본 원칙 │
│ │
│ ┌──────────┐ ┌──────────┐ │
│ │ Node 1 │ │ Node 2 │ │
│ │ │ │ │ │
│ │ ┌──────┐ │ NAT │ ┌──────┐ │ │
│ │ │Pod A │◄├───없음!───►┤│Pod C │ │ │
│ │ │10.1.1│ │ │ │10.1.2│ │ │
│ │ └──────┘ │ │ └──────┘ │ │
│ │ ┌──────┐ │ │ ┌──────┐ │ │
│ │ │Pod B │ │ │ │Pod D │ │ │
│ │ │10.1.1│ │ │ │10.1.2│ │ │
│ │ └──────┘ │ │ └──────┘ │ │
│ └──────────┘ └──────────┘ │
│ │
│ Pod A(10.1.1.2) → Pod C(10.1.2.3): 직접 통신 │
│ NAT, 포트 매핑 없음. 각 Pod는 고유 IP 소유 │
└────────────────────────────────────────────────────┘
1.2 네트워킹 계층 구조
┌──────────────────────────────────────────┐
│ Kubernetes 네트워킹 계층 │
├──────────────────────────────────────────┤
│ L7: Ingress / Gateway API │
│ (HTTP 라우팅, TLS 종료) │
├──────────────────────────────────────────┤
│ L4: Service │
│ (ClusterIP, NodePort, LoadBalancer) │
├──────────────────────────────────────────┤
│ L3: Pod 네트워킹 (CNI) │
│ (Pod-to-Pod, 오버레이/언더레이) │
├──────────────────────────────────────────┤
│ L2-L3: 노드 네트워킹 │
│ (물리/가상 네트워크) │
└──────────────────────────────────────────┘
2. Pod-to-Pod 네트워킹: 내부 동작 원리
2.1 같은 노드 내 Pod 통신
┌────────────────────────────────────────────┐
│ Node │
│ │
│ ┌──────┐ veth pair ┌──────────┐ │
│ │Pod A │◄──────────────►│ │ │
│ │eth0 │ │ cbr0 │ │
│ │ │ │ (bridge) │ │
│ └──────┘ │ │ │
│ │ │ │
│ ┌──────┐ veth pair │ │ │
│ │Pod B │◄──────────────►│ │ │
│ │eth0 │ └──────────┘ │
│ └──────┘ │
│ │
│ 1. Pod A가 Pod B로 패킷 전송 │
│ 2. veth pair를 통해 브릿지(cbr0)로 전달 │
│ 3. 브릿지가 MAC 주소로 목적지 veth 찾기 │
│ 4. Pod B의 veth pair를 통해 Pod B로 전달 │
└────────────────────────────────────────────┘
veth pair: 한쪽 끝은 Pod 네임스페이스의 eth0, 다른 쪽은 노드의 브릿지에 연결된 가상 이더넷 쌍입니다.
2.2 다른 노드의 Pod 통신 (오버레이)
┌──────────┐ ┌──────────┐
│ Node 1 │ │ Node 2 │
│ │ │ │
│ ┌──────┐ │ VXLAN/IPinIP │ ┌──────┐ │
│ │Pod A │ │ ┌───────────┐ │ │Pod C │ │
│ │10.1.1│ │───►│ 터널링 │────►│ │10.1.2│ │
│ └──────┘ │ │ │ │ └──────┘ │
│ │ │원본 패킷을 │ │ │
│ cbr0 │ │외부 패킷에 │ │ cbr0 │
│ 10.1.1.0 │ │캡슐화 │ │ 10.1.2.0 │
│ │ └───────────┘ │ │
│ eth0 │ │ eth0 │
│192.168.1│ │192.168.2│
└──────────┘ └──────────┘
VXLAN 캡슐화:
┌─────────────────────────────────────────┐
│ 외부IP │ UDP │ VXLAN │ 내부IP │ Payload │
│ Hdr │ Hdr │ Hdr │ Hdr │ │
│192→192│ │ │10→10 │ Data │
└─────────────────────────────────────────┘
2.3 오버레이 vs 언더레이 네트워킹
| 특성 | 오버레이 (VXLAN/IPinIP) | 언더레이 (BGP/Direct) |
|---|---|---|
| 설정 난이도 | 쉬움 | 어려움 |
| 네트워크 요구사항 | L3 연결만 필요 | BGP 지원 필요 |
| 성능 오버헤드 | 있음 (캡슐화) | 없음 |
| MTU 영향 | 감소 (50-54 bytes) | 없음 |
| 디버깅 | 어려움 | 쉬움 |
| 사용 사례 | 클라우드, 멀티테넌트 | 베어메탈, 고성능 |
3. CNI 플러그인 상세 비교
3.1 CNI란 무엇인가
CNI(Container Network Interface)는 컨테이너 런타임이 네트워크 플러그인과 상호작용하는 표준 인터페이스입니다.
Pod 생성 시 CNI 동작 흐름:
1. kubelet이 CRI를 통해 컨테이너 생성 요청
2. 컨테이너 런타임이 네트워크 네임스페이스 생성
3. kubelet이 CNI 플러그인 호출 (ADD 명령)
4. CNI 플러그인이:
a. veth pair 생성
b. Pod에 IP 주소 할당 (IPAM)
c. 라우팅 규칙 설정
d. 필요시 오버레이 터널 설정
5. Pod가 네트워크 준비 완료
3.2 주요 CNI 플러그인 비교
| 기능 | Calico | Cilium | Flannel | Weave Net |
|---|---|---|---|---|
| 개발사 | Tigera | Isovalent (Cisco) | CoreOS | Weaveworks |
| 데이터플레인 | iptables/eBPF | eBPF | VXLAN | VXLAN/슬리브 |
| 네트워크 모드 | BGP/VXLAN/IPinIP | VXLAN/Native | VXLAN | VXLAN |
| Network Policy | 풍부함 | 매우 풍부 (L7) | 없음 | 기본적 |
| 암호화 | WireGuard | WireGuard/IPsec | 없음 | IPsec |
| kube-proxy 대체 | eBPF 모드 | 네이티브 eBPF | 불가 | 불가 |
| 관찰성 | 기본적 | Hubble (강력) | 없음 | Scope |
| 서비스 메시 | 없음 | 내장 (옵션) | 없음 | 없음 |
| 멀티클러스터 | 지원 | ClusterMesh | 미지원 | 지원 |
| 성능 | 높음 (BGP) | 매우 높음 (eBPF) | 보통 | 낮음 |
| 복잡도 | 중간 | 높음 | 낮음 | 낮음 |
| 프로덕션 추천 | 강력 추천 | 강력 추천 | 소규모만 | 비추천 |
3.3 Calico 심화
# Calico BGP 모드 설정 (IPPool)
apiVersion: crd.projectcalico.org/v1
kind: IPPool
metadata:
name: default-ipv4-ippool
spec:
cidr: 10.244.0.0/16
encapsulation: None # BGP 사용 시 캡슐화 없음
natOutgoing: true
nodeSelector: all()
blockSize: 26 # /26 = 노드당 64 IP
---
# Calico BGP Peering 설정
apiVersion: crd.projectcalico.org/v1
kind: BGPPeer
metadata:
name: rack-tor-switch
spec:
peerIP: 192.168.1.1
asNumber: 64512
nodeSelector: rack == "rack-1"
# Calico VXLAN 모드 (클라우드 환경)
apiVersion: crd.projectcalico.org/v1
kind: IPPool
metadata:
name: default-ipv4-ippool
spec:
cidr: 10.244.0.0/16
encapsulation: VXLAN # VXLAN 캡슐화
natOutgoing: true
vxlanMode: Always
3.4 Cilium 심화
# Cilium Helm 설치 (kube-proxy 대체 모드)
# helm install cilium cilium/cilium --namespace kube-system \
# --set kubeProxyReplacement=true \
# --set k8sServiceHost=API_SERVER_IP \
# --set k8sServicePort=6443 \
# --set hubble.enabled=true \
# --set hubble.relay.enabled=true \
# --set hubble.ui.enabled=true \
# --set encryption.enabled=true \
# --set encryption.type=wireguard
# Cilium 상태 확인
# cilium status
# cilium connectivity test
# Cilium L7 Network Policy (HTTP 기반 정책)
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: l7-rule
namespace: default
spec:
endpointSelector:
matchLabels:
app: backend
ingress:
- fromEndpoints:
- matchLabels:
app: frontend
toPorts:
- ports:
- port: "8080"
protocol: TCP
rules:
http:
- method: "GET"
path: "/api/v1/.*"
- method: "POST"
path: "/api/v1/orders"
4. Service 심화: ClusterIP부터 LoadBalancer까지
4.1 Service의 동작 원리
┌──────────────────────────────────────────────────┐
│ Service 트래픽 흐름 │
│ │
│ Client Pod ──► Service VIP ──► kube-proxy │
│ (10.96.0.10) /iptables/IPVS │
│ │ │
│ ┌────────┼────────┐ │
│ ▼ ▼ ▼ │
│ Pod A Pod B Pod C │
│ 10.1.1.2 10.1.1.3 10.1.2.4│
│ │
│ Service VIP는 실제 인터페이스에 바인딩되지 않음 │
│ iptables/IPVS 규칙이 DNAT으로 Pod IP로 변환 │
└──────────────────────────────────────────────────┘
4.2 Service 유형별 상세
# 1. ClusterIP (기본값) - 클러스터 내부 접근만 가능
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
type: ClusterIP
selector:
app: my-app
ports:
- port: 80
targetPort: 8080
# 클러스터 IP: 자동 할당 (예: 10.96.0.10)
# 클러스터 내부에서만 10.96.0.10:80으로 접근 가능
# 2. NodePort - 모든 노드의 고정 포트로 외부 접근
apiVersion: v1
kind: Service
metadata:
name: my-nodeport-service
spec:
type: NodePort
selector:
app: my-app
ports:
- port: 80
targetPort: 8080
nodePort: 30080 # 30000-32767 범위
# 모든 노드의 30080 포트로 접근 가능
# NodeIP:30080 → ClusterIP:80 → Pod:8080
# 3. LoadBalancer - 클라우드 로드밸런서 자동 프로비저닝
apiVersion: v1
kind: Service
metadata:
name: my-lb-service
annotations:
service.beta.kubernetes.io/aws-load-balancer-type: nlb
service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing
spec:
type: LoadBalancer
selector:
app: my-app
ports:
- port: 443
targetPort: 8443
# 외부 LB IP → NodePort → ClusterIP → Pod
# 4. ExternalName - 외부 DNS를 클러스터 내 서비스로 매핑
apiVersion: v1
kind: Service
metadata:
name: external-db
spec:
type: ExternalName
externalName: mydb.example.com
# external-db.default.svc.cluster.local → mydb.example.com
# 5. Headless Service - ClusterIP 없이 Pod IP 직접 반환
apiVersion: v1
kind: Service
metadata:
name: my-headless-service
spec:
clusterIP: None # Headless 선언
selector:
app: my-stateful-app
ports:
- port: 5432
# DNS 쿼리 시 Service IP가 아닌 개별 Pod IP 반환
# StatefulSet과 함께 사용: pod-0.my-headless-service.default.svc
4.3 Session Affinity
apiVersion: v1
kind: Service
metadata:
name: sticky-service
spec:
selector:
app: web
ports:
- port: 80
sessionAffinity: ClientIP
sessionAffinityConfig:
clientIP:
timeoutSeconds: 10800 # 3시간
# 같은 클라이언트 IP는 같은 Pod로 라우팅
5. kube-proxy 모드: iptables vs IPVS vs eBPF
5.1 iptables 모드 (기본)
iptables 규칙 체인 (Service당):
PREROUTING → KUBE-SERVICES → KUBE-SVC-XXXX → KUBE-SEP-YYYY
(확률적 분배)
Service에 Pod 3개인 경우:
KUBE-SVC-XXXX:
-p tcp -d 10.96.0.10 --dport 80
→ 33% 확률로 KUBE-SEP-1 (DNAT → 10.1.1.2:8080)
→ 33% 확률로 KUBE-SEP-2 (DNAT → 10.1.1.3:8080)
→ 34% 확률로 KUBE-SEP-3 (DNAT → 10.1.2.4:8080)
문제점:
- Service/Pod 수에 비례하여 규칙 수 증가 (O(n))
- 규칙 업데이트 시 전체 체인 재작성
- 10,000+ Service에서 성능 저하 심각
5.2 IPVS 모드
IPVS (IP Virtual Server):
┌───────────────────────────────────────────┐
│ IPVS Virtual Server: 10.96.0.10:80 │
│ │
│ Real Server 1: 10.1.1.2:8080 (weight 1)│
│ Real Server 2: 10.1.1.3:8080 (weight 1)│
│ Real Server 3: 10.1.2.4:8080 (weight 1)│
│ │
│ Algorithm: rr (Round Robin) │
│ 다른 옵션: lc, dh, sh, sed, nq │
└───────────────────────────────────────────┘
장점:
- 해시 테이블 기반 O(1) 조회
- 다양한 로드밸런싱 알고리즘
- 대규모 클러스터에서 안정적 성능
- 실시간 통계 및 연결 추적
# kube-proxy IPVS 모드 설정
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
mode: "ipvs"
ipvs:
scheduler: "rr" # rr, lc, dh, sh, sed, nq
syncPeriod: "30s"
minSyncPeriod: "2s"
5.3 eBPF 모드 (Cilium)
eBPF kube-proxy 대체:
┌────────────────────────────────────────────┐
│ 기존: Pod → iptables/IPVS → Pod │
│ eBPF: Pod → BPF 프로그램 → Pod (직접!) │
│ │
│ ┌──────┐ BPF map ┌──────┐ │
│ │Pod A │──(Service ──│Pod B │ │
│ │ │ lookup) │ │ │
│ └──────┘ └──────┘ │
│ │
│ iptables 체인 순회 없음! │
│ 커널 공간에서 직접 패킷 리다이렉트 │
└────────────────────────────────────────────┘
| 비교 항목 | iptables | IPVS | eBPF (Cilium) |
|---|---|---|---|
| 조회 복잡도 | O(n) | O(1) | O(1) |
| 규칙 업데이트 | 전체 재작성 | 증분 업데이트 | 증분 업데이트 |
| 로드밸런싱 | 확률적 | 다양한 알고리즘 | Maglev 해시 |
| 연결 추적 | conntrack | conntrack | BPF conntrack |
| 성능 | 보통 | 높음 | 매우 높음 |
| L7 정책 | 불가 | 불가 | 가능 |
| 관찰성 | 제한적 | 기본적 | Hubble (강력) |
| 10K Services | 매우 느림 | 빠름 | 매우 빠름 |
6. DNS: CoreDNS와 서비스 디스커버리
6.1 CoreDNS 아키텍처
┌──────────────────────────────────────────────────┐
│ CoreDNS 동작 │
│ │
│ Pod ──DNS 쿼리──► CoreDNS Pod │
│ (nameserver (kube-system) │
│ 10.96.0.10) │
│ │ │
│ ┌──────────┼──────────┐ │
│ ▼ ▼ ▼ │
│ K8s API Corefile Upstream │
│ (서비스/Pod (플러그인 DNS │
│ 레코드) 설정) (외부 DNS) │
└──────────────────────────────────────────────────┘
6.2 DNS 레코드 형식
Service DNS:
my-service.my-namespace.svc.cluster.local
└─ 서비스명 ─┘ └ 네임스페이스 ┘
Pod DNS:
10-1-1-2.my-namespace.pod.cluster.local
└ IP(점→대시)┘
Headless Service의 Pod DNS (StatefulSet):
pod-0.my-headless.my-namespace.svc.cluster.local
└ Pod명┘ └ 서비스명 ┘
SRV 레코드:
_http._tcp.my-service.my-namespace.svc.cluster.local
→ 포트 번호와 호스트명 반환
6.3 CoreDNS Corefile 설정
# CoreDNS Corefile (ConfigMap)
apiVersion: v1
kind: ConfigMap
metadata:
name: coredns
namespace: kube-system
data:
Corefile: |
.:53 {
errors
health {
lameduck 5s
}
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
prometheus :9153
forward . /etc/resolv.conf {
max_concurrent 1000
}
cache 30
loop
reload
loadbalance
}
6.4 DNS 디버깅
# DNS 디버깅용 Pod 생성
kubectl run dnsutils --image=registry.k8s.io/e2e-test-images/jessie-dnsutils:1.3 \
--restart=Never -- sleep infinity
# Service DNS 조회
kubectl exec dnsutils -- nslookup my-service.default.svc.cluster.local
# Pod DNS 조회
kubectl exec dnsutils -- nslookup 10-1-1-2.default.pod.cluster.local
# DNS 응답 상세 확인
kubectl exec dnsutils -- dig my-service.default.svc.cluster.local +search +short
# CoreDNS 로그 확인
kubectl logs -n kube-system -l k8s-app=kube-dns -f
# resolv.conf 확인
kubectl exec dnsutils -- cat /etc/resolv.conf
# nameserver 10.96.0.10
# search default.svc.cluster.local svc.cluster.local cluster.local
# options ndots:5
6.5 ndots:5 문제와 해결
# ndots:5 기본 설정의 문제:
# "api.example.com" 조회 시 (점이 5개 미만):
# 1. api.example.com.default.svc.cluster.local (실패)
# 2. api.example.com.svc.cluster.local (실패)
# 3. api.example.com.cluster.local (실패)
# 4. api.example.com (성공)
# → 외부 DNS 조회에 불필요한 3번의 쿼리 발생!
# 해결방법 1: Pod DNS 설정 커스터마이징
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
dnsConfig:
options:
- name: ndots
value: "2" # ndots를 2로 줄임
containers:
- name: app
image: my-app
# 해결방법 2: FQDN으로 직접 접근 (끝에 점)
# api.example.com. ← 마지막 점이 FQDN을 의미
7. Ingress: HTTP/HTTPS 라우팅
7.1 Ingress 아키텍처
┌──────────────────────────────────────────────────┐
│ Ingress 아키텍처 │
│ │
│ 외부 트래픽 │
│ │ │
│ ▼ │
│ ┌─────────────────────┐ │
│ │ Ingress Controller │ (nginx, Traefik 등) │
│ │ (실제 프록시) │ │
│ └─────────┬───────────┘ │
│ │ │
│ Ingress 리소스 감시 │
│ (라우팅 규칙) │
│ │ │
│ ┌──────┼──────┐ │
│ ▼ ▼ ▼ │
│ Svc A Svc B Svc C │
│ /api /web /docs │
└──────────────────────────────────────────────────┘
7.2 Ingress 리소스 예시
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
ingressClassName: nginx
tls:
- hosts:
- app.example.com
secretName: app-tls-secret
rules:
- host: app.example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api-service
port:
number: 80
- path: /
pathType: Prefix
backend:
service:
name: frontend-service
port:
number: 80
7.3 Ingress Controller 비교
| 기능 | nginx-ingress | Traefik | HAProxy | Contour |
|---|---|---|---|---|
| 개발사 | Kubernetes/F5 | Traefik Labs | HAProxy Tech | VMware |
| 프로토콜 | HTTP/HTTPS/gRPC | HTTP/HTTPS/gRPC/TCP | HTTP/HTTPS/TCP | HTTP/HTTPS/gRPC |
| 설정 리로드 | 재시작 필요 | 핫 리로드 | 핫 리로드 | 핫 리로드 |
| Rate Limiting | 어노테이션 | 미들웨어 | 내장 | 미지원 |
| 인증 | Basic/OAuth | Forward Auth | 내장 | 제한적 |
| 성능 | 높음 | 중간 | 매우 높음 | 높음 |
| Gateway API | 지원 | 지원 | 부분 지원 | 완전 지원 |
| 커뮤니티 | 매우 큼 | 큼 | 중간 | 중간 |
8. Gateway API: Ingress의 진화
8.1 왜 Gateway API인가
Ingress의 한계:
- 어노테이션에 의존한 비표준 설정
- L7 HTTP만 지원 (TCP/UDP/gRPC 미지원)
- 단일 리소스로 역할 분리 어려움
- 트래픽 분할, 헤더 매칭 등 고급 기능 부재
8.2 Gateway API 리소스 계층
┌──────────────────────────────────────────────────┐
│ Gateway API 리소스 계층 │
│ │
│ 인프라 관리자: │
│ ┌─────────────┐ │
│ │GatewayClass │ 어떤 컨트롤러를 사용할지 │
│ └──────┬──────┘ │
│ │ │
│ 클러스터 운영자: │
│ ┌──────▼──────┐ │
│ │ Gateway │ 리스너 (포트, 프로토콜, TLS) │
│ └──────┬──────┘ │
│ │ │
│ 애플리케이션 개발자: │
│ ┌──────▼──────┐ │
│ │ HTTPRoute │ 라우팅 규칙 (호스트, 경로, 헤더) │
│ │ TCPRoute │ │
│ │ GRPCRoute │ │
│ └─────────────┘ │
└──────────────────────────────────────────────────┘
8.3 Gateway API 실전 예시
# 1. GatewayClass - 인프라 관리자가 정의
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: cilium
spec:
controllerName: io.cilium/gateway-controller
---
# 2. Gateway - 클러스터 운영자가 정의
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: my-gateway
namespace: gateway-infra
spec:
gatewayClassName: cilium
listeners:
- name: https
protocol: HTTPS
port: 443
tls:
mode: Terminate
certificateRefs:
- kind: Secret
name: wildcard-tls
allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
shared-gateway: "true"
---
# 3. HTTPRoute - 개발자가 정의
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: app-routes
namespace: my-app
spec:
parentRefs:
- name: my-gateway
namespace: gateway-infra
hostnames:
- "app.example.com"
rules:
# 헤더 기반 라우팅
- matches:
- headers:
- name: "X-Canary"
value: "true"
backendRefs:
- name: app-canary
port: 80
weight: 100
# 트래픽 분할 (Canary 배포)
- matches:
- path:
type: PathPrefix
value: /api
backendRefs:
- name: app-stable
port: 80
weight: 90
- name: app-canary
port: 80
weight: 10
# 기본 라우팅
- backendRefs:
- name: app-stable
port: 80
8.4 Ingress vs Gateway API 비교
| 기능 | Ingress | Gateway API |
|---|---|---|
| 역할 분리 | 단일 리소스 | GatewayClass/Gateway/Route |
| 프로토콜 | HTTP/HTTPS만 | HTTP/HTTPS/TCP/UDP/gRPC |
| 트래픽 분할 | 어노테이션 (비표준) | 네이티브 weight 지원 |
| 헤더 매칭 | 어노테이션 (비표준) | 표준 스펙 |
| TLS 설정 | 기본적 | 세밀한 제어 |
| 크로스 네임스페이스 | 어려움 | 네이티브 지원 |
| 확장성 | 어노테이션만 | Policy API 확장 |
| 상태 | GA (안정) | GA (v1.0+, 2023) |
9. Network Policy: 마이크로 세그멘테이션
9.1 기본 개념
Network Policy는 Pod 수준의 방화벽입니다. 선택된 Pod에 대한 트래픽을 허용/차단합니다.
중요: Network Policy가 없으면 모든 트래픽이 허용됩니다. Network Policy를 하나라도 적용하면 해당 Pod에 대해 명시적으로 허용된 트래픽만 통과합니다.
9.2 Default Deny 정책
# 네임스페이스의 모든 Ingress 차단 (기본 거부)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: production
spec:
podSelector: {} # 모든 Pod에 적용
policyTypes:
- Ingress # Ingress만 차단 (Egress는 허용)
---
# 네임스페이스의 모든 Egress도 차단
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
9.3 실전 Network Policy 예시
# 백엔드 Pod: 프론트엔드에서만 접근 허용
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-policy
namespace: production
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Ingress
- Egress
ingress:
- from:
# 같은 네임스페이스의 frontend Pod만 허용
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
egress:
# DB 접근 허용
- to:
- podSelector:
matchLabels:
app: postgres
ports:
- protocol: TCP
port: 5432
# DNS 허용 (필수!)
- to:
- namespaceSelector: {}
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
9.4 Cilium Network Policy (L7 확장)
# Cilium: HTTP 메서드/경로 기반 정책
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: api-l7-policy
spec:
endpointSelector:
matchLabels:
app: api-server
ingress:
- fromEndpoints:
- matchLabels:
role: reader
toPorts:
- ports:
- port: "443"
protocol: TCP
rules:
http:
- method: GET # 읽기만 허용
- fromEndpoints:
- matchLabels:
role: writer
toPorts:
- ports:
- port: "443"
protocol: TCP
rules:
http:
- method: GET
- method: POST # 쓰기도 허용
- method: PUT
- method: DELETE
---
# Cilium: DNS 기반 Egress 정책
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: dns-egress-policy
spec:
endpointSelector:
matchLabels:
app: payment
egress:
- toFQDNs:
- matchPattern: "*.stripe.com" # Stripe API만 허용
- matchName: "api.paypal.com" # PayPal API만 허용
toPorts:
- ports:
- port: "443"
protocol: TCP
10. eBPF 네트워킹: iptables를 넘어서
10.1 eBPF란 무엇인가
eBPF(extended Berkeley Packet Filter)는 커널을 수정하지 않고 커널 공간에서 프로그램을 실행할 수 있는 기술입니다.
┌──────────────────────────────────────────────────┐
│ eBPF 아키텍처 │
│ │
│ User Space │
│ ┌──────────────────────────────────────────┐ │
│ │ Cilium Agent / hubble │ │
│ │ (eBPF 프로그램 관리, 정책 적용) │ │
│ └──────────────┬───────────────────────────┘ │
│ │ BPF Syscall │
│ ───────────────┼────────────────────────────── │
│ Kernel Space │ │
│ ┌──────────────▼───────────────────────────┐ │
│ │ BPF Program (검증 후 JIT 컴파일) │ │
│ │ │ │
│ │ Hook Points: │ │
│ │ ┌─────┐ ┌──────┐ ┌────────┐ ┌──────┐ │ │
│ │ │ XDP │ │tc/cls│ │ socket │ │kprobe│ │ │
│ │ │ │ │ │ │ │ │ │ │ │
│ │ └─────┘ └──────┘ └────────┘ └──────┘ │ │
│ │ │ │
│ │ BPF Maps (프로그램 간 데이터 공유) │ │
│ │ ┌──────────────────────────────────┐ │ │
│ │ │ Service Map │ Endpoint Map │ CT │ │ │
│ │ └──────────────────────────────────┘ │ │
│ └──────────────────────────────────────────┘ │
└──────────────────────────────────────────────────┘
10.2 eBPF가 iptables를 대체하는 이유
| 비교 항목 | iptables | eBPF |
|---|---|---|
| 실행 위치 | 커널 네트필터 프레임워크 | 커널 다양한 훅 포인트 |
| 규칙 매칭 | 선형 탐색 O(n) | 해시맵 O(1) |
| 업데이트 | 전체 체인 재작성 | 맵 엔트리만 업데이트 |
| 관찰성 | 제한적 (카운터만) | 풍부한 메트릭, 이벤트 |
| L7 처리 | 불가능 | 가능 (HTTP, DNS 등) |
| 연결 추적 | conntrack (공유) | BPF 자체 CT (효율적) |
| CPU 사용 | 높음 (대규모) | 낮음 |
| 확장성 | 10K 서비스에서 저하 | 100K+ 서비스도 안정 |
10.3 Cilium Hubble: eBPF 기반 관찰성
# Hubble CLI로 트래픽 모니터링
hubble observe --namespace production
# 특정 Pod의 트래픽 확인
hubble observe --pod production/frontend-abc123
# DNS 쿼리 모니터링
hubble observe --protocol DNS
# 차단된 트래픽 확인 (Network Policy)
hubble observe --verdict DROPPED
# HTTP 요청 모니터링 (L7)
hubble observe --protocol HTTP --http-method GET
# 출력 예시:
# TIMESTAMP SOURCE DESTINATION TYPE VERDICT
# Apr 14 09:15:23.456 prod/frontend-xxx prod/backend-yyy L4/TCP FORWARDED
# Apr 14 09:15:23.789 prod/backend-yyy prod/postgres-zzz L4/TCP FORWARDED
# Apr 14 09:15:24.012 prod/frontend-xxx prod/redis-aaa L4/TCP DROPPED
11. 네트워크 디버깅 실전 가이드
11.1 tcpdump를 이용한 패킷 캡처
# 에페메럴 컨테이너로 tcpdump 실행 (K8s 1.25+)
kubectl debug -it pod/my-app-xxx \
--image=nicolaka/netshoot \
--target=my-app-container \
-- tcpdump -i eth0 -nn port 8080
# 특정 호스트와의 통신 캡처
kubectl debug -it pod/my-app-xxx \
--image=nicolaka/netshoot \
--target=my-app-container \
-- tcpdump -i eth0 -nn host 10.1.2.3
# DNS 쿼리 캡처
kubectl debug -it pod/my-app-xxx \
--image=nicolaka/netshoot \
--target=my-app-container \
-- tcpdump -i eth0 -nn port 53
11.2 네트워크 연결 테스트
# netshoot Pod으로 종합 테스트
kubectl run netshoot --image=nicolaka/netshoot --rm -it -- bash
# TCP 연결 테스트
curl -v telnet://my-service:8080
# DNS 해상도 확인
dig my-service.default.svc.cluster.local
dig +trace my-service.default.svc.cluster.local
# MTU 확인
ping -M do -s 1472 target-pod-ip # 1472 + 28 = 1500
# 라우팅 테이블 확인
ip route show
# ARP 테이블 확인
ip neigh show
# iptables 규칙 확인 (노드에서)
iptables -t nat -L KUBE-SERVICES -n --line-numbers
11.3 Network Policy 트러블슈팅
# 1. 현재 적용된 Network Policy 확인
kubectl get networkpolicy -A
# 2. 특정 Pod에 적용되는 정책 확인
kubectl describe networkpolicy -n production
# 3. Cilium의 경우: 엔드포인트 정책 상태
cilium endpoint list
cilium policy get
# 4. 연결 테스트
kubectl exec -it frontend-pod -- curl -v http://backend-service:8080
# 5. DNS 허용 확인 (Egress Policy 시 필수)
kubectl exec -it my-pod -- nslookup kubernetes.default
# 흔한 실수들:
# - Egress Policy에서 DNS(UDP/TCP 53) 허용 누락
# - namespaceSelector 없이 podSelector만 사용
# (다른 네임스페이스의 Pod 매칭 안 됨)
# - policyTypes에 Egress 누락 (Egress 규칙이 있어도 무시됨)
11.4 DNS 문제 해결 체크리스트
# 1. CoreDNS Pod 상태 확인
kubectl get pods -n kube-system -l k8s-app=kube-dns
# 2. CoreDNS 로그 확인
kubectl logs -n kube-system -l k8s-app=kube-dns --tail=100
# 3. DNS 서비스 엔드포인트 확인
kubectl get endpoints kube-dns -n kube-system
# 4. resolv.conf 확인
kubectl exec my-pod -- cat /etc/resolv.conf
# 5. 직접 DNS 쿼리 테스트
kubectl exec my-pod -- nslookup kubernetes.default.svc.cluster.local 10.96.0.10
# 6. 외부 DNS 해상도 테스트
kubectl exec my-pod -- nslookup google.com
# DNS 성능 문제 시:
# - ndots:5를 ndots:2로 줄이기
# - NodeLocal DNSCache 사용
# - autopath 플러그인 활성화
12. 성능 튜닝
12.1 MTU 최적화
MTU 체인:
Pod (MTU) → 오버레이 (VXLAN -50, IPinIP -20) → 물리 NIC (MTU)
권장 설정:
- 물리 NIC: Jumbo Frame 9000 (가능한 경우)
- VXLAN 오버레이: 물리 MTU - 50 = 8950
- IPinIP 오버레이: 물리 MTU - 20 = 8980
- WireGuard 암호화: 물리 MTU - 60 = 8940
기본 1500 NIC:
- VXLAN: 1500 - 50 = 1450
- IPinIP: 1500 - 20 = 1480
# Calico MTU 설정
apiVersion: crd.projectcalico.org/v1
kind: FelixConfiguration
metadata:
name: default
spec:
mtu: 8950 # 물리 NIC 9000 - VXLAN 50
vxlanMTU: 8950
wireguardMTU: 8940
12.2 NodeLocal DNSCache
DNS 쿼리를 노드 로컬에서 캐싱하여 CoreDNS 부하를 줄입니다.
# NodeLocal DNSCache DaemonSet (간략화)
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: node-local-dns
namespace: kube-system
spec:
selector:
matchLabels:
k8s-app: node-local-dns
template:
spec:
containers:
- name: node-cache
image: registry.k8s.io/dns/k8s-dns-node-cache:1.23.0
args:
- "-localip"
- "169.254.20.10" # 링크 로컬 IP
- "-conf"
- "/etc/Corefile"
- "-upstreamsvc"
- "kube-dns"
NodeLocal DNSCache 동작:
Pod → 169.254.20.10 (NodeLocal) → CoreDNS (캐시 미스 시)
↓
로컬 캐시 히트 시 즉시 응답
효과:
- DNS 레이턴시 50% 이상 감소
- CoreDNS 부하 70-80% 감소
- conntrack 경합 해소
- UDP DNS 패킷 손실 방지
12.3 TCP 튜닝
# 노드 레벨 커널 파라미터 튜닝
# 연결 추적 테이블 크기
net.netfilter.nf_conntrack_max = 1048576
# TCP 버퍼 크기
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
# TCP 연결 재사용
net.ipv4.tcp_tw_reuse = 1
# SYN 백로그
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
# Keep-alive
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 10
13. 멀티클러스터 네트워킹
13.1 Cilium ClusterMesh
┌──────────────┐ ┌──────────────┐
│ Cluster A │ │ Cluster B │
│ (us-east-1) │ │ (eu-west-1) │
│ │ │ │
│ ┌────────┐ │ Tunnel │ ┌────────┐ │
│ │Cilium │◄─┼─────────┼─►│Cilium │ │
│ │Agent │ │ │ │Agent │ │
│ └────────┘ │ │ └────────┘ │
│ │ │ │
│ Pod CIDR: │ │ Pod CIDR: │
│ 10.1.0.0/16 │ │ 10.2.0.0/16 │
│ │ │ │
│ Service: │ │ Service: │
│ shared-db │ Global │ shared-db │
│ (annotated) │ Service │ (annotated) │
└──────────────┘ └──────────────┘
# Global Service 설정 (양쪽 클러스터에서)
apiVersion: v1
kind: Service
metadata:
name: shared-database
annotations:
service.cilium.io/global: "true"
service.cilium.io/shared: "true"
spec:
selector:
app: postgres
ports:
- port: 5432
14. 퀴즈
Q1. Kubernetes에서 모든 Pod가 고유한 IP를 가지며 NAT 없이 통신하는 이유는?
정답: Kubernetes 네트워킹 모델의 세 가지 기본 원칙 때문입니다:
- 모든 Pod는 고유한 IP 주소를 가진다
- 모든 Pod는 다른 모든 Pod에 NAT 없이 도달할 수 있다
- Pod가 보는 자신의 IP와 다른 Pod가 보는 IP가 동일하다
이 모델은 포트 매핑이나 NAT의 복잡성을 제거하여 서비스 디스커버리와 네트워크 정책을 단순하게 만듭니다. CNI 플러그인이 이 요구사항을 구현합니다.
Q2. eBPF가 iptables보다 Kubernetes 네트워킹에 더 적합한 이유는?
정답: iptables는 규칙을 선형으로 탐색(O(n))하며, 규칙 변경 시 전체 체인을 재작성해야 합니다. 10,000개 이상의 Service에서 심각한 성능 저하가 발생합니다.
eBPF는:
- 해시맵 기반 O(1) 조회로 Service 수에 관계없이 일정한 성능
- 맵 엔트리만 업데이트하므로 규칙 변경이 빠름
- L7 정책(HTTP 메서드/경로)을 커널에서 직접 처리
- Hubble로 풍부한 관찰성 제공
Cilium이 eBPF를 사용하여 kube-proxy를 완전히 대체합니다.
Q3. Gateway API가 Ingress를 대체하게 된 핵심 이유는?
정답: Ingress의 핵심 한계들:
- 비표준 어노테이션에 의존하여 컨트롤러마다 설정이 다름
- HTTP/HTTPS만 지원하여 TCP/UDP/gRPC 불가
- 단일 리소스로 인프라 관리자, 운영자, 개발자 간 역할 분리 어려움
- 트래픽 분할, 헤더 매칭 등 고급 기능이 표준에 없음
Gateway API는 GatewayClass/Gateway/Route의 계층적 리소스로 역할을 분리하고, 트래픽 분할, 헤더 매칭, 다중 프로토콜을 표준 스펙으로 지원합니다.
Q4. Network Policy에서 Egress 규칙 설정 시 가장 흔한 실수는?
정답: DNS(UDP/TCP 포트 53) 허용을 누락하는 것입니다.
Egress Policy를 설정하면 모든 아웃바운드 트래픽이 차단됩니다. DNS 쿼리도 차단되므로 Service 이름 해상도가 실패합니다. 반드시 kube-dns(CoreDNS) Pod로의 UDP/TCP 53 포트 Egress를 허용해야 합니다.
다른 흔한 실수:
- policyTypes에 Egress 명시 누락
- namespaceSelector 없이 다른 네임스페이스 Pod를 매칭하려는 시도
- CIDR 기반 규칙에서 Pod CIDR 범위 오류
Q5. ndots:5 설정이 외부 DNS 조회 성능을 저하시키는 이유와 해결책은?
정답: Kubernetes 기본 ndots:5 설정에서 "api.example.com"처럼 점이 5개 미만인 도메인을 조회하면, search 도메인을 순서대로 추가하여 먼저 시도합니다:
- api.example.com.default.svc.cluster.local (실패)
- api.example.com.svc.cluster.local (실패)
- api.example.com.cluster.local (실패)
- api.example.com (성공)
외부 DNS 하나를 조회하는데 불필요한 3번의 쿼리가 발생합니다.
해결책:
- Pod dnsConfig에서 ndots를 2로 줄이기
- FQDN 사용 (끝에 점 추가: api.example.com.)
- NodeLocal DNSCache로 캐시 활용
- CoreDNS autopath 플러그인 사용
15. 참고 자료
- Kubernetes Networking Model - Official Documentation
- Cilium Documentation - eBPF-based Networking
- Calico Documentation - Project Calico
- Gateway API - Official Specification
- CoreDNS - DNS for Service Discovery
- Network Policies - Kubernetes Documentation
- eBPF.io - Introduction to eBPF
- Hubble - Network Observability for Kubernetes
- IPVS-based kube-proxy - Kubernetes Blog
- CNI Specification - Container Network Interface
- NodeLocal DNSCache - Kubernetes Documentation
- Cilium ClusterMesh - Multi-Cluster Networking
- Life of a Packet in Kubernetes - Conference Talk
현재 단락 (1/876)
Kubernetes 네트워킹은 세 가지 기본 원칙 위에 구축됩니다.