본문 바로가기

[k8s] Kubernetes Network 본문

Infra

[k8s] Kubernetes Network

겨울바람_ 2024. 11. 8. 14:20

Overview

Kubernetes 환경에서 이루어지는 네트워킹은 크게 4가지로 분류할 수 있다.

  • 서로 결합된 컨테이너와 컨테이너 간의 통신
  • Pod와 Pod 간의 통신
  • Pod와 Service 간의 통신
  • 외부와 Service 간의 통신서로 결합된 컨테이너와 컨테이너 간의 통신Docker 환경에서 생성된 컨테이너의 기본적인 네트워크 동작 구조를 그림으로 표현하면 아래와 같다.


Docker에서는 기본적으로 같은 노드 내의 컨테이너끼리의 통신은 위 그림과 같이 docker0라는 가상 네트워크 인터페이스를 통해 가능하다.

 

각 컨테이너는 veth라는 가상 네트워크 인터페이스를 고유하게 가지며 따라서 각각의 veth IP 주소 값으로 통신할 수 있다. 하지만 두 컨테이너가 동일한 veth에 할당되는 경우도 존재한다.


위 그림에서는 두 개의 컨테이너가 모두 veth0라는 동일한 네트워크를 사용한다. 외부에서 바라봤을 때 두 개의 컨테이너는 동일한 IP로 인식되기 때문에 동일한 네트워크 상의 컨테이너는 port를 통해 구분된다.

 

즉, Pod 내에서 컨테이너는 각자 고유한 포트 번호를 사용해야 한다. 이러한 네트워크 인터페이스를 제공해주는 특별한 컨테이너가 있다.

 

pause라는 명령으로 실행된 컨테이너는 각 Pod마다 존재하며 다른 컨테이너들에게 네트워크 인터페이스를 제공하는 역할만 담당한다.

Pod와 Pod 간의 통신

  • 싱글 노드 Pod 네트워크


Pod의 특징 중 하나로 각 Pod는 veth를 통해 고유한 IP 주소를 가진다. 각 Pod는 kubenet 혹은 CNI로 구성된 네트워크 인터페이스를 통하여 고유한 IP 주소로 서로 통신할 수 있다.

 

  • 멀티 노드 Pod 네트워크


여러 개의 워커 노드 사이에 각각 다른 노드에 존재하는 Pod가 서로 통신하려면 라우터를 거쳐서 통신하게 된다.Pod는 기본적으로 쉽게 대체될 수 있기 때문에 Pod간 네트워크만으로는 쿠버네티스 시스템을 내구성 있게 구축할 수 없다.

Pod와 Service 간의 통신

이를 해결하기 위해 서비스 앞단에 reverse-proxy혹은 Load Balancer를 위치시키는 방법이 있다.

 

클라이언트에서 proxy로 연결을 하면 proxy의 역할은 서버들 목록을 관리하며 현재 살아있는 서버에게 트래픽을 전달하는 것이다.

 

이는 다음의 요구사항을 만족해야 한다.

  • proxy 서버 스스로 내구성이 있어야 하며 장애에 대응할 수 있어야 함
  • 트래픽을 전달할 서버 리스트를 가지고 있어야 함
  • 서버 리스트 내 서버들이 정상적인지 확인할 수 있는 방법을 알아야 함

쿠버네티스 리소스 중 Service가 이러한 요구사항을 만족한다. Service는 각 Pod로 트래픽을 포워딩해주는 프록시 역할을 한다.

 

이때, selector라는 것을 이용하여 트래픽을 전달받을 Pod들을 결정한다.

 

Service 또한 Pod 네트워크와 동일하게 가상 IP 주소지만, Pod 네트워크가 실질적으로 가상 이더넷 네트워크 인터페이스가 설정되어 ifconfig 명령어로 조회되는 것과 다르게 ifconfig 명령어로 조회할 수 없다.

 

또한 라우팅 테이블에서도 Service 네트워크에 대한 경로를 찾아볼 수 없다. 이는 Service와 Pod 간의 통신이 netfilterkube-proxy를 통해 이루어지기 때문이다.

Client Pod에서 Target Pod로 요청을 보내면 Pod의 이더넷 인터페이스가 해당 요청을 전달받은 후, 해당 Target Pod의 IP를 알지 못하기 때문에 다음 게이트웨이로 해당 요청을 전달한다.

 

veth0는 다음 게이트웨이인 cbr로 요청을 전달하지만, cbrbridge이기 때문에 해당 요청을 바로 다음 게이트웨이로 다시 전달한다.

 

이 전달 과정에서 netfilter가 해당 요청의 목적지 IP를 실제 Target Pod의 IP로 변환하여 다음 게이트웨이로 전달한다. 이후 최종적으로 eth0에 의해 Target Pod로 요청이 라우팅되게 되는 것이다.

 

쿠버네티스 1.2 버전 이후에서는 해당 패킷의 주소 변환 과정의 비용을 줄이기 위해 kube-proxy의 역할을 netfilter의 규칙을 알맞게 수정하는 것만을 담당하는 것으로 축소시켰다.

 

위의 그림 또한 쿠버네티스 1.2 버전 이후를 예시로 들어 그려진 것이다.

 

kube-proxy는 마스터 노드의 API Server에서 정보를 수신하기 때문에 클러스터의 변화를 감지할 수 있고, 이를 통해 지속적으로 iptables를 업데이트하여 netfilter의 규칙을 최신화한다. 하지만 이러한 방식은 클러스터 내부에 위치한 Pod간 통신에서만 동작한다.

 

외부에서 들어온 요청에 대해서는 다른 처리 방식을 사용해야 한다. 외부의 요청을 처리하는 대표적인 방식이 바로 NodePort, Load Balancer, Ingress다.

 

NodePort, Load Balancer, Ingress에 대해서는 다음 포스팅에서 자세히 다뤄볼 예정이다.

Comments