Container Orchestration/Kubernetes

Kubernetes 클러스터로의 외부 트래픽 흐름 완벽 가이드

Somaz 2026. 6. 4. 00:00
728x90
반응형

Overview

Kubernetes 클러스터에서 외부 트래픽이 어떻게 내부 Pod까지 도달하는지 이해하는 것은 운영과 트러블슈팅에 필수적이다. 외부에서 들어오는 요청은 단순히 Pod에 직접 연결되는 것이 아니라, 여러 네트워크 컴포넌트를 거쳐 최종 목적지에 도달한다.

 

외부 트래픽은 먼저 Ingress Controller나 LoadBalancer를 통해 클러스터로 진입한다. 이후 Service 객체의 ClusterIP로 전달되는데, 이 Service는 실제 IP가 아닌 가상 IP이다.

 

kube-proxy가 각 노드에서 iptables 또는 IPVS 규칙을 관리하며, Service IP로 오는 패킷을 실제 Pod IP로 DNAT(Destination NAT) 변환한다.

 

최종적으로 CNI(Container Network Interface)가 구성한 네트워크를 통해 해당 Pod의 Container 포트로 패킷이 전달된다. 만약 Pod가 다른 노드에 있다면, Overlay 네트워크(VXLAN 등)를 통해 노드 간 통신이 이루어진다.

 

이 가이드에서는 외부 트래픽이 Kubernetes 클러스터에 진입하는 순간부터 실제 컨테이너까지 도달하는 전체 과정을 단계별로 분석한다.

 

 

 

 

 

 

 

 

 


 

 

전체 트래픽 흐름 개요

 

외부 트래픽이 Kubernetes 클러스터 내부의 Pod까지 도달하는 과정은 크게 5단계로 나눌 수 있다.

외부 클라이언트 (인터넷)
        │
        ▼
┌─────────────────────────────────────┐
│  1단계: 진입점 (Entry Point)         │
│  - Ingress Controller (L7)          │
│  - LoadBalancer (L4)                │
│  - NodePort (노드 특정 포트)         │
└─────────────────────────────────────┘
        │
        ▼
┌─────────────────────────────────────┐
│  2단계: Service (가상 IP)            │
│  - ClusterIP: 10.96.x.x             │
│  - 실제 네트워크에 존재하지 않음      │
└─────────────────────────────────────┘
        │
        ▼
┌─────────────────────────────────────┐
│  3단계: kube-proxy (NAT 변환)        │
│  - iptables 또는 IPVS 규칙          │
│  - DNAT: Service IP → Pod IP        │
└─────────────────────────────────────┘
        │
        ▼
┌─────────────────────────────────────┐
│  4단계: CNI 네트워크                 │
│  - Pod Network (192.168.x.x)        │
│  - 노드 간 통신: Overlay/Routing     │
└─────────────────────────────────────┘
        │
        ▼
┌─────────────────────────────────────┐
│  5단계: 최종 목적지                  │
│  - Pod Container (targetPort)       │
└─────────────────────────────────────┘

 

 

 

 

 

 

1단계: 진입점 (Entry Point)

 

외부 트래픽이 Kubernetes 클러스터에 진입하는 방법은 크게 세 가지가 있다. 각각 다른 계층에서 동작하며, 사용 목적에 따라 선택한다.

외부 클라이언트
    │
    ▼
┌─────────────────────┐
│  Ingress Controller │ (L7) - HTTP/HTTPS
│  또는               │
│  LoadBalancer       │ (L4) - TCP/UDP
│  또는               │
│  NodePort           │ (노드의 특정 포트)
└─────────────────────┘

 

 

 

Ingress Controller (L7 로드밸런서)

 

Ingress Controller는 HTTP/HTTPS 트래픽을 처리하는 L7(Application Layer) 로드밸런서다. URL 경로, 호스트 헤더, TLS 종료 등 애플리케이션 레벨의 라우팅을 수행한다.

# Ingress 리소스 예시
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  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: web-service
            port:
              number: 80
  tls:
  - hosts:
    - app.example.com
    secretName: tls-secret

 

 

 

Ingress Controller의 패킷 흐름은 다음과 같다.

클라이언트 (203.0.113.10)
    │
    │ HTTPS 요청: app.example.com/api/users
    ▼
┌───────────────────────────────────────┐
│  External LoadBalancer               │
│  (AWS ALB / GCP GLB / MetalLB)       │
│  Public IP: 34.123.45.67             │
└───────────────────────────────────────┘
    │
    │ 포트 443 → NodePort 또는 LoadBalancer IP
    ▼
┌───────────────────────────────────────┐
│  Ingress Controller Pod              │
│  (nginx-ingress / traefik / etc)     │
│  ┌─────────────────────────────────┐ │
│  │ 1. TLS 종료 (SSL Termination)   │ │
│  │ 2. Host 헤더 확인               │ │
│  │ 3. URL 경로 매칭                │ │
│  │ 4. 백엔드 Service 선택          │ │
│  └─────────────────────────────────┘ │
└───────────────────────────────────────┘
    │
    │ HTTP 요청 → ClusterIP Service
    ▼
┌───────────────────────────────────────┐
│  api-service (ClusterIP)             │
│  10.96.100.50:80                     │
└───────────────────────────────────────┘

 

 

 

 

주요 Ingress Controller 종류는 다음과 같다.

Ingress Controller 특징 사용 환경
NGINX Ingress 가장 널리 사용, 안정적 범용
Traefik 자동 설정, Let's Encrypt 통합 소규모~중규모
HAProxy 고성능, 세밀한 설정 대규모 트래픽
AWS ALB Ingress AWS 네이티브 통합 AWS EKS
GKE Ingress GCP 네이티브 통합 GCP GKE
Istio Gateway 서비스 메시 통합 마이크로서비스

 

 

 

Gateway API (차세대 Ingress)

 

Gateway API는 Kubernetes SIG-Network에서 개발한 Ingress의 차세대 버전이다. 더 표현력이 풍부하고, 역할 기반의 리소스 분리를 제공하며, 확장성이 뛰어나다. Kubernetes 1.29부터 Gateway, GatewayClass, HTTPRoute가 GA(정식 버전)로 승격되었다.

┌─────────────────────────────────────────────────────────────┐
│                    Gateway API 리소스 계층                  │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  인프라 관리자 (Cluster Operator)                           │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  GatewayClass                                       │   │
│  │  - Gateway 구현체 정의 (nginx, envoy, istio 등)     │   │
│  │  - 클러스터 범위 리소스                              │   │
│  └─────────────────────────────────────────────────────┘   │
│                          │                                  │
│                          ▼                                  │
│  클러스터 운영자 (Platform Team)                            │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  Gateway                                            │   │
│  │  - 실제 로드밸런서/프록시 인스턴스                   │   │
│  │  - 리스너 (포트, 프로토콜, TLS) 설정                │   │
│  │  - 네임스페이스 범위 리소스                          │   │
│  └─────────────────────────────────────────────────────┘   │
│                          │                                  │
│                          ▼                                  │
│  애플리케이션 개발자 (App Team)                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  HTTPRoute / GRPCRoute / TCPRoute / TLSRoute       │   │
│  │  - 트래픽 라우팅 규칙 정의                          │   │
│  │  - 백엔드 Service 연결                              │   │
│  │  - 네임스페이스 범위 리소스                          │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

 

 

GatewayClass 예시

# 인프라 관리자가 생성 - NGINX Gateway Fabric 사용
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: nginx
spec:
  controllerName: gateway.nginx.org/nginx-gateway-controller

 

 

Gateway 예시

# 클러스터 운영자가 생성 - 실제 Gateway 인스턴스
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: main-gateway
  namespace: gateway-system
spec:
  gatewayClassName: nginx
  listeners:
  - name: http
    port: 80
    protocol: HTTP
    allowedRoutes:
      namespaces:
        from: All  # 모든 네임스페이스의 Route 허용
  - name: https
    port: 443
    protocol: HTTPS
    tls:
      mode: Terminate
      certificateRefs:
      - name: tls-secret
        kind: Secret
    allowedRoutes:
      namespaces:
        from: Selector
        selector:
          matchLabels:
            gateway-access: "true"

 

 

 

HTTPRoute 예시

# 애플리케이션 개발자가 생성 - 라우팅 규칙
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: api-routes
  namespace: app-namespace
spec:
  parentRefs:
  - name: main-gateway
    namespace: gateway-system
  hostnames:
  - "api.example.com"
  rules:
  # /v1/* 요청 → api-v1-service
  - matches:
    - path:
        type: PathPrefix
        value: /v1
    backendRefs:
    - name: api-v1-service
      port: 80
      weight: 100
  # /v2/* 요청 → api-v2-service (카나리 배포)
  - matches:
    - path:
        type: PathPrefix
        value: /v2
    backendRefs:
    - name: api-v2-stable
      port: 80
      weight: 90
    - name: api-v2-canary
      port: 80
      weight: 10
  # 헤더 기반 라우팅
  - matches:
    - headers:
      - name: X-Version
        value: beta
    backendRefs:
    - name: api-beta-service
      port: 80

 

 

 

Gateway API의 패킷 흐름은 다음과 같다.

클라이언트 (203.0.113.10)
    │
    │ HTTPS 요청: api.example.com/v1/users
    ▼
┌───────────────────────────────────────┐
│  External LoadBalancer               │
│  (Cloud LB / MetalLB)                │
│  Public IP: 34.123.45.67             │
└───────────────────────────────────────┘
    │
    ▼
┌───────────────────────────────────────┐
│  Gateway (main-gateway)              │
│  ┌─────────────────────────────────┐ │
│  │ Listener: HTTPS (443)           │ │
│  │ - TLS 종료                       │ │
│  │ - Host 매칭: api.example.com    │ │
│  └─────────────────────────────────┘ │
│               │                       │
│               ▼                       │
│  ┌─────────────────────────────────┐ │
│  │ HTTPRoute 매칭                  │ │
│  │ - Path: /v1/* → api-v1-service │ │
│  │ - Weight 기반 로드밸런싱         │ │
│  └─────────────────────────────────┘ │
└───────────────────────────────────────┘
    │
    ▼
┌───────────────────────────────────────┐
│  api-v1-service (ClusterIP)          │
│  10.96.100.50:80                     │
└───────────────────────────────────────┘

 

 

 

주요 Gateway API 구현체

구현체 특징 상태
NGINX Gateway Fabric NGINX 기반, 엔터프라이즈 지원 GA
Envoy Gateway Envoy 프록시 기반, CNCF 프로젝트 GA
Istio 서비스 메시 통합, 고급 트래픽 관리 GA
Cilium eBPF 기반, 고성능 GA
Traefik 자동 설정, 간편한 사용 GA
Kong API Gateway 기능 통합 GA
HAProxy Kubernetes Ingress 고성능, 세밀한 설정 Beta

 

 

 

Ingress vs Gateway API 비교

특성 Ingress Gateway API
API 성숙도 Stable (v1) Stable (v1) - 1.29+
역할 분리 단일 리소스 GatewayClass/Gateway/Route
프로토콜 지원 HTTP/HTTPS HTTP, HTTPS, TCP, UDP, gRPC, TLS
트래픽 분할 제한적 (어노테이션) 네이티브 지원 (weight)
헤더 기반 라우팅 구현체 의존 표준 스펙
확장성 어노테이션 기반 Policy Attachment
크로스 네임스페이스 제한적 네이티브 지원

 

 

 

LoadBalancer (L4 로드밸런서)

 

LoadBalancer 타입 Service는 L4(Transport Layer)에서 동작하며, TCP/UDP 트래픽을 직접 처리한다. 클라우드 환경에서는 CSP(Cloud Service Provider)의 로드밸런서가 자동으로 프로비저닝된다.

# LoadBalancer Service 예시
apiVersion: v1
kind: Service
metadata:
  name: web-service
  annotations:
    # AWS NLB 사용 시
    service.beta.kubernetes.io/aws-load-balancer-type: nlb
    # 내부 LB로 설정 시
    service.beta.kubernetes.io/aws-load-balancer-internal: "true"
spec:
  type: LoadBalancer
  selector:
    app: web
  ports:
  - name: http
    port: 80
    targetPort: 8080
  - name: https
    port: 443
    targetPort: 8443

 

 

 

LoadBalancer의 패킷 흐름은 다음과 같다.

클라이언트 (203.0.113.10)
    │
    │ TCP 연결: 34.123.45.67:80
    ▼
┌───────────────────────────────────────┐
│  Cloud Load Balancer                 │
│  (AWS NLB / GCP LB / Azure LB)       │
│  ┌─────────────────────────────────┐ │
│  │ External IP: 34.123.45.67       │ │
│  │ Health Check: /healthz          │ │
│  │ 로드밸런싱: Round Robin         │ │
│  └─────────────────────────────────┘ │
└───────────────────────────────────────┘
    │
    │ 트래픽 분산 → 각 노드의 NodePort
    ▼
┌─────────────┐  ┌─────────────┐  ┌─────────────┐
│   Node 1    │  │   Node 2    │  │   Node 3    │
│ :30080      │  │ :30080      │  │ :30080      │
└─────────────┘  └─────────────┘  └─────────────┘
    │                  │                  │
    └──────────────────┼──────────────────┘
                       │
                       ▼
              ┌─────────────────┐
              │ ClusterIP       │
              │ 10.96.100.50:80 │
              └─────────────────┘

 

 

 

 

 

NodePort (노드 포트 직접 노출)

 

NodePort는 모든 노드의 특정 포트(30000-32767)를 통해 Service를 외부에 노출한다. 가장 기본적인 외부 노출 방식이며, LoadBalancer의 기반이 된다.

# NodePort Service 예시
apiVersion: v1
kind: Service
metadata:
  name: web-service
spec:
  type: NodePort
  selector:
    app: web
  ports:
  - port: 80          # Service 포트 (ClusterIP에서 사용)
    targetPort: 8080  # Pod 컨테이너 포트
    nodePort: 30080   # 노드에서 열리는 포트 (생략 시 자동 할당)

 

 

 

NodePort의 패킷 흐름은 다음과 같다.

클라이언트 (203.0.113.10)
    │
    │ TCP 연결: 10.100.1.10:30080 (Node 1 IP)
    ▼
┌───────────────────────────────────────┐
│  Node 1 (10.100.1.10)                │
│  ┌─────────────────────────────────┐ │
│  │ kube-proxy가 30080 포트 리스닝  │ │
│  │                                 │ │
│  │ iptables/IPVS 규칙:            │ │
│  │ :30080 → ClusterIP:80          │ │
│  └─────────────────────────────────┘ │
└───────────────────────────────────────┘
    │
    │ DNAT: 10.100.1.10:30080 → 10.96.100.50:80
    ▼
┌───────────────────────────────────────┐
│  Service (ClusterIP)                 │
│  10.96.100.50:80                     │
└───────────────────────────────────────┘

 

 

 

 

 

 

진입점 비교 요약

특성 Ingress Gateway API LoadBalancer NodePort
OSI 계층 L7 (HTTP/HTTPS) L7 (HTTP/HTTPS/gRPC) L4 (TCP/UDP) L4 (TCP/UDP)
TLS 종료 지원 지원 별도 설정 필요 미지원
URL 라우팅 지원 지원 (고급) 미지원 미지원
트래픽 분할 제한적 네이티브 (weight) 미지원 미지원
역할 분리 단일 리소스 3단계 분리 단일 리소스 단일 리소스
외부 IP LB IP 1개 Gateway당 1개 Service당 1개 노드 IP 사용
비용 LB 1개 LB 1개 Service당 LB 무료
사용 사례 웹 애플리케이션 복잡한 라우팅, MSA TCP 서비스, DB 개발/테스트

 

 

 

 

 


 

 

 

 

 

2단계: Service (가상 IP)

 

진입점을 통해 들어온 트래픽은 Service의 ClusterIP로 전달된다. ClusterIP는 완전한 가상 IP로, 실제 네트워크 인터페이스에 존재하지 않는다.

 

 

 

Service 네트워크 이해

┌─────────────────────────────────────────────────────────┐
│                    Kubernetes Cluster                   │
│                                                         │
│  ┌─────────────────────────────────────────────────┐   │
│  │          Service Network (가상)                  │   │
│  │          CIDR: 10.96.0.0/12                     │   │
│  │                                                  │   │
│  │   ┌─────────────┐    ┌─────────────┐           │   │
│  │   │ web-service │    │ api-service │           │   │
│  │   │ 10.96.1.10  │    │ 10.96.1.20  │           │   │
│  │   └─────────────┘    └─────────────┘           │   │
│  │          │                  │                   │   │
│  │          └────────┬─────────┘                   │   │
│  │                   │                             │   │
│  └───────────────────┼─────────────────────────────┘   │
│                      │                                  │
│                      ▼                                  │
│  ┌─────────────────────────────────────────────────┐   │
│  │          Pod Network (실제)                     │   │
│  │          CIDR: 192.168.0.0/16                   │   │
│  │                                                  │   │
│  │   ┌─────────┐ ┌─────────┐ ┌─────────┐          │   │
│  │   │ Pod 1   │ │ Pod 2   │ │ Pod 3   │          │   │
│  │   │.1.10    │ │.1.11    │ │.2.10    │          │   │
│  │   └─────────┘ └─────────┘ └─────────┘          │   │
│  └─────────────────────────────────────────────────┘   │
│                                                         │
└─────────────────────────────────────────────────────────┘

 

 

 

 

 

Service와 Endpoints 관계

 

Service는 label selector를 통해 Pod를 선택하고, 선택된 Pod의 IP는 Endpoints(또는 EndpointSlice) 리소스에 등록된다.

# Service 정의
apiVersion: v1
kind: Service
metadata:
  name: web-service
spec:
  selector:
    app: web      # 이 label을 가진 Pod를 선택
  ports:
  - port: 80
    targetPort: 8080
 
 
 
# Endpoints 확인
kubectl get endpoints web-service

# 출력 예시
NAME          ENDPOINTS                                      AGE
web-service   192.168.1.10:8080,192.168.1.11:8080,192.168.2.10:8080   5m
 
 
 
# EndpointSlice 확인 (Kubernetes 1.21+)
kubectl get endpointslices -l kubernetes.io/service-name=web-service

# 출력 예시
NAME                  ADDRESSTYPE   PORTS   ENDPOINTS                           AGE
web-service-abc12     IPv4          8080    192.168.1.10,192.168.1.11,192.168.2.10   5m
  • Service가 트래픽을 받으면 Endpoints에 등록된 Pod IP 중 하나로 전달된다. 이 과정은 3단계의 kube-proxy가 담당한다.

 

 

 

 

 

3단계: kube-proxy (NAT 변환)

 

kube-proxy는 각 노드에서 실행되며, Service IP로 들어오는 트래픽을 실제 Pod IP로 변환(DNAT)하는 역할을 한다.

 

 

 

 

kube-proxy 동작 모드

kube-proxy는 세 가지 모드로 동작할 수 있다.

모드 방식 성능 특징
iptables iptables 규칙 중간 기본 모드, 선형 탐색
IPVS IPVS 커널 모듈 높음 해시 테이블, O(1) 조회
userspace 프록시 프로세스 낮음 레거시, 거의 사용 안 함
 
 
# kube-proxy 모드 확인
kubectl get cm kube-proxy -n kube-system -o yaml | grep mode

# 출력 예시
mode: ipvs

 

 

 

 

iptables 모드 패킷 흐름

클라이언트 요청: 10.96.100.50:80 (Service IP)
    │
    ▼
┌───────────────────────────────────────────────────────┐
│  Node (iptables 규칙)                                │
│                                                       │
│  PREROUTING chain:                                   │
│  ┌─────────────────────────────────────────────────┐ │
│  │ -A PREROUTING -j KUBE-SERVICES                  │ │
│  └─────────────────────────────────────────────────┘ │
│                      │                               │
│                      ▼                               │
│  KUBE-SERVICES chain:                               │
│  ┌─────────────────────────────────────────────────┐ │
│  │ -A KUBE-SERVICES -d 10.96.100.50/32 -p tcp     │ │
│  │    --dport 80 -j KUBE-SVC-XXXXXX               │ │
│  └─────────────────────────────────────────────────┘ │
│                      │                               │
│                      ▼                               │
│  KUBE-SVC-XXXXXX chain (로드밸런싱):                │
│  ┌─────────────────────────────────────────────────┐ │
│  │ -A KUBE-SVC-XXXXXX -m statistic --mode random  │ │
│  │    --probability 0.33 -j KUBE-SEP-AAAA         │ │
│  │ -A KUBE-SVC-XXXXXX -m statistic --mode random  │ │
│  │    --probability 0.50 -j KUBE-SEP-BBBB         │ │
│  │ -A KUBE-SVC-XXXXXX -j KUBE-SEP-CCCC            │ │
│  └─────────────────────────────────────────────────┘ │
│                      │                               │
│                      ▼                               │
│  KUBE-SEP-AAAA chain (DNAT):                        │
│  ┌─────────────────────────────────────────────────┐ │
│  │ -A KUBE-SEP-AAAA -p tcp -j DNAT                │ │
│  │    --to-destination 192.168.1.10:8080          │ │
│  └─────────────────────────────────────────────────┘ │
└───────────────────────────────────────────────────────┘
    │
    ▼
목적지 변환: 192.168.1.10:8080 (Pod IP)

 

 

 

 

IPVS 모드 패킷 흐름

 

IPVS 모드는 Linux 커널의 IPVS(IP Virtual Server) 모듈을 사용하여 더 효율적인 로드밸런싱을 수행한다.

클라이언트 요청: 10.96.100.50:80 (Service IP)
    │
    ▼
┌───────────────────────────────────────────────────────┐
│  Node (IPVS 규칙)                                    │
│                                                       │
│  kube-ipvs0 인터페이스:                              │
│  ┌─────────────────────────────────────────────────┐ │
│  │ inet 10.96.100.50/32 scope global kube-ipvs0   │ │
│  │ (모든 ClusterIP가 바인딩됨)                     │ │
│  └─────────────────────────────────────────────────┘ │
│                      │                               │
│                      ▼                               │
│  IPVS Virtual Server:                               │
│  ┌─────────────────────────────────────────────────┐ │
│  │ TCP  10.96.100.50:80 rr                        │ │
│  │   -> 192.168.1.10:8080    Masq    1    0    0  │ │
│  │   -> 192.168.1.11:8080    Masq    1    0    0  │ │
│  │   -> 192.168.2.10:8080    Masq    1    0    0  │ │
│  └─────────────────────────────────────────────────┘ │
│                      │                               │
│                      ▼                               │
│  해시 테이블 조회 → Real Server 선택 → DNAT          │
└───────────────────────────────────────────────────────┘
    │
    ▼
목적지 변환: 192.168.1.10:8080 (Pod IP)
 
 
 
# IPVS 규칙 확인
sudo ipvsadm -Ln

# 출력 예시
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  10.96.100.50:80 rr
  -> 192.168.1.10:8080            Masq    1      0          0
  -> 192.168.1.11:8080            Masq    1      0          0
  -> 192.168.2.10:8080            Masq    1      0          0

 

 

 

DNAT와 SNAT 상세

 

kube-proxy가 수행하는 NAT 변환을 상세히 살펴보면 다음과 같다.

요청 패킷 (DNAT - Destination NAT)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

[원본 패킷]
┌─────────────────────────────────────┐
│ Src IP: 203.0.113.10 (클라이언트)   │
│ Dst IP: 10.96.100.50 (Service)      │
│ Src Port: 54321                     │
│ Dst Port: 80                        │
└─────────────────────────────────────┘
                │
                │ DNAT 수행
                ▼
[변환된 패킷]
┌─────────────────────────────────────┐
│ Src IP: 203.0.113.10 (클라이언트)   │
│ Dst IP: 192.168.1.10 (Pod)          │  ← 목적지 변경
│ Src Port: 54321                     │
│ Dst Port: 8080                      │  ← 포트도 변경
└─────────────────────────────────────┘


응답 패킷 (SNAT - Source NAT)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

[원본 응답]
┌─────────────────────────────────────┐
│ Src IP: 192.168.1.10 (Pod)          │
│ Dst IP: 203.0.113.10 (클라이언트)   │
│ Src Port: 8080                      │
│ Dst Port: 54321                     │
└─────────────────────────────────────┘
                │
                │ SNAT 수행 (conntrack 기반)
                ▼
[변환된 응답]
┌─────────────────────────────────────┐
│ Src IP: 10.96.100.50 (Service)      │  ← 소스 변경
│ Dst IP: 203.0.113.10 (클라이언트)   │
│ Src Port: 80                        │  ← 포트도 변경
│ Dst Port: 54321                     │
└─────────────────────────────────────┘
  • conntrack(Connection Tracking)이 요청과 응답 패킷을 매칭하여 올바른 NAT 역변환을 수행한다.

 

 

 

 


 

 

 

 

 

4단계: CNI 네트워크

 

DNAT로 변환된 패킷은 CNI(Container Network Interface)가 구성한 네트워크를 통해 실제 Pod로 전달된다.

 

 

 

 

CNI의 역할

 

CNI 플러그인은 다음과 같은 역할을 수행한다.

  1. Pod에 네트워크 인터페이스(veth pair) 생성
  2. Pod에 IP 주소 할당 (IPAM)
  3. 노드 내 Pod 간 통신 경로 설정
  4. 노드 간 Pod 통신을 위한 Overlay/Routing 설정
 
┌─────────────────────────────────────────────────────────────┐
│  Node 1 (10.100.1.10)                                      │
│  Pod CIDR: 192.168.1.0/24                                  │
│                                                             │
│  ┌───────────────┐    ┌───────────────┐                    │
│  │    Pod A      │    │    Pod B      │                    │
│  │ 192.168.1.10  │    │ 192.168.1.11  │                    │
│  │    ┌─────┐    │    │    ┌─────┐    │                    │
│  │    │eth0 │    │    │    │eth0 │    │                    │
│  │    └──┬──┘    │    │    └──┬──┘    │                    │
│  └───────┼───────┘    └───────┼───────┘                    │
│          │                    │                             │
│          │ veth pair          │ veth pair                   │
│          │                    │                             │
│  ┌───────┴────────────────────┴───────┐                    │
│  │           cni0 (Bridge)            │                    │
│  │           192.168.1.1              │                    │
│  └────────────────┬───────────────────┘                    │
│                   │                                         │
│  ┌────────────────┴───────────────────┐                    │
│  │         eth0 (Node NIC)            │                    │
│  │         10.100.1.10                │                    │
│  └────────────────┬───────────────────┘                    │
└───────────────────┼─────────────────────────────────────────┘
                    │
                    │  Overlay Network (VXLAN/Geneve)
                    │  또는 BGP Routing
                    │
┌───────────────────┼─────────────────────────────────────────┐
│  Node 2 (10.100.1.11)                                      │
│  Pod CIDR: 192.168.2.0/24                                  │
│                   │                                         │
│  ┌────────────────┴───────────────────┐                    │
│  │         eth0 (Node NIC)            │                    │
│  │         10.100.1.11                │                    │
│  └────────────────┬───────────────────┘                    │
│                   │                                         │
│  ┌────────────────┴───────────────────┐                    │
│  │           cni0 (Bridge)            │                    │
│  │           192.168.2.1              │                    │
│  └───────┬────────────────────┬───────┘                    │
│          │                    │                             │
│  ┌───────┴───────┐    ┌───────┴───────┐                    │
│  │    Pod C      │    │    Pod D      │                    │
│  │ 192.168.2.10  │    │ 192.168.2.11  │                    │
│  └───────────────┘    └───────────────┘                    │
└─────────────────────────────────────────────────────────────┘

 

 

 

 

주요 CNI 플러그인 비교

CNI 네트워크 모드 특징 사용 환경
Calico BGP/VXLAN 높은 성능, Network Policy 온프레미스, 클라우드
Flannel VXLAN/host-gw 간단한 설정, 경량 소규모 클러스터
Cilium eBPF 고성능, L7 정책 대규모, 보안 중시
AWS VPC CNI 네이티브 VPC AWS 네이티브, 높은 성능 AWS EKS
Weave VXLAN 암호화 지원, 간단 멀티 클라우드

 

 

 

 

 

같은 노드 vs 다른 노드 통신

 

 

같은 노드 내 Pod 간 통신

Pod A (192.168.1.10) → Pod B (192.168.1.11)

┌─────────────────────────────────────┐
│  Node 1                             │
│                                     │
│  Pod A          Bridge         Pod B│
│  ┌─────┐       ┌─────┐       ┌─────┐│
│  │eth0 │──veth─│cni0 │─veth──│eth0 ││
│  └─────┘       └─────┘       └─────┘│
│                                     │
│  패킷 경로: eth0 → veth → cni0 → veth → eth0
│  (노드 외부로 나가지 않음)                │
└─────────────────────────────────────┘

 

 

 

다른 노드의 Pod 간 통신

Pod A (192.168.1.10) → Pod C (192.168.2.10)

┌─────────────┐                      ┌─────────────┐
│   Node 1    │                      │   Node 2    │
│             │                      │             │
│  ┌───────┐  │    Overlay/BGP       │  ┌───────┐  │
│  │ Pod A │  │  ┌──────────────┐    │  │ Pod C │  │
│  │.1.10  │──┼──│ VXLAN Tunnel │────┼──│.2.10  │  │
│  └───────┘  │  │ 또는 BGP      │    │  └───────┘  │
│             │  └──────────────┘    │             │
└─────────────┘                      └─────────────┘

패킷 경로:
1. Pod A eth0 → veth → cni0 → Node1 eth0
2. Node1 eth0 → [VXLAN 캡슐화] → 물리 네트워크
3. 물리 네트워크 → Node2 eth0 → [VXLAN 디캡슐화]
4. cni0 → veth → Pod C eth0

 

 

 

 

 

 

5단계: 최종 목적지 (Pod Container)

 

CNI 네트워크를 통해 전달된 패킷은 최종적으로 Pod 내의 Container로 도달한다.

┌───────────────────────────────────────────────────────────┐
│  Pod (192.168.1.10)                                       │
│  ┌─────────────────────────────────────────────────────┐  │
│  │  Network Namespace                                  │  │
│  │                                                     │  │
│  │  ┌─────────┐                                        │  │
│  │  │  eth0   │ ← 패킷 수신 (dst: 192.168.1.10:8080)     │  │
│  │  └────┬────┘                                        │  │
│  │       │                                             │  │
│  │       │ iptables/네트워크 스택                         │  │
│  │       │                                             │  │
│  │       ▼                                             │  │
│  │  ┌─────────────────────────────────────────────┐    │  │
│  │  │              localhost                      │  │ │
│  │  │                                             │  │ │
│  │  │  ┌─────────────┐    ┌─────────────┐         │  │ │
│  │  │  │ Container 1 │    │ Container 2 │         │  │ │
│  │  │  │ (App)       │    │ (Sidecar)   │         │  │ │
│  │  │  │ :8080       │    │ :9090       │         │  │ │
│  │  │  └─────────────┘    └─────────────┘         │  │ │
│  │  │                                             │  │ │
│  │  │  같은 Pod 내 컨테이너는 localhost로 통신          │  │ │
│  │  └─────────────────────────────────────────────┘  │ │
│  └─────────────────────────────────────────────────────┘  │
└───────────────────────────────────────────────────────────┘
  • Pod 내 컨테이너들은 동일한 Network Namespace를 공유하므로 localhost를 통해 서로 통신할 수 있다.

 

 

 

 


 

 

 

 

전체 패킷 흐름 요약

 

외부 클라이언트에서 Pod까지의 전체 패킷 흐름을 하나의 다이어그램으로 정리하면 다음과 같다.

┌─────────────────────────────────────────────────────────────────────────┐
│                        전체 패킷 흐름 요약                                   │
└─────────────────────────────────────────────────────────────────────────┘

외부 클라이언트 (203.0.113.10)
    │
    │ ① HTTPS 요청: app.example.com/api (203.0.113.10:54321 → 34.123.45.67:443)
    ▼
┌─────────────────────────────────────┐
│ 1단계: Ingress Controller            │
│ - TLS 종료                           │
│ - Host/Path 라우팅                    │
│ - 백엔드 Service 선택                  │
└─────────────────────────────────────┘
    │
    │ ② HTTP 요청: (Ingress Pod IP:port → 10.96.100.50:80)
    ▼
┌─────────────────────────────────────┐
│ 2단계: Service (ClusterIP)           │
│ - 가상 IP: 10.96.100.50              │
│ - Endpoints: Pod IP 목록 관리         │
└─────────────────────────────────────┘
    │
    │ ③ DNAT 수행 (kube-proxy)
    ▼
┌──────────────────────────────────────┐
│ 3단계: kube-proxy (iptables/IPVS)     │
│ - 10.96.100.50:80 → 192.168.1.10:8080│
│ - 로드밸런싱 (RR/LC/SH)                 │
│ - conntrack 기록                      │
└──────────────────────────────────────┘
    │
    │ ④ Pod Network 전달
    ▼
┌─────────────────────────────────────┐
│ 4단계: CNI 네트워크                    │
│ - 같은 노드: Bridge 통과               │
│ - 다른 노드: Overlay/Routing          │
└─────────────────────────────────────┘
    │
    │ ⑤ 컨테이너 포트 도달
    ▼
┌─────────────────────────────────────┐
│ 5단계: Pod Container                 │
│ - 192.168.1.10:8080                 │
│ - 애플리케이션 처리                     │
└─────────────────────────────────────┘
    │
    │ ⑥ 응답 (역순으로 SNAT 적용)
    ▼
외부 클라이언트 (203.0.113.10)

 

 

 

패킷 분석 실습

실제 환경에서 패킷 흐름을 확인하는 방법을 알아보자.

 

 

 

테스트 환경 구성

# 테스트 네임스페이스 생성
kubectl create namespace traffic-test

# 백엔드 Pod 배포
kubectl create deployment nginx --image=nginx:latest --replicas=3 -n traffic-test

# Service 생성
kubectl expose deployment nginx --port=80 --target-port=80 --name=nginx-service -n traffic-test

# 클라이언트 Pod 생성
kubectl run client-pod --image=nicolaka/netshoot -n traffic-test --command -- sleep infinity

 

 

 

 

각 단계별 패킷 캡처

# 1. Service IP 확인
SERVICE_IP=$(kubectl get svc nginx-service -n traffic-test -o jsonpath='{.spec.clusterIP}')
echo "Service IP: $SERVICE_IP"

# 2. 노드에서 IPVS 규칙 확인
ssh <node> "sudo ipvsadm -Ln | grep -A 5 $SERVICE_IP"

# 3. 클라이언트 Pod에서 tcpdump
kubectl exec -it client-pod -n traffic-test -- tcpdump -i any -nn host $SERVICE_IP

# 4. 노드에서 tcpdump (DNAT 전후 확인)
ssh <node> "sudo tcpdump -i any -nn host $SERVICE_IP"

# 5. 백엔드 Pod에서 tcpdump
kubectl exec -it <nginx-pod> -n traffic-test -- tcpdump -i any -nn port 80

 

 

 

트래픽 생성 및 분석

# 클라이언트 Pod에서 요청 생성
kubectl exec -it client-pod -n traffic-test -- curl -v http://nginx-service

# 여러 요청으로 로드밸런싱 확인
kubectl exec -it client-pod -n traffic-test -- bash -c "for i in {1..10}; do curl -s http://nginx-service | head -1; done"

 

 

 

 

 

트러블슈팅 가이드

 

외부 트래픽 흐름에서 발생할 수 있는 주요 문제와 해결 방법을 정리한다.

 

 

문제 1: Service에 연결할 수 없음

# 진단 순서
# 1. Service 존재 확인
kubectl get svc -n <namespace>

# 2. Endpoints 확인 (Pod가 등록되었는지)
kubectl get endpoints <service-name> -n <namespace>

# 3. Pod 상태 확인
kubectl get pods -n <namespace> -l <selector>

# 4. kube-proxy 상태 확인
kubectl get pods -n kube-system -l k8s-app=kube-proxy

# 5. IPVS/iptables 규칙 확인
sudo ipvsadm -Ln | grep <service-ip>

 

 

 

 

문제 2: Ingress가 동작하지 않음

# 1. Ingress Controller Pod 상태
kubectl get pods -n ingress-nginx

# 2. Ingress 리소스 확인
kubectl describe ingress <ingress-name> -n <namespace>

# 3. Ingress Controller 로그
kubectl logs -n ingress-nginx -l app.kubernetes.io/name=ingress-nginx

# 4. Backend Service 연결 테스트
kubectl exec -it <ingress-pod> -n ingress-nginx -- curl http://<service-ip>

 

 

 

문제 3: 간헐적 연결 실패

# 1. 백엔드 Pod 상태 확인 (Readiness)
kubectl get pods -o wide | grep -v Running

# 2. EndpointSlice 상태 확인
kubectl get endpointslices -l kubernetes.io/service-name=<service>

# 3. conntrack 테이블 확인
sudo conntrack -L -d <service-ip> | head -20

# 4. 네트워크 정책 확인
kubectl get networkpolicies -n <namespace>

 

 

 

 

 

 


 

 

 

 

마무리

 

이 글에서는 외부 트래픽이 Kubernetes 클러스터에 진입하여 최종 Pod까지 도달하는 전체 과정을 단계별로 분석했다.

 

 

 

 

핵심 내용 요약

 

진입점 선택

  • Ingress Controller: L7 라우팅, TLS 종료, URL 기반 라우팅이 필요한 웹 애플리케이션
  • Gateway API: 복잡한 라우팅, 트래픽 분할, 역할 기반 관리가 필요한 마이크로서비스 환경 (차세대 표준)
  • LoadBalancer: L4 수준의 TCP/UDP 서비스 노출
  • NodePort: 개발/테스트 환경 또는 외부 로드밸런서 직접 연동

 

 

Gateway API의 장점

  • 역할 기반 리소스 분리 (GatewayClass → Gateway → Route)
  • 네이티브 트래픽 분할 (weight 기반 카나리 배포)
  • 다양한 프로토콜 지원 (HTTP, HTTPS, gRPC, TCP, UDP)
  • 크로스 네임스페이스 라우팅 지원
  • 표준화된 확장 방식 (Policy Attachment)

 

 

Service의 역할

  • ClusterIP는 가상 IP로 실제 네트워크에 존재하지 않음
  • Endpoints/EndpointSlice가 실제 Pod IP 목록 관리
  • label selector를 통한 동적 Pod 선택

 

 

kube-proxy의 NAT 변환

  • Service IP → Pod IP로 DNAT 수행
  • iptables 모드: 선형 탐색, 소규모 클러스터에 적합
  • IPVS 모드: 해시 테이블 기반 O(1) 조회, 대규모 클러스터에 적합
  • conntrack을 통한 응답 패킷 SNAT

 

 

CNI 네트워크

  • Pod에 네트워크 인터페이스와 IP 할당
  • 같은 노드: Bridge를 통한 직접 통신
  • 다른 노드: Overlay(VXLAN) 또는 BGP Routing

 

 

 

이러한 이해를 바탕으로 Kubernetes 네트워크 문제를 체계적으로 분석하고 해결할 수 있다. 특히 tcpdump를 활용한 패킷 분석과 각 컴포넌트의 상태 확인을 통해 문제의 원인을 정확히 파악하는 것이 중요하다.

 

 

 

 

 

 

 

 


Reference

 

 

 

Somaz | DevOps Engineer | Kubernetes & Cloud Infrastructure Specialist

728x90
반응형