-
Notifications
You must be signed in to change notification settings - Fork 0
KR_K8s_Network
- Pod의 veth pair는 호스트의 caliXXX 인터페이스와 연결되는데, caliXXX는 IP 없이 MAC 주소만 가진다.
- Pod가 목적지 Pod의 MAC 주소를 알기 위해 ARP 요청을 보내면, 호스트 커널의 ARP Proxy가 자신의 MAC 주소로 응답한다.
- Pod는 호스트 MAC으로 패킷을 보내고, 호스트의 라우팅 테이블이 목적지 Pod의 veth 인터페이스로 패킷을 전달한다. 이를 통해 L2 브리지 없이도 효율적인 Pod 간 통신이 가능하다.
VXLAN은 L2 over L3 터널링으로 동작한다.
- ① Pod의 원본 패킷(Inner Ethernet + IP) →
- ② VXLAN 헤더 추가(VNID 포함) →
- ③ 외부 UDP 헤더(포트 4789) 추가 →
- ④ 외부 IP 헤더(노드 IP) 추가 →
- ⑤ 외부 Ethernet 헤더.
MTU는 1500에서 50바이트 감소하여 1450이 되며, CPU 오버헤드(캡슐화/역캡슐화)와 대역폭 오버헤드(약 3%)가 발생한다. AWS VPC CNI는 이를 해결하기 위해 Pod가 VPC IP를 직접 사용한다.
- iptables는 순차 검색으로 O(n) 복잡도를 가지며, 1000개 이상 Service에서 성능 저하가 발생한다. Rule 업데이트 시 전체 재작성이 필요하다.
- IPVS는 해시 테이블로 O(1) 복잡도를 가지며, 대규모 클러스터에 적합하다. rr(라운드 로빈), lc(최소 연결), sh(소스 해싱) 등 다양한 로드밸런싱 알고리즘을 지원한다.
- 100개 이하 Service는 iptables, 그 이상은 IPVS를 권장한다. IPVS는 ipvsadm으로 실시간 연결 상태를 확인할 수 있다.
Service 생성 시 kube-proxy가 iptables 규칙을 생성한다.
- PREROUTING → KUBE-SERVICES → KUBE-SVC-XXX → KUBE-SEP-XXX (각 Pod Endpoint). ClusterIP 트래픽: DNAT로 Pod IP로 변환.
- NodePort 트래픽: KUBE-MARK-MASQ로 마킹 후 SNAT로 노드 IP로 변환(externalTrafficPolicy: Cluster), Local 설정 시 SNAT 생략.
- conntrack으로 연결 추적하여 응답 패킷도 올바르게 라우팅한다. iptables-save로 전체 규칙을 확인하고, KUBE-SERVICES 체인부터 추적한다.
- CoreDNS는
/etc/resolv.conf의 ndots(기본값 5) 설정에 따라 DNS 쿼리를 처리한다. - FQDN이 아닌 경우 search 도메인을 순차적으로 붙여 조회하므로, my-service 조회 시 최대 6번의 쿼리가 발생할 수 있다.
- ndots를 낮추거나 FQDN(
my-service.namespace.svc.cluster.local)을 사용하여 쿼리 수를 줄인다. - CoreDNS는 cache plugin으로 TTL 기반 캐싱을 하며, 응답 시간을 ms 단위로 단축한다. autopath plugin은 search 순서를 최적화한다.
- Calico는 BGP로 Pod CIDR 정보를 노드 간 광고한다.
- Full-mesh 모드는 모든 노드가 서로 피어링하여 N(N-1)/2 개의 연결이 생성되므로, 100개 이상 노드에서는 확장성 문제가 발생한다.
- Route Reflector 모드는 중앙 RR 노드가 라우팅 정보를 집중 관리하여 N개의 연결만 필요하다.
- RR은 SPOF 방지를 위해 다중화하며, Kubernetes Node를 RR로 설정하거나 전용 RR을 구성한다. calicoctl node status로 BGP 피어 상태를 확인한다.
- ① Cert-Manager로 CA 인증서 생성 →
- ② 각 Pod에 TLS Secret 마운트 →
- ③ 애플리케이션에서 TLS 핸드셰이크 구현.
NetworkPolicy로 특정 Label Pod만 통신 허용하고, PSP(Pod Security Policy) 또는 PSA(Pod Security Admission)로 권한 제한한다.
Linkerd/Istio 같은 Service Mesh는 이를 자동화하여 애플리케이션 코드 수정 없이 mTLS를 제공하며, 트래픽 암호화, 인증, 인가, Observability를 통합 관리한다.
- Ingress Controller는 Ingress 리소스를 감시하여 리버스 프록시 설정을 자동 생성한다. NGINX Ingress: 가장 성숙하고 안정적, annotation 기반 설정, ConfigMap으로 글로벌 설정.
- Traefik: 동적 설정, 자동 SSL(Let's Encrypt), 미들웨어 체인, Kubernetes CRD 지원.
- Istio Gateway: Service Mesh 통합, L7 라우팅, 트래픽 분할(Canary), mTLS, Observability 내장.
- 단순한 L7 라우팅은 NGINX, 동적 환경은 Traefik, 마이크로서비스 고급 기능은 Istio를 선택한다.
Legacy(in-tree)는 K8s 코어에 포함되어 업데이트가 느리고, Classic LB만 지원한다.
AWS LoadBalancer Controller(out-of-tree)는 ALB/NLB를 네이티브 지원하고, IP 모드, TargetGroupBinding, WAF 통합, Subnet Discovery(태그 기반), NLB의 Client IP 보존을 제공한다.
Ingress로 ALB 생성 시 annotation으로 세밀한 제어가 가능하며, Service로 NLB 생성 시 Instance/IP 타입을 선택할 수 있다. IP 타입은 Pod IP를 직접 등록하여 노드 홉을 제거한다.
-
AWS:
externalTrafficPolicy: Local+ NLB(Proxy Protocol v2)로 클라이언트 IP 유지. ALB는 X-Forwarded-For 헤더 사용. -
On-Premise:
externalTrafficPolicy: Local+ MetalLB(Layer2/BGP 모드). - X-Forwarded-For: HTTP 헤더, L7에서만 동작, 애플리케이션 파싱 필요, 여러 프록시 거칠 때 체인 형성.
-
Proxy Protocol: TCP 연결 시작 시 바이너리 헤더 전송, L4에서 동작, NGINX/HAProxy 지원, 성능 우수.
externalTrafficPolicy: Local은 불균형 문제가 있으므로 Pod가 모든 노드에 고르게 분산되도록 Anti-Affinity 설정이 필요하다.
💡 용어 설명:
- 네트워크 심화 질문들(Q11-Q20)에서 사용된 용어들(ARP Proxy, Overlay Network, VXLAN, IPIP, iptables, IPVS, CNI, Calico, BGP, CoreDNS, Ingress Controller, X-Forwarded-For, Proxy Protocol 등)에 대한
- 상세한 설명은 문서 상단의 주요 용어 통합 정리 > 네트워킹 섹션을 참고하세요.
- ① External Traffic → Ingress Controller/LoadBalancer(AWS ELB, NLB, ALB) 진입 →
- ② Service ClusterIP(Virtual IP, Endpoints 관리) →
- ③ kube-proxy(iptables KUBE-SERVICES 체인, DNAT로 Pod IP 변환, IPVS 모드는 해시 테이블 사용) →
- ④ CNI Network(Calico/Flannel이 라우팅, 같은 노드는 veth pair + ARP Proxy) →
- ⑤ Overlay Network(다른 노드는 VXLAN/IPIP 캡슐화, 터널링 인터페이스) →
- ⑥ Pod Container Port 도착. 디버깅: kubectl logs, tcpdump, iptables-save, Endpoints 확인, NetworkPolicy 검증.
💡 용어 설명:
- 위 답변에 사용된 네트워크 관련 용어들(ClusterIP, Endpoints, DNAT, IPVS, CNI, veth pair, ARP Proxy, Overlay Network, VXLAN, IPIP, 캡슐화, NetworkPolicy 등)에 대한
- 상세한 설명은 문서 상단의 주요 용어 통합 정리 > 네트워킹 섹션을 참고하세요.
CNI(Container Network Interface)는 kubelet과 네트워크 플러그인 간의 표준 인터페이스다.
- ① kubelet이 CRI(Container Runtime Interface)를 통해 컨테이너 생성 요청 →
- ② CRI가 네트워크 네임스페이스 생성 →
- ③ kubelet이 CNI Plugin 호출(ADD 커맨드) →
- ④ IPAM(IP Address Management) Plugin이 사용 가능한 IP 주소 할당 →
- ⑤ CNI Plugin이 veth pair 생성(한쪽은 Pod 네임스페이스, 다른쪽은 호스트) →
- ⑥ Pod쪽 인터페이스에 IP 설정 및 기본 라우트 추가 →
- ⑦ 호스트쪽 인터페이스를 브리지 또는 라우팅 테이블에 연결 →
- ⑧ CNI가 결과(IP, Gateway, DNS)를 kubelet에 반환.
- host-local: 로컬 파일에 IP 할당 정보 저장, 간단하지만 노드 간 동기화 없음.
- Calico IPAM: etcd 기반 분산 IP 관리, IP Pool 개념으로 효율적 할당.
- Whereabouts: etcd/Kubernetes API로 멀티 네트워크 IP 관리.
- CNI 로그:
/var/log/pods/또는/opt/cni/bin/실행 로그 - IP 할당 상태: Calico의 경우
calicoctl ipam show - 네트워크 네임스페이스:
ip netns list,nsenter명령으로 접근
Istio/Linkerd 같은 Service Mesh는 Envoy/Linkerd-proxy를 sidecar로 주입하여 모든 트래픽을 가로챈다.
- ① Mutating Admission Webhook이 Pod Spec 수정 →
- ② Init Container(
istio-init)가 iptables 규칙 설정 → - ③ Sidecar Proxy 컨테이너 추가 →
- ④ 애플리케이션 컨테이너와 함께 실행.
# Outbound 트래픽 가로채기
-A OUTPUT -p tcp -j ISTIO_OUTPUT
-A ISTIO_OUTPUT ! -d 127.0.0.1/32 -j ISTIO_REDIRECT
-A ISTIO_REDIRECT -p tcp -j REDIRECT --to-ports 15001
# Inbound 트래픽 가로채기
-A PREROUTING -p tcp -j ISTIO_INBOUND
-A ISTIO_INBOUND -p tcp --dport 80 -j REDIRECT --to-ports 15006- Outbound: 애플리케이션이 외부로 보내는 트래픽을 Envoy의 15001 포트로 리다이렉트
- Inbound: Pod로 들어오는 트래픽을 Envoy의 15006 포트로 리다이렉트
- Envoy가 mTLS, 라우팅, 로드밸런싱, Retry, Circuit Breaker 적용 후 실제 목적지로 전달
- Envoy 자신의 트래픽은 무한 루프 방지를 위해 제외
- Prometheus 메트릭 수집 포트(15090) 제외
-
traffic.sidecar.istio.io/excludeOutboundPortsannotation으로 특정 포트 제외
MTU(Maximum Transmission Unit)는 한 번에 전송 가능한 최대 패킷 크기다.
- 일반 Ethernet MTU: 1500 bytes
- VXLAN Overlay 오버헤드: 50 bytes (VXLAN header 8 + Outer IP 20 + Outer UDP 8 + Outer Ethernet 14)
- Pod의 실제 사용 가능 MTU: 1450 bytes
- 애플리케이션이 1500 byte 패킷 전송 시 단편화(fragmentation) 발생
- 대용량 데이터 전송 시 성능 저하
- TCP 연결이 중간에 끊김
-
PMTUD (Path MTU Discovery)실패 시 통신 불가
1. Pod MTU 자동 설정:
# Calico CNI 설정
apiVersion: projectcalico.org/v3
kind: FelixConfiguration
metadata:
name: default
spec:
mtuIfacePattern: "^((en|wl|ww|sl|ib)[opsx].*|(eth|wlan|wwan).*)"
vxlanMTU: 1450 # VXLAN 사용 시2. CNI에서 자동 감지:
- Calico:
FELIX_IPINIPMTU,FELIX_VXLANMTU환경변수 - Cilium:
tunnel-protocol설정에 따라 자동 계산 - AWS VPC CNI: ENI MTU 기반 자동 설정 (Jumbo Frame 지원 시 9001)
3. TCP MSS Clamping:
# iptables로 MSS 조정
iptables -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu4. 물리 인터페이스 Jumbo Frame 활성화:
# 호스트 네트워크에서 MTU 증가
ip link set dev eth0 mtu 9000# Pod 내부 MTU 확인
kubectl exec -it <pod> -- ip link show eth0
# PMTUD 테스트 (Don't Fragment 플래그로 ping)
ping -M do -s 1472 <destination> # 성공하면 MTU 1500 OK
ping -M do -s 1422 <destination> # VXLAN 환경에서 테스트NodePort Service는 모든 노드의 특정 포트로 외부 접근을 허용한다.
- ① 외부 클라이언트가 Node1:30080으로 요청 →
- ② Node1의 kube-proxy가 랜덤하게 Node2의 Pod B 선택 →
- ③ SNAT(Source NAT) 발생: 클라이언트 IP → Node1 IP로 변경 →
- ④ Pod B는 Node1을 클라이언트로 인식 (원본 IP 손실) →
- ⑤ 응답 패킷도 Node1을 거쳐 돌아감 (추가 홉)
- Node2의 Pod B가 클라이언트 IP로 직접 응답하면, 클라이언트는 Node1으로 요청했는데 Node2에서 응답이 와서 연결이 끊김 (비대칭 라우팅)
- SNAT로 Node1 IP를 유지하여 응답 경로 보장
apiVersion: v1
kind: Service
spec:
sessionAffinity: ClientIP # 같은 클라이언트 IP는 같은 Pod로
sessionAffinityConfig:
clientIP:
timeoutSeconds: 10800 # 3시간문제점:
- SNAT로 인해 모든 요청이 Node IP에서 오는 것처럼 보임
- 여러 클라이언트가 같은 Node를 통하면 모두 같은 Pod로 라우팅 (불균형)
1. externalTrafficPolicy: Local 사용:
spec:
type: NodePort
externalTrafficPolicy: Local # SNAT 제거, 원본 IP 유지- 장점: 클라이언트 IP 유지, Session Affinity 정상 동작, 홉 감소
- 단점: Pod 없는 노드로 요청 시 실패, 불균형 가능성
2. LoadBalancer + Proxy Protocol:
service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
service.beta.kubernetes.io/aws-load-balancer-proxy-protocol: "*"3. Ingress Controller 사용:
- L7에서 X-Forwarded-For 헤더로 원본 IP 전달
- Ingress가 Session Affinity 관리 (Cookie 기반)
# conntrack 테이블 확인
conntrack -L | grep <service-ip>
# iptables SNAT 규칙 확인
iptables -t nat -L KUBE-POSTROUTING -n -v
# Session Affinity 동작 확인
kubectl get endpoints <service> --watchDual-Stack은 IPv4와 IPv6를 동시에 지원하는 네트워크 구성이다 (K8s 1.23+ GA).
# kube-apiserver 플래그
--service-cluster-ip-range=10.96.0.0/12,fd00:1234::/112
--feature-gates=IPv6DualStack=true
# kube-controller-manager 플래그
--cluster-cidr=10.244.0.0/16,fd00:5678::/104
--service-cluster-ip-range=10.96.0.0/12,fd00:1234::/112
--node-cidr-mask-size-ipv4=24
--node-cidr-mask-size-ipv6=120apiVersion: v1
kind: Pod
metadata:
name: dual-stack-pod
spec:
containers:
- name: app
image: nginx
status:
podIPs:
- ip: 10.244.1.5 # IPv4
- ip: fd00:5678::5 # IPv6apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
ipFamilyPolicy: PreferDualStack # SingleStack | PreferDualStack | RequireDualStack
ipFamilies:
- IPv4
- IPv6
clusterIPs:
- 10.96.100.200 # Primary (IPv4)
- fd00:1234::200 # Secondary (IPv6)- Calico: Dual-Stack 완전 지원, IPv6 BGP 피어링
- Cilium: Native IPv6 지원, eBPF 기반 고성능
- Flannel: 제한적 지원 (VXLAN 모드만)
1. DNS 해석:
# CoreDNS가 AAAA 레코드 자동 생성
my-service.default.svc.cluster.local. # A + AAAA 반환2. 애플리케이션 호환성:
-
0.0.0.0:8080→::/0:8080또는[::]:8080(IPv6 바인딩) - Go:
net.Listen("tcp", ":8080")자동 Dual-Stack - Python:
socket.AF_INET6+IPV6_V6ONLY=0
3. NetworkPolicy:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
spec:
ingress:
- from:
- ipBlock:
cidr: 10.0.0.0/8 # IPv4
- ipBlock:
cidr: fd00::/8 # IPv64. Cloud Provider 제약:
- AWS: VPC IPv6 CIDR 필요, ELB는 Dual-Stack NLB만 지원
- GCP: Dual-Stack GKE 베타, 추가 설정 필요
- Azure: AKS Dual-Stack 프리뷰
- ① IPv4 Single-Stack 클러스터 구성
- ②
ipFamilyPolicy: PreferDualStack으로 점진적 IPv6 추가 - ③ 애플리케이션별 IPv6 호환성 검증
- ④
ipFamilyPolicy: RequireDualStack으로 강제 - ⑤ Primary IP Family를 IPv6로 전환 (ipFamilies 순서 변경)
# Pod IPv6 주소 확인
kubectl get pod <pod> -o jsonpath='{.status.podIPs}'
# IPv6 연결 테스트
kubectl exec -it <pod> -- curl -6 http://[fd00:1234::200]:80
# CNI IPv6 라우팅 확인
ip -6 route show