Search

[번역] Kube-Proxy: 무엇이고 어떻게 동작하는가

Subtitle
kube-proxy의 기능과 역할 뜯어보기
Index
Kubernetes
Date
2025/02/15
2 more properties

소개

네트워킹은 쿠버네티스의 아주 중요한 파트입니다. 네트워크 컴포넌트가 어떻게 다르게 동작하는지 이해하는 것은 올바른 어플리케이션을 설계하는데 있어서 큰 도움이 됩니다.
쿠버네티스 네트워크 뒤에는 내부에서 작동하는 구성요소가 있습니다. 이는 서비스를 사용가능한 네트워킹 규칙으로 변환하는데 이를 kube-proxy라고 합니다.
이번 아티클에서는 kube-proxy가 어떻게 동작하는지 소개해드릴려고 합니다. 우리는 서비스가 생성될 때 만들어지는 플로우에 대해서 설명하면서 kube-proxy가 만드는 몇 가지 예시 규칙들에 대해서 살펴보려고 합니다.
만약 여러분이 기초 쿠버네티스 네트워킹에 익숙하지 않다면 이 블로그를 먼저 보고 오는 것을 추천합니다.

Kube-Proxy란?

우리는 쿠버네티스에서 파드가 임시적이라는 것을 알고 있습니다. 그들은 언제든 종료되고 재시작될 수 있죠. 이런 양상때문에 우리는 언제든 바뀔 수 있는 그들의 IP를 신뢰하지 못합니다.
Service 오브젝트는 바로 이 문제를 해결해줍니다. 서비스는 파드에 연결되는 안정적인 IP를 제공합니다. 각각의 서비스는 파드 그룹과 연관되어 있고, 만약 트래픽이 서비스에 도달한다면 서비스는 파드에 리다이렉트 시켜주죠.
하지만 어떻게 서비스에서 파드로의 이러한 매핑이 네트워킹 수준에서 구현이 될까요? 바로 이 역할을 kube-proxy가 해주고 있습니다.
kube-proxy는 쿠버네티스 클러스터의 모든 노드에 agent로 설치되어 있습니다. 그것은 서비스 오브젝트와 엔드포인트 간 일어나는 모든 변화를 모니터링하고 있죠. kube-proxy는 보통 Daemonset 형태로 클러스터에서 실행되지만 리눅스 프로세스 형태로 직접 노드에 설치할 수도 있습니다. 이는 클러스터 설치 유형에 따라 다릅니다.
만약 kubeadm을 이용한다면 데몬셋으로 설치될 것이고, 클러스터 컴포넌트들을 직접 수동으로 설치한다면 리눅스 공식 tarball 바이너리를 이용해 노드에 프로세스 형태로 직접 실행할 수도 있죠.

Kube-Proxy의 동작방식

kube-proxy는 설치 후 API 서버와의 인증을 거칩니다. 새로운 서비스나 엔드포인트가 추가되거나 삭제된다면 API 서버는 이러한 변화를 kube-proxy에게 전달합니다.
그러면 kube-proxy는 이 변화를 노드의 NAT 규칙에 반영합니다. NAT 규칙은 그냥 단순하게 서비스의 IP와 파드의 IP를 매핑해주고 있습니다. 트래픽이 서비스로 전달되면 서비스는 이 규칙에 따라 파드로 리다이렉트 시켜줍니다.
조금 더 디테일한 예시를 들어보겠습니다.
우리가 ClusterIP 타입의 서비스 SVC01을 가지고 있다고 가정해보겠습니다. 서비스가 생성되면 API 서버는 이 서비스와 관련있는 파드들을 체크합니다. 즉, 서비스의 label selector와 일치하는 label을 가진 파드를 찾습니다.
이 파드를 Pod01과 Pod02라고 하겠습니다. 이제 API 서버는 enpoint라고 불리는 추상화를 생성합니다. 엔드포인트는 각 파드를 대표하는 IP입니다(역자 주. 엔드포인트는 서비스가 트래픽을 전달하고자 하는 파드의 집합으로 서비스에 유효한 파드가 추가되면 부여됩니다.SVC01는 이제 2개의 엔드포인트와 결합되었고 이를 EP01과 EP02라고 하겠습니다.
이제 API 서버는 SVC01의 IP와 EP01EP02 IP를 매핑합니다.
X 파드와 연결된 서비스 생성
해당 라벨과 연결된 파드를 API 서버가 연결
이 모든 구성은 컨트롤 플레인의 일부분입니다. 우리가 이 매핑이 실제로 네트워크에서 구현되기를 원합니다. 이것이 적용되었을 때 트래픽은 SVC01의 IP로 오는 트래픽은 EP01, EP02로 전달됩니다.
여기서 kube-proxy가 필요하게 됩니다. API 서버는 이러한 업데이트를 각 노드에 있는 kube-proxy에게 전달하고 kube-proxy는 이러한 내부 규칙을 각 노드에 적용합니다.
이제 SVC01로 향하던 트래픽은 DNAT 규칙을 따라가게 되고 파드로 연결됩니다. EP01과 EP02 모두 기본적으로 파드의 IP임을 잊지마세요!
지금까지 살펴본 과정은 kube-proxy의 중요한 기능에만 포커스를 맞춘 가장 간단한 시나리오입니다. 하지만 언급할 가치가 있는 몇 가지 사항이 있습니다
1.
서비스와 엔드포인트는 IP 뿐 아니라 Port의 매핑도 합니다.
2.
예제에서 DNAT 변환은 소스 노드에서 발생했습니다. 이는 ClusterIP 유형의 서비스를 사용했기 때문입니다. 이것이 바로 ClusterIP가 클러스터 외부로 라우팅되지 않는 이유입니다. 기본적으로 내부 NAT 규칙이기 때문에 클러스터 내부에서만 엑세스할 수 있습니다. 즉, 클러스터 외부의 누구도 이 IP에 대해 알지 못합니다.
3.
만약 다른 타입의 서비스를 사용한다면 다른 규칙이 노드에 설치됩니다. 이러한 규칙은 chain이라고 불리는 별도의 공간에 배치될 수 있습니다. 이 주제를 벗어나기는 했지만 체인은 리눅스 내부의 규칙입니다. 그들은 특정 유형을 가지고 있고 특정 순서로 적용됩니다.
4.
NAT 규칙은 파드 중 하나를 랜덤하게 선택합니다. 하지만 이런 양상은 "kube-proxy 모드"에 따라 달라질 수 있습니다.
그러면 다음 주제를 알아보러 가보죠.

Kube-Proxy modes

kube-proxy는 다른 모드로 동작할 수 있습니다. 각 모드는 kube-proxy가 NAT 규칙을 어떻게 구현할 지를 결정합니다. 모드들은 각각의 장단점이 있고 우리는 그것들이 어떤 식으로 동작하는지 이해할 필요가 있습니다.

IPtables mode

이 모드는 디폴트이고 현재 가장 널리 사용하고 있습니다. 이 모드에서 kube-proxy는 IPtables이라는 리눅스의 기능을 사용합니다. IPtables는 내부 패킷 처리와 필터링 컴포넌트로 동작하는데요. 들어오고 나가는 트래픽을 검사하는 역할을 합니다. 그런 다음 특정 기준과 일치하는 패킷에 대해 특정 규칙을 적용합니다.
이 모드를 사용하면 kube-proxy는 서비스에서 파드로의 NAT 규칙을 IPtables에 주입하고 이를 통해 서비스 IP로 들어온 트래픽이 파드의 IP로 변환(NAT)되어 리다이렉션 되게 됩니다. 이제 kube-proxy은 "규칙을 설치하는 역할"로서 더 잘 설명할 수 있습니다.
이 모드의 단점은 IPtables이 테이블을 통과하는 순차적인 접근 방식을 사용했다는 것입니다. 이는 원래 패킷 필터링 용도로 설계되었기 때문인데요.
이런 순차적 알고리즘은 규칙이 많아질수록 적합하지 않습니다. 우리 시나리오에서는 서비스와 엔드포인트의 개수겠죠. 저수준에서 살펴보면, 알고리즘은 O(n)의 시간복잡도를 가집니다. 이는 룩업의 개수가 많아지만 규칙의 개수도 선형적으로 증가한다는 말입니다.
또한 IPtables는 로드 밸런싱 알고리즘도 지원하지 않습니다. IPtables는 랜덤 분배방식을 사용하고 있기 때문입니다.

IPVS mode

IPVS는 리눅스의 로드 밸런싱 기능을 위해 설계되었습니다. 이는 kube-proxy를 사용하는 것을 더 환상적인 선택으로 만들어줍니다. 이 모드에서 kube-proxy는 IPtables 대신 IPVS에 규칙을 저장합니다.
IPVS는 O(1)의 시간복잡도를 갖도록 최적화되어 있습니다. 얼마나 많은 규칙이 있든지 일정한 성능을 제공하는 것이죠. 우리 케이스에서 더 효과적인 서비스와 엔드포인의 연결을 의미합니다.
IPVS는 또한 라운드 로빈, 최소 연결, 기타 해싱 방식 등 다양한 로드밸런싱도 지원합니다.
이러한 장점에도 불구하고, IPVS는 모든 리눅스 시스템에 설치되어 있지는 않을 수 있습니다. 대부분의 리눅스의 핵심 기능인 IPtables와 대조적으로 말이죠. 또한 만약 그다지 많은 서비스를 가지고 있지 않다면 IPtables도 아주 잘 동작할 것입니다.

KernelSpace mode

이 모드는 윈도우 운영체제에만 해당합니다. 이 모드에서 kube-proxy는 윈도우의 Virtual Filtering Platform (VFP)를 사용하여 패킷 필터링 규칙을 주입합니다. VFP는 다시 패킷 캡슐화를 하고 대상 IP 주소를 파드의 IP로 대체하는 역할을 하는 리눅스의 IPtables와 동일하게 동작합니다.
만약 윈도우에서 VM이 익숙하다면, VM 네트워크로 사용하는 Hyper-V 스위치의 익스텐션으로 VFP를 생각해볼 수 있습니다.

Kube-Proxy mode 확인하는 법

기본적으로 kube-proxy는 10249 포트에서 실행되고, kube-proxy에 정보를 쿼리하는데 사용할 수 있는 엔드포인트 집합을 노출합니다. /proxyMode 엔드포인트를 사용하여 kube-proxy 모드를 확인해볼 수 있는데요. 첫번째로 노드 중 하나에 SSH 연결을 해봅시다. 그리고 curl -v localhost:10249/proxyMode
명령어를 실행해보죠.
kube-proxy가 IPtables 모드를 사용하고 있다는 것을 확인해볼 수 있네요.

ClusterIP 서비스에 대한 IPtables 규칙 확인하기

이제 IPtables의 규칙을 조금 더 자세하게 살펴보죠. 이를 위해 ClusterIP 서비스를 만들고 만들어진 규칙을 살펴보도록 하죠.
전제조건:
동작하는 쿠버네티스 클러스터
kubectl 설치
SSH 연결이 가능한 노드
먼저 2개의 레플리카를 가진 레디스 디플로이먼트를 생성합니다.
이제 파드가 만들어졌는지 확인해보죠.
2개의 파드를 생성함으로써 2개의 IP가 생성된 것을 볼 수 있습니다.
이제 이 파드들에 붙을 서비스를 만들어봅시다. 이를 위해 파드의 레이블에 매칭되는 셀렉터를 가진 서비스를 만듭니다.
서비스를 살펴보면 IP 주소를 가진 레디스 서비스를 살펴볼 수 있습니다.
YAML에서 서비스 유형을 따로 지정하지 않았었는데요. 이는 기본 유형이 ClusterIP이기 때문입니다.
만약 엔드포인트 리스트를 열거하면, 우리는 서비스가 파드에 연결된 2개의 엔드포인트를 가지고 있다는 것을 확인할 수 있습니다.
2개의 엔드포인트가 파드 IP를 대표하고 있다는 것을 볼 수 있습니다. 지금까지 모든 구성은 아주 간단합니다. 이제 그 안에서 일어나는 마법을 한 번 살펴보겠습니다.
우리는 노드 중 하나의 IPtables를 열거해볼겁니다. 먼저 노드에 ssh 연결을 한 뒤 아래의 명령어를 입력해야 합니다.
물론 지금 IPtables에 대해서 자세하게 다루지는 않을 예정입니다. 이번에는 우리 시나리오에서 중요한 정보만 다룰겁니다.
아까 전에 친 명령어의 옵션에 대해서 먼저 설명해봅시다. "-t nat"은 우리가 열거하기를 원하는 테이블 유형을 말합니다. IPtables는 많은 타입을 가지고 있고 그 중 kube-proxy는 NAT 테이블을 사용하고 있습니다. 이는 kube-proxy가 서비스 IP를 변환하는데 주로 IPtables를 사용하기 때문입니다.
chain 개념에 대해 설명했던 거 기억하시나요? "-L PREROUTING"은 테이블 안의 chain의 이름에 대한 옵션입니다. 이 체인은 IPtables에 기본적으로 존재하는 체인입니다. kube-proxy는 이 체인에 규칙을 연결합니다.
간단히 말해서 우리는 PREROUTING 체인 안에 있는 NAT 규칙을 열거하고자 하는 것입니다.
이제 명령어의 출력을 살펴보죠. 출력에서 가장 중요한 부분은 KUBE-SERVICES 라인입니다. 이는 kube-proxy가 서비스를 위해 만든 커스텀 체인인데요. 규칙이 모든 소스나 대상 트래픽을 이 체인으로 전달한다는 것을 알 수 있습니다.
즉, PREROUTING 체인을 통한 패킷들은 KUBE-SERVICES 체인으로 가는 것입니다. KUBE-SERVICES 안을 한 번 살펴보죠.
저기서 저 암호는 또 뭘까요? 간단히 말하면 몇 가지 체인이 더 있습니다.
여기서 중요한 것은 IP 주소입니다. 서비스의 대상 IP(10.99.231.137)에 대한 규칙으로 생성된 특정 체인을 확인할 수 있습니다. 이는 서비스로 향하는 트래픽이 해당 체인으로 들어온다는 것을 의미합니다. IP 오른쪽에 몇 가지 정보가 표시되는데요. 이는 서비스 이름, 서비스 유형, 포트를 나타냅니다.
이 모든 것들은 IPtables의 올바른 섹션을 보고 있다는 것을 보장합니다. 이제 이 체인을 살펴봅시다.
드디어 우리의 NAT 규칙에 왔습니다. 우리는 KUBE-SEP과 랜덤한 id를 가진 2개의 추가적인 체인들을 찾을 수가 있습니다. 이는 Service Endpoints (SEP)를 의미합니다. 그래서 각 체인에 대한 파드의 IP 주소를 볼 수가 있습니다.
혹시 중간에 statistic mode random probability 라인이 보이나요? 이는 IPTables가 두 개의 파드 사이에서 랜덤한 로드 밸런싱을 지원한다는 의미입니다.
이러한 KUBE-SEP 체인으로 들어가보면 기본적으로 DNAT(역자 주. Destination NAT의 약자로 도착지 주소를 변경하는 NAT) 규칙이라는 것을 볼 수 있습니다.
여기까지가 서비스에 대한 IPtables 규칙입니다. 이제 이 규칙을 자세히 살펴보는 방법을 알았으니 여러분의 환경에서 이런 규칙들을 더 많이 살펴볼 준비가 되었습니다!

FAQ

Q. 서비스는 프록시인가요?
A. 네 맞습니다. 서비스는 프록시처럼 동작합니다.
클라이언트가 연결할 수 있는 안정적인 IP를 제공하고, 이 IP로 전송된 트래픽은 파드 IP로 리다이렉션 됩니다. 이는 파드가 재성성될 때마다 IP가 바뀌는 문제를 해결해줍니다.
Q. kube-proxy가 로드 밸런서 역할도 할 수 있나요?
A. kube-proxy의 어느 부분을 의미하느냐에 따라 다릅니다.
만약 kube-proxy 에이전트 자체에 대해 이야기하는 것이라면, 답은 no입니다. kube-proxy는 실제 트래픽을 받거나 로드 밸런싱을 해주지는 않습니다. 단지 서비스 규칙을 만들어내는 컨트롤 플레인의 구성요소일 뿐입니다.
만약 kube-proxy가 만드는 규칙에 대한 이야기라면, 이때는 yes입니다. kube-proxy는 여러 파드에 걸쳐 트래픽의 부하를 분산하는 서비스 규칙을 생성합니다. 이 파드들은 특정한 서비스로 묶여있는 서로의 복제본입니다.

결론

kube-proxy는 서비스 정의를 네트워킹 규칙으로 변환하는 쿠버네티스 에이전트입니다. 클러스터의 모든 노드에서 실행되고 API 서버와 통신하여 업데이트를 수신합니다. 그런 다음 이런 업데이트는 노드 내부의 kube-proxy에 의해 이루어집니다.
이런 규칙을 만듦으로서, kube-proxy는 서비스로 보내진 트래픽이 올바른 파드로 갈 수 있도록 해줍니다. 이는 클라이언트와 파드 IP 간의 디커플링을 가능하게 해줍니다.

Reference