이것은 사용자가 2025-1-2 13:08에 https://learnk8s.io/kubernetes-services-and-load-balancing을(를) 위해 저장한 이중 언어 스냅샷 페이지로, 몰입형 번역에 의해 제공된 이중 언어 지원이 있습니다. 저장하는 방법을 알아보세요?
Gulcan Topcu
Gulcan Topcu

Kubernetes networking: service, kube-proxy, load balancing
Kubernetes 네트워킹: 서비스, kube-proxy, 로드 밸런싱

October 2024


Kubernetes networking: service, kube-proxy, load balancing

TL;DR: This article explores Kubernetes networking, focusing on Services, kube-proxy, and load balancing.
TL;DR: 이 기사는 Kubernetes 네트워킹을 탐구하며, Services, kube-proxy 및 로드 밸런싱에 중점을 둡니다.

It covers how pods communicate within a cluster, how Services direct traffic, and how external access is managed.
클러스터 내에서 파드가 어떻게 통신하는지, 서비스가 트래픽을 어떻게 유도하는지, 외부 접근이 어떻게 관리되는지를 다룹니다.

You will explore ClusterIP, NodePort, and LoadBalancer service types and dive into their implementations using iptables rules.
ClusterIP, NodePort, LoadBalancer 서비스 유형을 탐색하고 iptables 규칙을 사용한 구현에 대해 깊이 있게 살펴보겠습니다.

You will also discuss advanced topics like preserving source IPs, handling terminating endpoints, and integrating with cloud load balancers.
소스 IP 보존, 종료 엔드포인트 처리, 클라우드 로드 밸런서와의 통합과 같은 고급 주제에 대해서도 논의할 것입니다.

Table of contents  목차

Deploying a two-tier application
두 계층 애플리케이션 배포

Consider a two-tier application consisting of two tiers: the frontend tier, which is a web server that serves HTTP responses to browser requests, and the backend tier, which is a stateful API containing a list of job titles.
두 계층으로 구성된 이중 계층 애플리케이션을 고려하십시오: 프론트엔드 계층은 브라우저 요청에 대한 HTTP 응답을 제공하는 웹 서버이고, 백엔드 계층은 직무 목록을 포함하는 상태 저장 API입니다.

A front-end and backend application in Kubernetes

The front end calls the backend to display a job title and logs which pod processed the request.
프론트 엔드는 백엔드에 호출하여 직무 제목을 표시하고 요청을 처리한 포드를 기록합니다.

Let's deploy and expose those applications in Kubernetes.
Kubernetes에서 해당 애플리케이션을 배포하고 노출합시다.

Deploying the Backend Pods
백엔드 팟 배포

This is what backend-deployment.yaml looks like.
이것이 backend-deployment.yaml 의 모습입니다.

Notice that we will include replicas: 1 to indicate that I want to deploy only one pod.
replicas: 1 를 포함하여 단일 포드만 배포하고 싶다는 것을 알 수 있습니다.

backend-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: backend
  template:
    metadata:
      labels:
        app: backend
    spec:
      containers:
      - name: backend
        image: ghcr.io/learnk8s/jobs-api
        ports:
          - containerPort: 3000

You can submit the file to the cluster with:
클러스터에 파일을 제출할 수 있습니다:

bash

kubectl apply -f backend-deployment.yaml
deployment.apps/backend-deployment created

Great!  좋아요!

Now, you have a deployment of a single pod running the backend API.
이제 백엔드 API를 실행하는 단일 포드의 배포가 있습니다.

Verify this:  이것을 확인하십시오:

bash

kubectl get deployment
NAME                 READY   UP-TO-DATE   AVAILABLE
backend-deployment   1/1     1            1

The command above provides deployment information, but it'd be great to get information about the individual pod, like the IP address or node it was assigned to.
위의 명령은 배포 정보를 제공하지만, 할당된 IP 주소나 노드와 같은 개별 포드에 대한 정보를 얻는 것이 좋습니다.

Inspecting the backend deployment
백엔드 배포 검사

You can retrieve the pod's IP address by appending -l app=backend to get only pods matching our deployment and -o wide so that the output includes the pod IP address.
배포와 일치하는 포드만 가져오기 위해 -l app=backend 를 추가하고 출력에 포드 IP 주소가 포함되도록 -o wide 을 추가하여 포드의 IP 주소를 검색할 수 있습니다.

bash

kubectl get pod -l app=backend -o wide
NAME                                  READY   STATUS    IP           NODE
backend-deployment-6c84d55bc6-v7tcq   1/1     Running   10.244.1.2   minikube-m02

Great!  좋아요!

Now you know that the pod IP address is 10.244.1.2.
이제 포드 IP 주소가 10.244.1.2 임을 알게 되었습니다.

But how will the frontend pods reach this IP address when they need to call the backend API?
하지만 프론트엔드 팟이 백엔드 API를 호출해야 할 때 이 IP 주소에 어떻게 도달할 수 있을까요?

Exposing the backend pods within the cluster with a Service
클러스터 내에서 서비스로 백엔드 포드를 노출하기

A Service in Kubernetes allows pods to be easily discoverable and reachable across the pod network.
Kubernetes의 서비스는 포드 네트워크 전반에 걸쳐 포드를 쉽게 발견하고 접근할 수 있도록 합니다.

Exposing the backend application with a ClusterIP Service

To enable the frontend pods to discover and reach the backend, let's expose the backend pod through a Service.
프론트엔드 팟이 백엔드를 발견하고 접근할 수 있도록 백엔드 팟을 서비스로 노출합시다.

This is what the service looks like:
서비스는 다음과 같습니다:

backend-service.yaml

apiVersion: v1
kind: Service
metadata:
  name: backend-service
spec:
  selector:
    app: backend
  ports:
    - name: backend
      protocol: TCP
      port: 3000
      targetPort: 3000

You can create the resource with the following command:
다음 명령어로 리소스를 생성할 수 있습니다:

bash

kubectl apply -f backend-service.yaml
service/backend-service created

Verify the creation of this service:
이 서비스의 생성 여부를 확인하십시오:

bash

kubectl get service
NAME              TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)
backend-service   ClusterIP   10.96.5.81   <none>        3000/TCP

The service's IP address is 10.96.5.81, exposing a single port: 3000.
서비스의 IP 주소는 10.96.5.81 이며, 단일 포트를 노출합니다: 3000 .

But how do the frontend pods know they should reach that IP address?
하지만 프론트엔드 팟이 그 IP 주소에 도달해야 한다는 것을 어떻게 알까요?

And what if the IP address changes?
IP 주소가 변경되면 어떻게 되나요?

DNS Resolution for the backend service
백엔드 서비스에 대한 DNS 해상도

Instead of reaching the Service by its IP address, you can assign a friendly name and rely on the DNS to translate it to an IP address.
서비스의 IP 주소에 접근하는 대신, 친숙한 이름을 할당하고 DNS가 이를 IP 주소로 변환하도록 의존할 수 있습니다.

And that's precisely what happens when you create a Service in Kubernetes: a DNS record is created with the Fully Qualified Domain Name (FQDN) of <service-name>.<namespace>.svc.cluster.local.
그리고 그것이 바로 Kubernetes에서 서비스를 생성할 때 발생하는 일입니다: <service-name>.<namespace>.svc.cluster.local 의 완전한 도메인 이름(FQDN)으로 DNS 레코드가 생성됩니다.

You can access services and pods using DNS names instead of IP addresses.
DNS 이름을 사용하여 IP 주소 대신 서비스와 포드에 접근할 수 있습니다.

CoreDNS is the component that resolves these DNS names to their corresponding IP addresses.
CoreDNS는 이러한 DNS 이름을 해당 IP 주소로 변환하는 구성 요소입니다.

It is deployed as a ClusterIP service named kube-dns and managed by a Deployment in the kube-system namespace.
ClusterIP 서비스로 배포되며 kube-dns 라는 이름을 가지고 Deployment 에 의해 kube-system 네임스페이스에서 관리됩니다.

When a pod needs to resolve a service name, it sends a DNS query to the kube-dns service.
포드가 서비스 이름을 확인해야 할 때, kube-dns 서비스에 DNS 쿼리를 보냅니다.

CoreDNS processes the request and resolves the service name to the appropriate ClusterIP.
CoreDNS는 요청을 처리하고 서비스 이름을 적절한 ClusterIP 로 해결합니다.

  • The front-end pod doesn't know the IP address of the Service, but all services can be called using their Fully Qualified Domain Name (FQDN).
    1/4

    The front-end pod doesn't know the IP address of the Service, but all services can be called using their Fully Qualified Domain Name (FQDN).
    프론트엔드 포드는 서비스의 IP 주소를 알지 못하지만, 모든 서비스는 완전한 도메인 이름(FQDN)을 사용하여 호출할 수 있습니다.

  • The application will query CoreDNS and swap the FQDN for an IP address.
    2/4

    The application will query CoreDNS and swap the FQDN for an IP address.

  • Depending on the type of Service, CoreDNS will return the appropriate IP address.
    3/4

    Depending on the type of Service, CoreDNS will return the appropriate IP address.

  • Finally, the application can use that IP address to connect to the Service.
    4/4

    Finally, the application can use that IP address to connect to the Service.

You can inspect the kube-dns service with:
kube-dns 서비스를 다음과 같이 검사할 수 있습니다:

bash

kubectl get svc -n kube-system kube-dns
NAME       TYPE        CLUSTER-IP   PORT(S)
kube-dns   ClusterIP   10.96.0.10   53/UDP,53/TCP

Kubelet configures each pod's /etc/resolv.conf file.
Kubelet은 각 포드의 /etc/resolv.conf 파일을 구성합니다.

This file specifies how DNS queries are resolved, including the nameservers to use and the search domains to help expand queries.
이 파일은 DNS 쿼리가 어떻게 해결되는지를 지정하며, 사용할 네임서버와 쿼리를 확장하는 데 도움이 되는 검색 도메인을 포함합니다.

Check the contents of a pod's /etc/resolv.conf file:

bash

kubectl exec -it pod-name -- cat /etc/resolv.conf
nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local

Typically, this file contains a DNS search list, including the pod's namespace and the cluster's default domain.
일반적으로 이 파일에는 DNS 검색 목록이 포함되어 있으며, 여기에는 포드의 네임스페이스와 클러스터의 기본 도메인이 포함됩니다.

For example, if a pod in the default namespace queries for kubernetes, the system appends default.svc.cluster.local, which resolves to the ClusterIP of the Kubernetes service.
예를 들어, 기본 네임스페이스의 포드가 kubernetes 를 쿼리하면 시스템은 default.svc.cluster.local 을 추가하여 Kubernetes 서비스의 ClusterIP 로 해결됩니다.

After CoreDNS resolves the service name to a ClusterIP, the application can communicate with the service using that IP address.
CoreDNS가 서비스 이름을 ClusterIP 로 해결한 후, 애플리케이션은 해당 IP 주소를 사용하여 서비스와 통신할 수 있습니다.

Let's review the above with an example.
위의 내용을 예를 들어 살펴보겠습니다.

Create a pod to perform a DNS lookup of backend-service:
backend-service 의 DNS 조회를 수행하는 포드를 생성합니다:

bash

kubectl run -i dnsutils \
  --image=gcr.io/kubernetes-e2e-test-images/dnsutils:1.3 \
  --rm \
  -- nslookup backend-service.jobs.svc.cluster.local
Server:   10.96.0.10
Address:  10.96.0.10#53

Name: backend-service.jobs.svc.cluster.local
Address: 10.96.5.81

The service can be resolved by its name "backend-service" to its IP address 10.96.5.81.
서비스는 이름 "backend-service"로 IP 주소 10.96.5.81 로 해석될 수 있습니다.

Any other pod in Kubernetes can target this service using its name.
Kubernetes의 다른 모든 포드는 이 서비스를 이름을 사용하여 타겟팅할 수 있습니다.

If you request that IP address, the traffic reaches the pod:
해당 IP 주소를 요청하면 트래픽이 포드에 도달합니다:

bash

kubectl run curl-client --rm -i --tty \
  --image=curlimages/curl -- /bin/sh
curl 10.96.5.81:3000
{"job":"Instructor at Learnk8s","pod":"backend-deployment-5df766bf5c-xfdng"}

The backend responds with a JSON object containing a job title and the backend pod that processed the request.
백엔드는 요청을 처리한 백엔드 포드와 직무 제목이 포함된 JSON 객체로 응답합니다.

Service discovery and DNS resolution work, but it is unclear how traffic is directed to the service and forwarded to the backend pods.
서비스 검색 및 DNS 해상도는 작동하지만, 트래픽이 서비스로 어떻게 전달되고 백엔드 포드로 전달되는지는 불분명합니다.

Endpoints and Services  엔드포인트 및 서비스

Kubernetes services don't exist in the infrastructure: there's no process for listening to incoming traffic and distributing it to the pods.
Kubernetes 서비스는 인프라에 존재하지 않습니다: 들어오는 트래픽을 수신하고 이를 포드에 분배하는 프로세스가 없습니다.

There's no load balancer.
로드 밸런서가 없습니다.

Services are just definitions of how traffic should be forwarded to pods.
서비스는 트래픽이 포드로 어떻게 전달되어야 하는지를 정의한 것입니다.

To confirm it, you can SSH into your cluster nodes and execute:
클러스터 노드에 SSH로 접속하여 다음을 실행할 수 있습니다:

bash

netstat -ntlp | grep 10.96.5.81
netstat -ntlp | grep 3000

These commands will not return results because 10.96.5.81 is a virtual IP managed by Kubernetes and is not tied to a specific process.
이 명령은 10.96.5.81 가 Kubernetes에 의해 관리되는 가상 IP이며 특정 프로세스에 연결되어 있지 않기 때문에 결과를 반환하지 않습니다.

So, how does it work?
그래서, 어떻게 작동하나요?

When you submit a Service to the control plane, the Endpoint controller evaluates the service's selector and notes all pods' IP addresses that match.
서비스를 제어 평면에 제출하면 엔드포인트 컨트롤러가 서비스의 선택기를 평가하고 일치하는 모든 포드의 IP 주소를 기록합니다.

The result is stored in an Endpoint object.
결과는 Endpoint 객체에 저장됩니다.

  • Usually, you don't create endpoints manually. Instead, you create Services which are stored in etcd.
    1/3

    Usually, you don't create endpoints manually. Instead, you create Services which are stored in etcd.
    보통, 엔드포인트를 수동으로 생성하지 않습니다. 대신, etcd에 저장되는 서비스를 생성합니다.

  • For each Service, the Endpoint controller evaluates the service selector and collects the matching pod IP addresses.
    2/3

    For each Service, the Endpoint controller evaluates the service selector and collects the matching pod IP addresses.

  • An Endpoint object is created in etcd with all the IP addresses and port pairs.
    3/3

    An Endpoint object is created in etcd with all the IP addresses and port pairs.

Let's verify that this is the case:
이것이 사실인지 확인해 봅시다:

bash

kubectl get endpoints,services
NAME                        ENDPOINTS
endpoints/backend-service   10.244.1.2:3000
endpoints/kubernetes        192.168.49.2:8443

NAME                      TYPE        CLUSTER-IP     PORT(S)
service/backend-service   ClusterIP   10.96.5.81     3000/TCP
service/kubernetes        ClusterIP   10.96.0.1      443/TCP

The above output shows that an Endpoints object named backend-service was created.
위의 출력은 backend-service 라는 이름의 Endpoints 객체가 생성되었음을 보여줍니다.

This list has a single endpoint: 10.244.1.2:3000.
이 목록에는 단일 엔드포인트가 있습니다: 10.244.1.2:3000 .

This is our backend pod's IP address and port.
이것은 우리의 백엔드 포드의 IP 주소와 포트입니다.

The Endpoint controller created an IP address and port pair and stored it in the Endpoint object.
엔드포인트 컨트롤러는 IP 주소와 포트 쌍을 생성하여 엔드포인트 객체에 저장했습니다.

The IP address and port pair are also called an endpoint to make everything more confusing.
IP 주소와 포트 쌍은 모든 것을 더 혼란스럽게 만들기 위해 엔드포인트라고도 불립니다.

However, in this article (and the rest of the Learnk8s material), we differentiate endpoints in this manner:
그러나 이 기사(및 나머지 Learnk8s 자료)에서는 다음과 같은 방식으로 엔드포인트를 구분합니다:

Understanding how endpoints are collected is crucial, but it still doesn't explain how the traffic reaches the pods behind the Service if those don't exist.
엔드포인트가 어떻게 수집되는지를 이해하는 것은 중요하지만, 서비스 뒤에 있는 포드가 존재하지 않는 경우 트래픽이 어떻게 포드에 도달하는지를 여전히 설명하지 않습니다.

Kubernetes uses a clever workaround to implement a distributed load balancer using endpoints.
Kubernetes는 엔드포인트를 사용하여 분산 로드 밸런서를 구현하기 위해 영리한 우회 방법을 사용합니다.

kube-proxy: translating Service IP to Pod IP
kube-proxy: 서비스 IP를 포드 IP로 변환

kube-proxy programs the Linux kernel to intercept connections made to the service IP.
kube-proxy 는 서비스 IP에 대한 연결을 가로채기 위해 Linux 커널을 프로그래밍합니다.

Then, it rewrites the destination and forwards the traffic to the available pods.
그런 다음, 목적지를 재작성하고 사용 가능한 포드로 트래픽을 전달합니다.

  • Services don't exist, but we must pretend they exist.
    1/4

    Services don't exist, but we must pretend they exist.
    서비스는 존재하지 않지만, 우리는 그것들이 존재한다고 가정해야 합니다.

  • When the traffic reaches the node, it is intercepted and rewritten.
    2/4

    When the traffic reaches the node, it is intercepted and rewritten.

  • The destination IP address is replaced with one of the pod IP addresses.
    3/4

    The destination IP address is replaced with one of the pod IP addresses.

  • The traffic is then forwarded to the pod.
    4/4

    The traffic is then forwarded to the pod.

You can think of kube-proxy and its rules as a mail redirection service.
kube-proxy와 그 규칙을 우편 리디렉션 서비스로 생각할 수 있습니다.

Imagine you want to move house but worry that a few friends might still send you letters to the old address.
집을 옮기고 싶지만 몇몇 친구들이 여전히 옛 주소로 편지를 보낼까 걱정한다고 상상해 보세요.

To work around it, you set up a redirection service: when the mail reaches the post office, it is redirected and forwarded to your newer address.
이를 해결하기 위해 리디렉션 서비스를 설정합니다: 메일이 우체국에 도착하면, 그것은 리디렉션되어 귀하의 새로운 주소로 전달됩니다.

Services work similarly: since they don't exist, when traffic reaches the node (e.g., the post office), it has to be redirected to a real pod.
서비스는 유사하게 작동합니다: 존재하지 않기 때문에 트래픽이 노드(예: 우체국)에 도달하면 실제 포드로 리디렉션되어야 합니다.

These redirection rules are set up by kube-proxy.
이 리디렉션 규칙은 kube-proxy에 의해 설정됩니다.

But how does kube-proxy know when traffic should be intercepted and redirected?
하지만 kube-proxy는 트래픽이 언제 가로채지고 리디렉션되어야 하는지 어떻게 알까요?

Kube-proxy is a pod deployed as a DaemonSet in the cluster and subscribes to changes to Endpoint and Services.
Kube-proxy는 클러스터에 DaemonSet으로 배포된 파드이며, Endpoint 및 Services의 변경 사항을 구독합니다.

When an Endpoint or Service is created, updated, or deleted, kube-proxy refreshes its internal state for the current node.
엔드포인트 또는 서비스가 생성, 업데이트 또는 삭제될 때, kube-proxy는 현재 노드에 대한 내부 상태를 새로 고칩니다.

Then, it proceeds to update its interception and redirect rules.
그런 다음, 가로막기 및 리디렉션 규칙을 업데이트합니다.

  • When a Service is created, the endpoint controller collects the IP addresses and stores them in etcd.
    1/3

    When a Service is created, the endpoint controller collects the IP addresses and stores them in etcd.
    서비스가 생성되면, 엔드포인트 컨트롤러가 IP 주소를 수집하여 etcd에 저장합니다.

  • Kube-proxy subscribes to updates to etcd. It has been notified of new services and endpoints.
    2/3

    Kube-proxy subscribes to updates to etcd. It has been notified of new services and endpoints.

  • Then it updates the iptables on the current node.
    3/3

    Then it updates the iptables on the current node.

kube-proxy primarily sets up iptables rules to route traffic between services and pods on each node to achieve this.
kube-proxy는 주로 각 노드의 서비스와 포드 간의 트래픽을 라우팅하기 위해 iptables 규칙을 설정합니다.

However, other popular tools use different options like IPVS, eBPF, and nftables.
그러나 다른 인기 있는 도구들은 IPVS, eBPF 및 nftables와 같은 다양한 옵션을 사용합니다.

Regardless of the underlying technology, these rules instruct the kernel to rewrite the destination IP address from the service IP to the IP of one of the pods backing the service.
기술의 종류에 관계없이, 이러한 규칙은 커널에게 서비스 IP에서 서비스를 지원하는 포드 중 하나의 IP 주소로 목적지 IP 주소를 재작성하도록 지시합니다.

Let's see how this works in practice.
이것이 실제로 어떻게 작동하는지 봅시다.

kube-proxy and iptables rules
kube-proxy 및 iptables 규칙

Iptables is a tool that operates at the network layer. It allows you to configure rules to control incoming and outgoing network traffic.
Iptables는 네트워크 계층에서 작동하는 도구입니다. 이를 통해 들어오고 나가는 네트워크 트래픽을 제어하는 규칙을 구성할 수 있습니다.

It's worth taking a step back and looking at how the traffic reaches the pod to understand how it works.
트래픽이 포드에 도달하는 방식을 살펴보는 것이 작동 방식을 이해하는 데 도움이 됩니다.

When traffic first arrives at the node, it's intercepted by the Linux Kernel's networking stack, and it's then forwarded to the pod.
트래픽이 노드에 처음 도착하면, 리눅스 커널의 네트워킹 스택에 의해 가로채어지고, 그 후 포드로 전달됩니다.

How packets flow in a Kubernetes node

The Linux Kernel offers several hooks to customize how the traffic is handled depending on the stage of the network stack.
리눅스 커널은 네트워크 스택의 단계에 따라 트래픽 처리 방식을 사용자 정의할 수 있는 여러 후크를 제공합니다.

Hooks in the Linux Kernel's networking stack

Iptables is a user-space application that allows you to configure these hooks and create rules to filter and manipulate the traffic.
Iptables는 이러한 훅을 구성하고 트래픽을 필터링하고 조작하는 규칙을 생성할 수 있는 사용자 공간 애플리케이션입니다.

The most notable example for iptables is firewall rules.
iptables의 가장 주목할 만한 예는 방화벽 규칙입니다.

You can define rules to allow or block traffic based on criteria such as source or destination IP address, port, protocol, and more.
트래픽을 허용하거나 차단하기 위한 규칙을 정의할 수 있으며, 기준으로는 출발지 또는 목적지 IP 주소, 포트, 프로토콜 등이 있습니다.

For example, you might say, I want to block all traffic coming from a specific IP address or range.
예를 들어, 특정 IP 주소 또는 범위에서 오는 모든 트래픽을 차단하고 싶다고 말할 수 있습니다.

But how does it help us with load-balancing traffic to pods?
하지만 이것이 포드로의 트래픽 로드 밸런싱에 어떻게 도움이 될까요?

iptables can also be used to rewrite the destination for a packet.
iptables는 패킷의 목적지를 재작성하는 데에도 사용할 수 있습니다.

For example, you might say, I want to redirect all traffic from a specific IP address to another IP address.
예를 들어, 특정 IP 주소에서 다른 IP 주소로 모든 트래픽을 리디렉션하고 싶다고 말할 수 있습니다.

And that's precisely what happens in Kubernetes.
그리고 그것이 바로 Kubernetes에서 발생하는 일입니다.

iptables has five modes of operations (i.e. tables): filter, nat, mangle, raw and security.
iptables는 다섯 가지 작동 모드(즉, 테이블)를 가지고 있습니다: filter, nat, mangle, raw 및 security.

Filter, Nat, mangle and raw tables in iptables

It's important to note that tables have different hooks available.
테이블에는 다양한 훅이 제공된다는 점에 유의하는 것이 중요합니다.

The Nat table, primarily used in Kubernetes, has only three hooks: PREROUTING, OUTPUT, and POSTROUTING.
Kubernetes에서 주로 사용되는 Nat 테이블은 PREROUTING, OUTPUT 및 POSTROUTING의 세 가지 훅만 가지고 있습니다.

You can create custom rules in this table and group them into chains.
이 표에서 사용자 정의 규칙을 만들고 이를 체인으로 그룹화할 수 있습니다.

Each rule has a target and an action.
각 규칙에는 대상과 동작이 있습니다.

Chains, rules, targets and actions in iptables

Chains are linked to each other and the hooks to create complex workflows.
체인은 서로 연결되어 있으며 복잡한 워크플로를 생성하는 후크가 있습니다.

You can link iptables chains to each other and to the hooks

In Kubernetes, kube-proxy programs iptables so that when a packet arrives at the node, its destination IP address is matched against all service IP addresses.
Kubernetes에서 kube-proxy 는 iptables를 프로그래밍하여 패킷이 노드에 도착할 때 목적지 IP 주소가 모든 서비스 IP 주소와 일치하도록 합니다.

If there's a match, the traffic is forwarded to a specific chain that handles load balancing for that service.
일치하는 경우, 트래픽은 해당 서비스의 로드 밸런싱을 처리하는 특정 체인으로 전달됩니다.

As pods are added or removed from the service, kube-proxy dynamically updates these chains.
서비스에 포드가 추가되거나 제거됨에 따라 kube-proxy 는 이러한 체인을 동적으로 업데이트합니다.

To see how chains interact in kube-proxy, let's follow the traffic from pod to service.
kube-proxy에서 체인이 어떻게 상호작용하는지 보려면, 포드에서 서비스로의 트래픽을 따라가 보겠습니다.

Following traffic from a Pod to Service
Pod에서 Service로의 트래픽 흐름

First, deploy a curl client pod in the cluster:
먼저 클러스터에 curl 클라이언트 포드를 배포합니다:

bash

kubectl run curl-client --rm -i --tty \
  --image=curlimages/curl -- /bin/sh

Inside the curl-client pod, send a request to the backend-service using its ClusterIP 10.96.5.81 on port 3000:
curl-client 팟 내부에서 backend-serviceClusterIP 10.96.5.81 를 사용하여 포트 3000 에서 요청을 보냅니다:

bash

curl http://10.96.5.81:3000

On another terminal, launch a privileged container that has access to the host's network stack:
다른 터미널에서 호스트의 네트워크 스택에 접근할 수 있는 권한이 있는 컨테이너를 실행합니다:

bash

kubectl run -it --rm --privileged \
  --image=ubuntu \
  --overrides='{"spec": {"hostNetwork": true, "hostPID": true}}' \
  ubuntu -- bash

Inside the container, update the package list and install iptables:
컨테이너 내부에서 패키지 목록을 업데이트하고 iptables 을(를) 설치합니다:

bash

apt update && apt install -y iptables

When a request is sent to the Service, it enters the node's network stack, where it is intercepted by the iptables rules set by kube-proxy.
서비스에 요청이 전송되면, 이는 노드의 네트워크 스택에 들어가고, 여기서 kube-proxy에 의해 설정된 iptables 규칙에 의해 가로채어집니다.

The process begins in the PREROUTING chain of the nat table, where incoming packets are matched to service IPs.
프로세스는 nat 테이블의 PREROUTING 체인에서 시작되며, 여기서 들어오는 패킷이 서비스 IP와 일치합니다.

Since the destination IP matches 10.96.5.81, the KUBE-SERVICES chain processes the packet.
대상 IP가 10.96.5.81 와 일치하므로 KUBE-SERVICES 체인이 패킷을 처리합니다.

Let's inspect the PREROUTING chain with:
PREROUTING 체인을 검사해 보겠습니다:

bash

iptables -t nat -L PREROUTING --line-numbers

#output
1  KUBE-SERVICES  all  anywhere      anywhere             /* kubernetes service portals */
2  DOCKER_OUTPUT  all  anywhere      host.minikube.internal
3  DOCKER         all  anywhere      anywhere             ADDRTYPE match dst-type LOCAL
The PREROUTING chain is followed by the KUBE-SERVICE chain

You can explore what happens next by inspecting the KUBE-SERVICES chain.
다음에 일어나는 일을 KUBE-SERVICES 체인을 검사하여 탐색할 수 있습니다.

bash

iptables -t nat -L KUBE-SERVICES -n --line-numbers

#output
1  KUBE-SVC-NPX46M4PTMTKRN6Y  /* default/kubernetes:https cluster IP */ tcp dpt:443
2  KUBE-SVC-TCOU7JCQXEZGVUNU  /* kube-system/kube-dns:dns cluster IP */ udp dpt:53
3  KUBE-SVC-ERIFXISQEP7F7OF4  /* kube-system/kube-dns:dns-tcp cluster IP */ tcp dpt:53
4  KUBE-SVC-JD5MR3NA4I4DYORP  /* kube-system/kube-dns:metrics cluster IP */ tcp dpt:9153
5  KUBE-SVC-6R7RAWWNQI6ZLKMO  /* default/backend-service:backend cluster IP */ tcp dpt:3000
6  KUBE-NODEPORTS             /* kubernetes service nodeports;

Don't get scared by the long list of IDs.
긴 ID 목록에 겁먹지 마세요.

You are seeing a list of redirection rules: one for each service.
서비스마다 하나씩의 리디렉션 규칙 목록을 보고 있습니다.

But you only created one service; how come there are so many?
하지만 당신은 하나의 서비스만 만들었는데, 왜 이렇게 많나요?

Kubernetes already has services for CoreDNS, the API server, and more; each is implemented as a chain in iptables.
Kubernetes는 이미 CoreDNS, API 서버 등과 같은 서비스를 가지고 있으며, 각 서비스는 iptables의 체인으로 구현됩니다.

You can verify (and match) those chains to their respective Service by listing all services in your cluster:
클러스터의 모든 서비스를 나열하여 해당 체인을 해당 서비스와 확인(및 일치)할 수 있습니다:

bash

kubectl get services -A
NAMESPACE     NAME              TYPE        CLUSTER-IP      PORT(S)
default       kubernetes        ClusterIP   10.96.0.1       443/TCP
kube-system   kube-dns          ClusterIP   10.96.0.10      53/UDP,53/TCP
kube-system   metrics-server    ClusterIP   10.96.10.5      443/TCP
jobs          backend-service   ClusterIP   10.96.5.81      3000/TCP

Now that the output makes more sense, let's continue.
이제 출력이 더 이해가 되므로 계속하겠습니다.

The KUBE-SERVICE chain is a collection of chains.
KUBE-SERVICE 체인은 체인의 모음입니다.

Only the KUBE-SVC-6R7RAWWNQI6ZLKMO chain matches the destination IP 10.96.5.81.
오직 KUBE-SVC-6R7RAWWNQI6ZLKMO 체인만이 목적지 IP 10.96.5.81 와 일치합니다.

The KUBE-SERVICE chain invokes the KUBE-SVC-6R7RAWWNQI6ZLKMO chain

Let's inspect this chain:
이 체인을 살펴보자:

bash

iptables -t nat -L KUBE-SVC-6R7RAWWNQI6ZLKMO -n --line-numbers
1  KUBE-MARK-MASQ             --  /* default/backend-service:backend cluster IP */ tcp dpt:3000
2  KUBE-SEP-O3HWD4DESFNXEYL6  --  /* default/backend-service:backend -> 10.244.1.2:3000 */

The first chain is KUBE-MARK-MASQ, which patches the source IP when the destination is external to the cluster.
첫 번째 체인은 KUBE-MARK-MASQ 로, 목적지가 클러스터 외부일 때 소스 IP를 패치합니다.

The second chain, KUBE-SEP-O3HWD4DESFNXEYL6, is the Service Endpoint chain.
두 번째 체인, KUBE-SEP-O3HWD4DESFNXEYL6 ,은 서비스 엔드포인트 체인입니다.

Inspecting the KUBE-SEP chain

If you inspect this chain, you will find two rules:
이 체인을 검사하면 두 개의 규칙을 찾을 수 있습니다:

bash

iptables -t nat -L KUBE-SEP-O3HWD4DESFNXEYL6 -n --line-numbers
1    KUBE-MARK-MASQ    10.244.1.2    0.0.0.0/0    /* default/backend-service:backend */
2    DNAT                                         /* default/backend-service:backend */ tcp to:10.244.1.2:3000

The first rule marks the packet for masquerading if the pod requesting the service is also chosen as the destination.
첫 번째 규칙은 서비스를 요청하는 포드가 목적지로 선택된 경우 패킷을 변조하도록 표시합니다.

The DNAT rule changes the destination IP from the service IP (10.96.5.81) to the pod's IP (10.244.1.2).
DNAT 규칙은 서비스 IP ( 10.96.5.81 )에서 포드의 IP ( 10.244.1.2 )로 목적지 IP를 변경합니다.

Inspecting the KUBE-SEP chain

What happens when you scale the deployments to three replicas?
배포를 세 개의 복제본으로 확장하면 어떤 일이 발생합니까?

bash

kubectl scale deployment backend-deployment --replicas=3

The KUBE-SVC-6R7RAWWNQI6ZLKMO chain now has three KUBE-SEP chains:

bash

iptables -t nat -L KUBE-SVC-6R7RAWWNQI6ZLKMO -n --line-numbers
1  KUBE-MARK-MASQ              /* default/backend-service:backend cluster IP */ tcp dpt:3000
2  KUBE-SEP-O3HWD4DESFNXEYL6   /* default/backend-service:backend -> 10.244.1.2:3000 */
3  KUBE-SEP-C2Y64IBVPH4YIBGX   /* default/backend-service:backend -> 10.244.1.3:3000 */
4  KUBE-SEP-MRYDKJV5U7PLF5ZN   /* default/backend-service:backend -> 10.244.1.4:3000 */

Each rule points to a chain that changes the destination IP to one of the three pods.
각 규칙은 목적지 IP를 세 개의 포드 중 하나로 변경하는 체인을 가리킵니다.

Scaling to three replicas creates more KUBE-SEP chains

You finally got to the bottom of how Kubernetes Services works.
당신은 마침내 Kubernetes Services가 어떻게 작동하는지에 대한 본질을 파악했습니다.

Let's create a deployment for the frontend app that will consume the API exposed by the backend.
프론트엔드 앱을 위한 배포를 생성하여 백엔드에서 노출된 API를 사용할 수 있도록 하겠습니다.

Deploying and exposing the frontend Pods
프론트엔드 팟 배포 및 노출

This is what frontend-deployment.yaml looks like:
이것이 frontend-deployment.yaml 의 모습입니다:

frontend-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend
    spec:
      containers:
      - name: frontend
        image: ghcr.io/learnk8s/jobs-api
        ports:
          - containerPort: 8080

You can submit the definition to the cluster with:
클러스터에 정의를 제출할 수 있습니다:

bash

kubectl apply -f frontend-deployment.yaml
deployment.apps/frontend-deployment created

Let's verify the deployment was successful:
배포가 성공했는지 확인해 봅시다:

bash

kubectl get pod -l app=frontend -o wide
NAME                                   READY   STATUS    IP           NODE
frontend-deployment-66dd585966-2bjtt   1/1     Running   10.244.1.7   minikube-m02
frontend-deployment-66dd585966-rxtxt   1/1     Running   10.244.2.6   minikube-m03
frontend-deployment-66dd585966-w8szs   1/1     Running   10.244.0.5   minikube

Unlike the backend service, the frontend service does not need to be reachable from other pods in the cluster.
백엔드 서비스와 달리 프론트엔드 서비스는 클러스터 내 다른 팟에서 접근할 필요가 없습니다.

It must be reached by clients using web browsers outside of the cluster.
클러스터 외부의 웹 브라우저를 사용하는 클라이언트가 접근할 수 있어야 합니다.

Exposing the frontend pods
프론트엔드 팟 노출하기

The Service you used until now was the default Kubernetes service: ClusterIP.
지금까지 사용한 서비스는 기본 Kubernetes 서비스인 ClusterIP였습니다.

However, Kubernetes has a second option for when you wish to expose your pods externally to the cluster: NodePort.
그러나 Kubernetes에는 클러스터 외부에 포드를 노출하려는 경우를 위한 두 번째 옵션이 있습니다: NodePort.

Let's create a NodePort service:
NodePort 서비스를 생성해 보겠습니다:

frontend-service.yaml

apiVersion: v1
kind: Service
metadata:
  name: frontend-service
spec:
  type: NodePort
  selector:
    app: frontend
  ports:
    - name: frontend
      protocol: TCP
      port: 80
      targetPort: 8080

You can submit it to the cluster with:
클러스터에 제출할 수 있습니다:

bash

kubectl apply -f frontend-service.yaml
service/frontend-service created

Next, let's verify the creation of the Endpoints resource by running:
다음으로, 다음 명령어를 실행하여 Endpoints 리소스의 생성을 확인해 보겠습니다:

bash

kubectl get endpoints frontend-service
NAME               ENDPOINTS
frontend-service   10.244.0.5:8080,10.244.1.7:8080,10.244.2.6:8080

The frontend-service is associated with three pod IPs: 10.244.0.5, 10.244.1.7, and 10.244.2.6, each listening on port 8080.
frontend-service 는 세 개의 포드 IP 10.244.0.5 , 10.244.1.7 , 10.244.2.6 와 연결되어 있으며, 각각 포트 8080 에서 수신 대기하고 있습니다.

These IPs correspond to the individual frontend deployment pods.
이 IP는 개별 프론트엔드 배포 포드에 해당합니다.

If you get the frontend-service, you can verify the TYPE column shows NodePort with:
frontend-service 를 받으면 TYPE 열이 NodePort 를 표시하는지 확인할 수 있습니다:

bash

kubectl get service frontend-service
NAME               TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)
frontend-service   NodePort    10.96.154.187   <none>        80:32073/TCP

When you create a service of this type, the API server allocates a unique port from the service node port range.
이 유형의 서비스를 생성할 때, API 서버는 서비스 노드 포트 범위에서 고유한 포트를 할당합니다.

This range typically defaults to 30000-32767, and in this case is 32073.
이 범위는 일반적으로 30000-32767로 기본 설정되며, 이 경우는 32073입니다.

The current setup with a NodePort Service, 3 front-end pods and 3 backend pods

This NodePort is open on all nodes in the cluster, not just the node where the pod is running.
이 NodePort는 포드가 실행되고 있는 노드뿐만 아니라 클러스터의 모든 노드에서 열려 있습니다.

As per the ClusterIP service, NodePort doesn't require a specific process to be listening on that port.
ClusterIP 서비스에 따라 NodePort는 해당 포트에서 수신 대기하는 특정 프로세스가 필요하지 않습니다.

When traffic reaches the port, it gets intercepted and redirected by iptables rules.
트래픽이 포트에 도달하면, iptables 규칙에 의해 가로채어지고 리디렉션됩니다.

The chain for the NodePorts is called KUBE-NODEPORTS and is the last chain in KUBE-SERVICES.
NodePorts에 대한 체인은 KUBE-NODEPORTS 라고 하며 KUBE-SERVICES 의 마지막 체인입니다.

bash

iptables -t nat -L KUBE-SERVICES -n --line-numbers

#output
1  KUBE-SVC-NPX46M4PTMTKRN6Y  /* default/kubernetes:https cluster IP */ tcp dpt:443
2  KUBE-SVC-TCOU7JCQXEZGVUNU  /* kube-system/kube-dns:dns cluster IP */ udp dpt:53
3  KUBE-SVC-ERIFXISQEP7F7OF4  /* kube-system/kube-dns:dns-tcp cluster IP */ tcp dpt:53
4  KUBE-SVC-JD5MR3NA4I4DYORP  /* kube-system/kube-dns:metrics cluster IP */ tcp dpt:9153
5  KUBE-SVC-6R7RAWWNQI6ZLKMO  /* default/backend-service:backend cluster IP */ tcp dpt:3000
6  KUBE-NODEPORTS             /* kubernetes service nodeports;
The KUBE-NODEPORTS chain is the last chain in KUBE-SERVICES

What's inside this chain?
이 체인 안에는 무엇이 있나요?

bash

iptables -t nat -L KUBE-NODEPORTS -n -v --line-numbers | grep 32073
1  KUBE-EXT-6XQSOA4B4HLF6UNI /* jobs/frontend-service:frontend */ tcp dpt:32073

The KUBE-NODEPORTS chain lists all NodePorts in the cluster.
KUBE-NODEPORTS 체인은 클러스터의 모든 NodePort를 나열합니다.

For each NodePort, there's a rule that redirects traffic to the corresponding service.
각 NodePort마다 해당 서비스로 트래픽을 리디렉션하는 규칙이 있습니다.

the KUBE-NODEPORTS chain lists all NodePorts in the cluster

Let's inspect the KUBE-EXT-6XQSOA4B4HLF6UNI chain:
KUBE-EXT-6XQSOA4B4HLF6UNI 체인을 살펴보겠습니다:

bash

iptables -t nat -L KUBE-EXT-6XQSOA4B4HLF6UNI -n -v --line-numbers
1  KUBE-MARK-MASQ      /* masquerade traffic for jobs/frontend-service:frontend external destinations */
2  KUBE-SVC-6XQSOA4B4HLF6UNI

You might recognize the second chain: it's the Service's chain!
두 번째 체인을 인식할 수 있을 것입니다: 그것은 서비스의 체인입니다!

So, if the incoming traffic is destined for the NodePort, it gets redirected to the correct Service chain.
따라서, 들어오는 트래픽이 NodePort를 대상으로 하는 경우, 올바른 서비스 체인으로 리디렉션됩니다.

The KUBE-EXT chain links back to the Service

As you've already experienced, this chain will have a KUBE-SEP-* chain for each pod in the Service.
이미 경험하셨듯이, 이 체인은 서비스의 각 포드에 대해 KUBE-SEP-* 체인이 있을 것입니다.

The KUBE-SEP chains will load balance the traffic to the destination pods

This is the third time you have found the KUBE-MARK-MASQ chain.
이것은 당신이 KUBE-MARK-MASQ 체인을 찾은 세 번째입니다.

  1. In the first case (in KUBE-SEP), it was used for making sure that the traffic originated from a pod could use a service that has the same pod as the destination (hairpin NAT).
    첫 번째 경우( KUBE-SEP )에서는 트래픽이 포드에서 시작되어 동일한 포드를 대상으로 하는 서비스를 사용할 수 있도록 하기 위해 사용되었습니다(헤어핀 NAT).
  2. The second case (in KUBE-SVC) ensured that traffic external to the cluster using the ClusterIP could be routed correctly.
    두 번째 사례( KUBE-SVC )는 ClusterIP를 사용하는 클러스터 외부의 트래픽이 올바르게 라우팅될 수 있도록 보장했습니다.
  3. The third case is just now in the NodePort chain (in KUBE-EXT).
    세 번째 경우는 지금 NodePort 체인에 있습니다 ( KUBE-EXT ).

To understand why those are necessary, consider the following scenario.
그것들이 왜 필요한지 이해하기 위해 다음 시나리오를 고려해 보십시오.

You make a request to a NodePort.
NodePort에 요청을 합니다.

The request has been redirected to one of the pods in the service.
요청이 서비스의 포드 중 하나로 리디렉션되었습니다.

The Pod processes the request and replies, but where should the traffic go back to?
Pod는 요청을 처리하고 응답하지만, 트래픽은 어디로 돌아가야 할까요?

The source IP address is your IP address; however, you are not expecting a response from a Pod but from a node.
소스 IP 주소는 귀하의 IP 주소입니다. 그러나 Pod가 아닌 노드로부터 응답을 기대하고 있습니다.

When the traffic reaches the node port, the source IP address is changed to that of the local node to work around the issue.
트래픽이 노드 포트에 도달하면, 소스 IP 주소가 로컬 노드의 주소로 변경되어 문제를 해결합니다.

  • As soon as you make a request to a NodePort service, the traffic is intercepted.
    1/4

    As soon as you make a request to a NodePort service, the traffic is intercepted.
    NodePort 서비스에 요청을 하면 즉시 트래픽이 가로채집니다.

  • We know that iptables rewrites the request to target a pod.
    2/4

    We know that iptables rewrites the request to target a pod.

  • When the traffic is external to the cluster, the source IP is also replaced with the Node's IP address.
    3/4

    When the traffic is external to the cluster, the source IP is also replaced with the Node's IP address.

  • The traffic finally reaches the pod.
    4/4

    The traffic finally reaches the pod.

When the traffic returns, the source IP address is amended to the real IP.
트래픽이 반환될 때, 소스 IP 주소는 실제 IP로 수정됩니다.

  • As soon as the request exits the node, it is intercepted.
    1/4

    As soon as the request exits the node, it is intercepted.
    요청이 노드를 벗어나자마자 가로채집니다.

  • The source IP address is amended again to be the original requester.
    2/4

    The source IP address is amended again to be the original requester.

  • Iptables also help us pretend the traffic comes from a service.
    3/4

    Iptables also help us pretend the traffic comes from a service.

  • The traffic finally reaches the original requester.
    4/4

    The traffic finally reaches the original requester.

Let's inspect the KUBE-MARK-MASQ chain to understand the details.
KUBE-MARK-MASQ 체인을 검사하여 세부 사항을 이해해 봅시다.

bash

iptables -t nat -L KUBE-MARK-MASQ -n -v
542 28196 MARK    *      *       0.0.0.0/0            0.0.0.0/0            MARK or 0x4000

When the KUBE-MARK-MASQ is invoked, it applies a mark with the 0x4000 value to all packets.
KUBE-MARK-MASQ 가 호출되면 0x4000 값이 모든 패킷에 적용됩니다.

And that's it.  그게 전부입니다.

When the packet is about to exit the node, the KUBE-POSTROUTING chain will check for the mark.
패킷이 노드를 나가려 할 때, KUBE-POSTROUTING 체인이 마크를 확인합니다.

Let's inspect the POSTROUTING chain:
POSTROUTING 체인을 살펴보겠습니다:

bash

iptables -t nat -L POSTROUTING -n -v
KUBE-POSTROUTING      --  0.0.0.0/0            0.0.0.0/0/* kubernetes postrouting rules */
IP-MASQ-AGENT         --  0.0.0.0/0            0.0.0.0/0  ADDRTYPE match dst-type !LOCAL

And let's inspect the KUBE-POSTROUTING chain:

bash

iptables -t nat -L KUBE-POSTROUTING -n -v
RETURN     --  *      *       0.0.0.0/0   0.0.0.0/0   mark match ! 0x4000/0x4000
MARK       --  *      *       0.0.0.0/0   0.0.0.0/0   MARK xor 0x4000
MASQUERADE --  *      *       0.0.0.0/0   0.0.0.0/0   random-fully

The three lines read:  세 줄은 다음과 같습니다:

  1. If there is no MARK, there is no need to apply SNAT. Return to the caller.
    MARK가 없으면 SNAT를 적용할 필요가 없습니다. 호출자에게 반환합니다.
  2. If there's a MARK, remove it.
    MARK가 있으면 제거하십시오.
  3. Finally, apply SNAT so that the traffic is correctly handled.
    마지막으로, 트래픽이 올바르게 처리되도록 SNAT를 적용합니다.
`KUBE-POSTROUTING` chain

But there's a drawback: the pods see the Node's IP as the source of the request and not the original client.
하지만 단점이 있습니다: 파드는 요청의 출처로 노드의 IP를 보고 원래 클라이언트를 보지 않습니다.

You can set externalTrafficPolicy: Local to preserve the client's original IP.
클라이언트의 원래 IP를 보존하려면 externalTrafficPolicy: Local 을 설정할 수 있습니다.

However, this change comes with its trade-offs, which we'll explore shortly.
그러나 이 변화는 몇 가지 대가를 동반하며, 우리는 곧 이를 탐구할 것입니다.

Before discussing those, let's reflect on the two Services you have used:
그것에 대해 논의하기 전에, 여러분이 사용했던 두 가지 서비스에 대해 생각해 봅시다:

ClusterIP and NodePort aren't the only two Service types in a Kubernetes cluster.
ClusterIP와 NodePort는 Kubernetes 클러스터에서 유일한 두 가지 서비스 유형이 아닙니다.

A popular option is the type: LoadBalancer service.
인기 있는 옵션은 type: LoadBalancer 서비스입니다.

Load Balancer Service  로드 밸런서 서비스

In a typical cloud environment, Kubernetes' cloud controller manager automatically provisions a load balancer from the cloud provider and assigns an external IP to the service of type: LoadBalancer.
전형적인 클라우드 환경에서 Kubernetes의 클라우드 컨트롤러 매니저는 클라우드 공급자로부터 로드 밸런서를 자동으로 프로비저닝하고 type: LoadBalancer 서비스에 외부 IP를 할당합니다.

The load balancer forwards requests to the service's NodePort, which kube-proxy directs to the appropriate pod.
로드 밸런서는 요청을 서비스의 NodePort로 전달하고, kube-proxy는 이를 적절한 포드로 전달합니다.

A LoadBalancer service in Kubernetes is a combination of a NodePort and a cloud load balancer

Some load balancers preserve the source IP, but by default, the NodePort setup might hide it.
일부 로드 밸런서는 소스 IP를 보존하지만, 기본적으로 NodePort 설정은 이를 숨길 수 있습니다.

Let's use Minikube to demonstrate this behaviour; the same principles apply to other load balancers, including cloud-based ones.
이 동작을 보여주기 위해 Minikube를 사용해 보겠습니다. 동일한 원칙이 클라우드 기반을 포함한 다른 로드 밸런서에도 적용됩니다.

To test it, amend the frontend service file as follows:
테스트하기 위해 프론트엔드 서비스 파일을 다음과 같이 수정하십시오:

frontend-service.yaml

apiVersion: v1
kind: Service
metadata:
  name: frontend-service
spec:
  type: LoadBalancer
  selector:
    app: frontend
  ports:
    - name: frontend
      protocol: TCP
      port: 80
      targetPort: 8080

Resubmit the resource to the cluster with:
클러스터에 리소스를 다시 제출하려면:

bash

kubectl apply -f frontend-service.yaml
service/fronted-service configured

Now you can see the TYPE column shows LoadBalancer.
이제 TYPE 열에 LoadBalancer 가 표시됩니다.

Let's inspect the service with the following command:
다음 명령어로 서비스를 검사해 보겠습니다:

bash

kubectl get service frontend-service
NAME               TYPE           CLUSTER-IP     EXTERNAL-IP    PORT(S)
frontend-service   LoadBalancer   10.107.34.82   <pending>   80:32073/TCP

Let's execute minikube tunnel:

bash

minikube tunnel
NAME               TYPE           CLUSTER-IP     EXTERNAL-IP    PORT(S)        AGE
frontend-service   LoadBalancer   10.107.34.82   10.107.34.82   80:32073/TCP   4d12h

When a LoadBalancer controller is installed in a cloud environment, it assigns a unique external IP from its pool of addresses to each LoadBalancer service.
클라우드 환경에 LoadBalancer 컨트롤러가 설치되면, 각 LoadBalancer 서비스에 고유한 외부 IP를 주소 풀에서 할당합니다.

This IP is often publicly accessible and differs from the ClusterIP, which belongs to the cluster network.
이 IP는 종종 공개적으로 접근 가능하며 클러스터 네트워크에 속하는 ClusterIP 와 다릅니다.

When using Minikube, if you make a browser request to the IP address 10.107.34.82 on port 80, the minikube tunnel intercepts the request.
Minikube를 사용할 때, 포트 80 에서 IP 주소 10.107.34.82 에 브라우저 요청을 하면 minikube tunnel 가 요청을 가로챕니다.

The tunnel forwards the request to the corresponding nodePort, such as 32073, on the Minikube node.
터널은 Minikube 노드의 해당 nodePort , 예를 들어 32073 ,로 요청을 전달합니다.

Once the request reaches the node, kube-proxy translates the nodePort to one of the frontend pod IPs (10.244.0.5, 10.244.1.7, or 10.244.2.6) and port 8080.
요청이 노드에 도달하면, kube-proxynodePort 을 프론트엔드 포드 IP 중 하나( 10.244.0.5 , 10.244.1.7 , 또는 10.244.2.6 )와 포트 8080 로 변환합니다.

Whether you use minikube tunnel or provision a cloud load balancer, in both cases, the traffic is forwarded to the NodePort first.
minikube tunnel 를 사용하든 클라우드 로드 밸런서를 프로비저닝하든, 두 경우 모두 트래픽은 먼저 NodePort로 전달됩니다.

In other words, the LoadBalancer service extends NodePort with a mechanism to load balance requests to multiple NodePorts.
다시 말해, LoadBalancer 서비스는 여러 NodePort에 요청을 로드 밸런싱하는 메커니즘으로 NodePort를 확장합니다.

From there onwards, the traffic is intercepted and redirected as usual.
그 이후로 트래픽은 일반적으로 가로채지고 리디렉션됩니다.

Until now, we've discussed NodePort and LoadBalancer services, where traffic is always routed to a node and then to a pod running on that node.
지금까지 우리는 NodePort 및 LoadBalancer 서비스에 대해 논의했습니다. 여기서 트래픽은 항상 노드로 라우팅된 다음 해당 노드에서 실행 중인 포드로 전달됩니다.

But what happens when the traffic hits a NodePort that doesn't have a pod?
하지만 트래픽이 포드가 없는 NodePort에 도달하면 어떻게 될까요?

Extra hop with kube-proxy and intra-cluster load balancing
kube-proxy와 클러스터 내 로드 밸런싱의 추가 홉

When a load balancer sends traffic to a NodePort, the node receiving the request may not always run the pod.
로드 밸런서가 NodePort로 트래픽을 보낼 때, 요청을 받는 노드가 항상 포드를 실행하지 않을 수 있습니다.

In such cases, the iptables rules forward the traffic to another node where the target pod is located.
이러한 경우, iptables 규칙은 트래픽을 대상 포드가 위치한 다른 노드로 전달합니다.

Let's test this in the two-node Minikube cluster, where a frontend application pod runs only on one node, minikube-m02 with IP 192.168.49.3.
두 노드 Minikube 클러스터에서 이를 테스트해 보겠습니다. 여기서 프론트엔드 애플리케이션 포드는 minikube-m02 노드에서만 실행되며, IP는 192.168.49.3 입니다.

However, the traffic is routed through minikube-m03 (IP 192.168.49.4) via the NodePort service.
그러나 트래픽은 NodePort 서비스를 통해 minikube-m03 (IP 192.168.49.4 )를 통해 라우팅됩니다.

A three node cluster with a single pod exposed via a NodePort

kube-proxy's rules on minikube-m03 forward the traffic to minikube-m02, where the pod is running.
kube-proxy 의 규칙은 minikube-m03 에서 minikube-m02 로 트래픽을 전달하여 포드가 실행되고 있습니다.

With a tool like tcpdump, you can observe this extra hop capturing the traffic on minikube-m02.
tcpdump와 같은 도구를 사용하면 minikube-m02 에서 트래픽을 캡처하는 이 추가 홉을 관찰할 수 있습니다.

First, identify the nodes in the cluster:
먼저 클러스터의 노드를 식별합니다:

bash

kubectl get node -o wide
NAME           STATUS   VERSION   INTERNAL-IP
minikube-m02   Ready    v1.30.0   192.168.49.3
minikube-m03   Ready    v1.30.0   192.168.49.4

Then, ssh into minikube-m02 and install tcpdump:
그런 다음, minikube-m02 에 ssh로 접속하여 tcpdump 을 설치합니다:

bash

minikube ssh -n minikube-m02
sudo apt update && sudo apt install tcpdump -y

A network interface is a connection point for communication.
네트워크 인터페이스는 통신을 위한 연결 지점입니다.

In Minikube, you usually see eth0, docker0, bridge, and veth interfaces.
Minikube에서는 일반적으로 eth0 , docker0 , bridge , 및 veth 인터페이스를 볼 수 있습니다.

The following command captures and displays network packets from all interfaces present on the minikube nodes, filtering traffic from minikube-m03 (192.168.49.4) and targeting NodePort 32073 for the frontend-service:
다음 명령은 minikube 노드에 있는 모든 인터페이스에서 네트워크 패킷을 캡처하고 표시하며, minikube-m03 ( 192.168.49.4 )에서 트래픽을 필터링하고 frontend-service 에 대해 NodePort 32073 를 대상으로 합니다:

bash

tcpdump -i any host 192.168.49.4 and port 32073

Lastly, on another terminal, ssh into minikube-m03 and send a curl request to minikube-m02 on NodePort 32073.
마지막으로, 다른 터미널에서 minikube-m03 에 ssh로 접속하고 NodePort 32073 에서 minikube-m02 에 curl 요청을 보냅니다.

This may seem like you're sending a request to "yourself", but the request is intended for the NodePort service, which is available on all nodes:
이것은 마치 "자신에게" 요청을 보내는 것처럼 보일 수 있지만, 요청은 모든 노드에서 사용할 수 있는 NodePort 서비스에 대한 것입니다:

bash

minikube ssh -n minikube-m03
curl http://192.168.49.3:32073

If you turn back to the tcpdump session, you'll see an output like this:
tcpdump 세션으로 돌아가면 다음과 같은 출력이 표시됩니다:

bash

...
09:53:06.968588 eth0  In  IP minikube-m03.minikube.39802 > minikube-m02.32073: Flags [S], seq 3518088234,
09:53:06.968613 eth0  Out IP minikube-m02.32073 > minikube-m03.minikube.39802: Flags [S.], seq 4178857212,
#truncated output

The curl request initially reaches minikube-m03, but the pod is not on minikube-m03.
curl 요청은 처음에 minikube-m03 에 도달하지만, 포드는 minikube-m03 에 없습니다.

Since there are no local endpoints, iptables rules forward the traffic to minikube-m02 because the pod is running there.
로컬 엔드포인트가 없기 때문에 iptables 규칙이 트래픽을 minikube-m02 로 전달합니다. 왜냐하면 해당 포드가 거기에서 실행되고 있기 때문입니다.

  • When the traffic reaches a node that doesn't have any pod, it is intercepted by iptables rules.
    1/2

    When the traffic reaches a node that doesn't have any pod, it is intercepted by iptables rules.
    트래픽이 어떤 파드도 없는 노드에 도달하면, iptables 규칙에 의해 가로채어집니다.

  • The traffic is then forwarded to the node that has the pod.
    2/2

    The traffic is then forwarded to the node that has the pod.

While this is great when you have fewer Pods than nodes, it also has drawbacks.
노드보다 포드가 적을 때는 좋지만, 단점도 있습니다.

Imagine having three pods, one on each node, exposed by a NodePort service.
세 개의 포드가 있다고 상상해 보세요. 각 노드에 하나씩 있으며, NodePort 서비스에 의해 노출됩니다.

If you send a request to the NodePort on the second node, does the traffic reach the pod on that node?
두 번째 노드의 NodePort에 요청을 보내면, 트래픽이 해당 노드의 포드에 도달합니까?

Not necessarily.  반드시 그렇지는 않습니다.

Let's inspect the iptables rules for the service again:
서비스에 대한 iptables 규칙을 다시 살펴보겠습니다:

bash

iptables -t nat -L KUBE-SVC-6XQSOA4B4HLF6UNI -n -v
KUBE-MARK-MASQ             /* jobs/frontend-service:frontend cluster IP */ tcp dpt:80
KUBE-SEP-IWKGAKCJPN2GDKZP  /* jobs/frontend-service:frontend -> 10.244.0.3:8080 */ statistic mode random probability 0.3
KUBE-SEP-G5YGW63JRCEDVAQZ  /* jobs/frontend-service:frontend -> 10.244.1.4:8080 */ statistic mode random probability 0.5
KUBE-SEP-SVT42V6SNWE7E3YK  /* jobs/frontend-service:frontend -> 10.244.2.3:8080 */

Kube-proxy uses the iptables "statistic mode random probability" mode to assign to each rule to ensure a roughly proportional distribution of connections.
Kube-proxy는 각 규칙에 대해 대략적으로 비례하는 연결 분포를 보장하기 위해 iptables의 "통계 모드 랜덤 확률" 모드를 사용합니다.

There's no rule that the pod should serve the traffic in the current node.
현재 노드에서 트래픽을 처리해야 한다는 규칙은 없습니다.

  • Traffic reaches a NodePort in the cluster, and the same node hosts a pod.
    1/4

    Traffic reaches a NodePort in the cluster, and the same node hosts a pod.
    트래픽이 클러스터의 NodePort에 도달하고, 동일한 노드에 포드가 호스팅됩니다.

  • The traffic is intercepted, and the iptables rules will use one of the KUBE-SEP-XXX chains to forward the traffic to a pod.
    2/4

    The traffic is intercepted, and the iptables rules will use one of the KUBE-SEP-XXX chains to forward the traffic to a pod.

  • Since those chains use a statistic mode to distribute the traffic, there's no guarantee that the traffic will be forwarded to the pod in the current node.
    3/4

    Since those chains use a statistic mode to distribute the traffic, there's no guarantee that the traffic will be forwarded to the pod in the current node.

  • In this case, the iptables rules selected the first pod.
    4/4

    In this case, the iptables rules selected the first pod.

As a consequence, when using NodePort or Loadbalancer, traffic might incur an extra hop as it is redirected elsewhere.
결과적으로, NodePort 또는 Loadbalancer를 사용할 때 트래픽이 다른 곳으로 리디렉션되면서 추가 홉이 발생할 수 있습니다.

Is this a problem?  이것이 문제인가요?

Not really.  그렇지 않아요.

Latency within a cluster network might be a handful of milliseconds, and this strategy ensures that all pods will serve the same number of requests.
클러스터 네트워크 내의 지연 시간은 몇 밀리초에 불과할 수 있으며, 이 전략은 모든 파드가 동일한 수의 요청을 처리하도록 보장합니다.

However, if you are running a latency-sensitive application, you might want to ensure that a pod always serves traffic on the local node.
그러나 지연에 민감한 애플리케이션을 실행하는 경우, 포드가 항상 로컬 노드에서 트래픽을 제공하도록 보장하고 싶을 수 있습니다.

In that case, you should use externalTrafficPolicy: Local in your service.
그 경우에는 서비스에서 externalTrafficPolicy: Local 를 사용해야 합니다.

ExternalTrafficPolicy: Local, preserving the source IP in Kubernetes

You have already encountered externalTrafficPolicy: Local twice so far:
당신은 지금까지 externalTrafficPolicy: Local 를 두 번 만났습니다:

  1. First, when you discussed NodePort, SNAT and preserving the IP address.
    먼저, NodePort, SNAT 및 IP 주소 보존에 대해 논의했을 때.
  2. Then, when discussing intra-cluster load balancing (and the extra hop).
    그런 다음, 클러스터 내 로드 밸런싱(및 추가 홉)에 대해 논의할 때.

What happens when you switch your services from externalTrafficPolicy: Cluster to externalTrafficPolicy: Local?
externalTrafficPolicy: Cluster 에서 externalTrafficPolicy: Local 으로 서비스를 전환하면 어떤 일이 발생합니까?

Let's find out.  알아보자.

frontend-service.yaml

apiVersion: v1
kind: Service
metadata:
  name: frontend-service
spec:
  type: NodePort
  externalTrafficPolicy: Local
  selector:
    app: frontend
  ports:
    - name: frontend
      protocol: TCP
      port: 80
      targetPort: 8080

Resubmit the resource to the cluster with:
클러스터에 리소스를 다시 제출하려면:

bash

kubectl apply -f frontend-service.yaml
service/fronted-service configured

You'll notice a few differences if you compare the rules for the externalTrafficPolicy: Cluster and Local iptables.
externalTrafficPolicy: ClusterLocal iptables의 규칙을 비교하면 몇 가지 차이점을 알 수 있습니다.

bash

iptables -t nat -L KUBE-EXT-6XQSOA4B4HLF6UNI -n -v --line-numbers
1  KUBE-MARK-MASQ               0.0.0.0/0            0.0.0.0/0
2  KUBE-SVC-6XQSOA4B4HLF6UNI    0.0.0.0/0            0.0.0.0/0

In Cluster mode, iptables always:
In Cluster 모드에서, iptables는 항상:

  1. Alters all external traffic source IPs to the node's IP through SNAT.
    모든 외부 트래픽 소스 IP를 노드의 IP로 SNAT를 통해 변경합니다.
  2. Invokes the Service's chain.
    서비스의 체인을 호출합니다.

Let's explore Local mode:
Local 모드를 탐색해 봅시다:

bash

iptables -t nat -L KUBE-EXT-6XQSOA4B4HLF6UNI -n -v --line-numbers
1  KUBE-SVC-6XQSOA4B4HLF6UNI    10.244.0.0/16    0.0.0.0/0
2  KUBE-MARK-MASQ               0.0.0.0/0        0.0.0.0/0 ADDRTYPE match src-type LOCAL
3  KUBE-SVC-6XQSOA4B4HLF6UNI    0.0.0.0/0        0.0.0.0/0 ADDRTYPE match src-type LOCAL
3  KUBE-SVL-6XQSOA4B4HLF6UNI    0.0.0.0/0        0.0.0.0/0

In Local mode, there are many more rules and a new chain: KUBE-SVL-6XQSOA4B4HLF6UNI.

Inspecting the new chain reveals that the traffic reaches the pod directly without SNAT:

bash

iptables -t nat -L KUBE-SVL-6XQSOA4B4HLF6UNI -n -v
1 KUBE-SEP-4B2TTHBRUYTSCT32  0.0.0.0/0   0.0.0.0/0  /* default/frontend-service -> 10.244.1.2:80 */

The KUBE-SEP-4B2TTHBRUYTSCT32 chain is the endpoint chain for the pod.

The only necessary translation is the destination NAT (DNAT) to the pod's IP — there is no SNAT.

What about all the other chains?

They are scoped to the local node so that traffic that consumes the service from the local node can still work (hence the ADDRTYPE match src-type LOCAL).

Those chains keep the Service running normally if the traffic originates from the local node and cluster.

While externalTrafficPolicy: Local preserves the source IP and routes traffic only to nodes with running pods, there are downsides:

While this answers the initial questions, it also raises another.

Does the type: LoadBalancer service know not to route traffic to nodes without a pod?

Yes.

When you use a LoadBalancer service in Kubernetes, the cloud provider's load balancer needs to know which nodes are healthy and ready to serve traffic.

It does this by regularly performing health checks on the nodes.

These checks typically target a NodePort and happen every 60 seconds.

If the node has a healthy pod running the service, it passes the check, and the load balancer routes traffic to it.

If the node does not have active pods for the service, it fails the check, and traffic stops being sent to that node.

When you set externalTrafficPolicy: Local, Kubernetes assigns a healthCheckNodePort to verify the health of the service's nodes.

You can check the assigned port with the following command:

bash

kubectl get svc frontend-service -o yaml | grep -i healthCheckNodePort
healthCheckNodePort: 31722

To check if the node is healthy, you can curl the health check endpoint like this:

bash

curl localhost:<healthCheckNodePort>/healthz

On a node with a healthy pod running the service, you'll see something like this:

bash

"localEndpoints": 1,
"serviceProxyHealthy": true
# truncated output

On a node without a pod, you might see:

bash

"localEndpoints": 0,
"serviceProxyHealthy": true
# truncated output

The load balancer uses this information to decide which nodes should receive traffic.

  • Nodes have /healthz endpoints and track if they have local pods for the current NodePort.
    1/2

    Nodes have /healthz endpoints and track if they have local pods for the current NodePort.

  • When they don't, the response is still successful, but the count is zero.
    2/2

    When they don't, the response is still successful, but the count is zero.

It will send traffic only to nodes with running pods for the service (in this example, the node with localEndpoints: 1).

Once the traffic reaches the NodePort, iptables' rules forward the request to the pod.

Having the load balancer check the pod's existence works well, but it also introduces a new challenge.

What if kube-proxy removes the chain from iptables rules just after the load balancer probes the health check?

For the next 60 seconds, the load balancer isn't aware that the pod is gone.

ProxyTerminatingEndpoints in Kubernetes

When you update the container image in your pods, Kubernetes replaces old pods with new ones with a rolling update.

As each new pod becomes ready, kube-proxy immediately updates the routing rules and removes the old pod's IP address from the iptables (or IPVS) NAT table to prevent traffic from reaching the terminating pod.

However, if the cloud LoadBalancer still uses the old IP (because its health check interval isn't due yet), it may continue sending traffic to a node without a healthy pod.

Requests are dropped until the next health check on the healthCheckNodePort.

The ProxyTerminatingEndpoints feature, introduced in Kubernetes v1.26, handles this issue by allowing terminating pods to remain available for existing connections.

Before v1.26, once a pod was marked as "terminating", it would stop serving traffic.

With ProxyTerminatingEndpoints, when a pod starts terminating, it's not immediately removed from active endpoints.

Instead, it is marked with the conditions terminating and serving.

This means the pod is shutting down but can still serve existing traffic until all tasks are complete.

If we take a look at the Endpoint object while the pod with IP 192.168.0.171 is terminating, it might look like this:

json

"endpoints": [
  {
    "addresses": ["192.168.0.171"],
    "conditions": {
      "ready": false,
      "serving": true,
      "terminating": true
    }
  }
]

kube-proxy has already removed the pod's IP from iptables to stop new traffic from reaching it.

However, because the pod is still marked as serving, it will continue processing existing connections until it is fully terminated.

After receiving the termination signal, the pod completes the request, performs a cleanup task, and only then shuts down gracefully.

Until now, you've learned about externalTrafficPolicy: Local and discussed some drawbacks and workarounds.

On the positive side, your pod knows the request's real source IP.

On the less positive side:

With great power comes a significant burden.

However, what if you could skip that and load balance the traffic directly from the load balancer to the pods (while skipping the nodes)?

How can the Pod's IP address be routable from the load balancer?

Pod IPs are non-routable in most Kubernetes environments because they exist within an internal Pod CIDR block.

To route traffic directly to the pods, their IPs must be part of a routable network that the external load balancer can see.

This can be achieved using certain Container Networking Interfaces (CNIs) that assign pod IPs from the same subnet as the node's network.

Azure Kubernetes Service offers the Azure CNI, in which pods receive IP addresses from the same Virtual Network (VNet) as the nodes, making them directly routable by the load balancer.

Another example is EKS with the AWS-CNI: the IP addresses are assigned from the VPC and can be routed by the Application Load Balancer.

  • When you use the AWS-CNI, all pods and nodes share IP addresses from the current VPC.
    1/2

    When you use the AWS-CNI, all pods and nodes share IP addresses from the current VPC.

  • When you use the Azure CNI, all pods and nodes share IP addresses from the current VNet.
    2/2

    When you use the Azure CNI, all pods and nodes share IP addresses from the current VNet.

This is only sometimes the case, though.

Taking AKS as an example again, with the default CNI, Kubenet, pods are assigned non-routable IPs from an internal CIDR block, meaning only the nodes are visible to the load balancer.

When using kubenet ins AKS, Pod's IP addresess are using an internal CIDR

When you provision a LoadBalancer Service with Kubenet, traffic forwarded by the load balancer reaches the nodes, and external traffic is routed to the pods through Network Address Translation (NAT).

Let's amend the previous AKS cluster, upgrade it to Azure CNI and set allocateLoadBalancerNodePorts: false.

This explicitly instructs the load balancer to bypass NodePorts and route traffic directly to pod IPs.

First, update your AKS cluster to enable Azure CNI with an overlay network configuration:

bash

az aks update --name $CLUSTER_NAME \
  --resource-group $RESOURCE_GROUP \
  --network-plugin-mode overlay \
  --pod-cidr 192.168.0.0/16

Next, apply the following LoadBalancer service configuration that disables NodePort allocation and ensures local traffic routing:

frontend-service.yaml

apiVersion: v1
kind: Service
metadata:
  name: frontend-service
spec:
  type: LoadBalancer
  allocateLoadBalancerNodePorts: false
  selector:
    app: frontend
  ports:
    - name: frontend
      protocol: TCP
      port: 80
      targetPort: 8080

You can apply the amended file to the cluster with:

bash

kubectl apply -f frontend-service.yaml
service/fronted-service configured

Let's inspect the IP addresses of your nodes and pods:

bash

kubectl get nodes,pods -o wide
NAME                                     STATUS   VERSION   INTERNAL-IP
node/aks-nodepool1-27397936-vmss000000   Ready    v1.29.8   10.224.0.5
node/aks-nodepool1-27397936-vmss000001   Ready    v1.29.8   10.224.0.4

NAME                                       READY   STATUS    IP              NODE
pod/backend-deployment-6b54b594f5-ds297    1/1     Running   192.168.1.125   aks-nodepool1-27397936-vmss000000
pod/frontend-deployment-84f666b996-5nlvb   1/1     Running   192.168.0.251   aks-nodepool1-27397936-vmss000001
pod/frontend-deployment-84f666b996-ldx22   1/1     Running   192.168.0.171   aks-nodepool1-27397936-vmss000001
pod/frontend-deployment-84f666b996-pk7qx   1/1     Running   192.168.1.178   aks-nodepool1-27397936-vmss000000

If you list the Services, you might notice the missing NodePort:

bash

kubectl get service
NAME               TYPE           CLUSTER-IP    EXTERNAL-IP   PORT(S)
backend-service    ClusterIP      10.0.96.133   <none>        3000/TCP
frontend-service   LoadBalancer   10.0.190.76   4.255.39.7    80/TCP

The node IPs (10.224.x.x) are part of the VNet's address space, and the pod IPs (192.168.x.x) are allocated from the pod CIDR we've configured in the VNet.

Both nodes and pods are now part of the routable network, which allows the load balancer to communicate directly with the pods without needing NodePorts or NAT.

Let's capture the traffic with a privileged container and install tcpdump on the node where the application is running.

Since the frontend deployment pods run on both nodes, we can choose any pod to capture the traffic.

bash

kubectl run -it --rm --privileged \
  --image=ubuntu \
  --overrides='{"spec": {"hostNetwork": true, "hostPID": true}}' \
  ubuntu -- bash

Let's make sure that tcpdump is installed.

bash

apt update && apt install -y tcpdump

Now, capture the traffic to one of the frontend pod IPs, such as 192.168.0.171:

bash

tcpdump -i eth0 host 192.168.0.171

On a terminal on your host machine, you may want to generate traffic with:

bash

curl http://4.255.39.7

Once you simulate requests, return to the tcpdump session.

The output may look like this:

bash

06:31:41.431009 IP 192.168.0.171.42222 > 192.168.1.125.3000: Flags [.], ack 1, win 502,
06:31:41.431327 IP 192.168.0.171.42222 > 192.168.1.125.3000: Flags [P.], seq 1:102, ack 1,
# truncated output

Now, the load balancer connects directly to the pod and routes traffic straight to the pod's IP.

The traffic flows straight from the load balancer 4.255.39.7 to the pod's IP 192.168.0.171 on port 80.

Routing traffic directly to pods and skipping NodePorts

The frontend pod processes the request and forwards traffic to the backend pod 192.168.1.125.

The request doesn't touch the node's IP at all.

This is possible because the load balancer sends traffic straight to the pod's IP.

As a benefit, the source IP is preserved, even without externalTrafficPolicy: Local.

Even though iptables rules are no longer involved in routing the external traffic, DNS lookups and service-to-service communication within the cluster still depend on CoreDNS, kube-proxy and iptables rules.

Summary

A special thank you goes to Michael O'Leary, who wrote the initial draft of this article and offered some invaluable feedback.

Your source for Kubernetes news
Kubernetes 뉴스의 출처

You are in!