개요
2024.05.18-[K8s] 네트워크 유형 - 컨테이너 간 통신, 2024.05.19-[K8s] 네트워크 유형 - Pod 간 통신에서 쿠버네티스의 4가지 통신 유형 중 2가지 유형을 살펴보았다. 이번 글에서는 Service의 개념과 Pod과 Service 간 통신에 대해 적어두려고 한다.
Service
Pod는 각각의 고유한 주소를 가지기 때문에 어떤 애플리케이션의 엔드포인트로 설정할 수도 있다. 하지만 쿠버네티스에서 Pod는 쉽게 대체되기 때문에 새로 생성된 Pod의 주소가 기존 Pod와 동일함을 보장하지 않는다.
이러한 문제를 해결하기 위해 애플리케이션 앞에 서버의 목록을 관리하며 서비스 중인 서버에게 트래픽을 전달하는 reverse-proxy나 load balancer를 구성할 수 있다. 이러한 동작을 위해 proxy는 내구성과 장애에 대응할 수 있는 능력을 갖추어야 하고, 트래픽을 전달할 서버의 목록과 해당 서버가 정상인지 확인할 방법을 갖고 있어야 한다.
쿠버네티스에서는 이러한 요구 사항을 만족하는 Service 리소스를 정의했다.
즉, Service는 Pod로 트래픽을 포워딩해 주는 proxy/load balancer라고 할 수 있다. Service에는 여러 유형이 존재하지만, 클러스터 내 모든 Pod가 접근할 수 있는 유형인 ClusterIP 유형이 기본이 되는 타입이다.
Service 네트워크
Service도 Pod와 마찬가지로 고유한 IP를 갖는데, 그 대역은 서로 다르다. Pod의 IP 대역과 Service의 IP 대역은 kubectl로는 확인이 불가능하며 클러스터 구축 방법마다 확인하는 방법이 조금씩 다르다. 자체적으로 구축한 경우에는 kubelet에서 –pod-cidr, 그리고 kube-apiserver에서 –service-cluster-ip-range로 확인할 수 있다. 이때 이러한 service의 주소 공간을 Service 네트워크라고 말한다.
Service 네트워크는 Pod 네트워크와 마찬가지로 가상 IP 주소지만, Pod 네트워크와는 조금 다르게 구성되고 동작한다.
다음과 같이 두 개의 노드가 Gateway로 서로 연결되어 있는 Pod 네트워크가 구성되어 있다고 하자.
이때 client pod가 service-test라는 DNS 이름으로 http 요청을 전달하면, 클라이언트 DNS 서버가 DNS 이름을 service IP(10.3.241.152)로 매핑시킨다. http 클라이언트는 매핑된 IP로 요청을 보낸다.
IP는 일반적으로 자신의 host에서 목적지를 찾지 못하면(IP 대역이 다르면) 상위 게이트웨이로 트래픽을 전달한다. 즉, client pod는 veth에서 IP를 보게 되지만 dest IP를 전혀 알지 못해 cbr(bridge)로 패킷을 전달한다. bridge는 단순히 오고 가는 패킷을 다음 노드의 인터페이스로 전달하기 때문에 패킷은 노드의 interface로 전달된다. 노드의 eth0로 dest IP에 대해서 알지 못하기 때문에 Gateway로 패킷이 전달되어야 하지만, 이 단계에서 kube-proxy에 의해 server pod로 패킷이 전달된다.
kube-proxy
kube-proxy는 리눅스 커널 기능 중 하나인 netfilter와 user 영역의 iptables를 사용해 트래픽을 제어한다.
netfilter는 커널 영역에 존재하는 proxy로, 커널 영역에서 오고 가는 패킷의 생명주기를 관찰하여 규칙에 일치하는 패킷을 발견하면 정해진 action을 수행하는 rule based 패킷 처리 엔진이다.
아래 그림은 kube-proxy가 user space proxy로 실행될 때의 netfilter의 역할을 나타낸 것이다.
kube-proxy는 service 요청을 받기 위해 예시 기준으로 10400 포트를 연다. 그리고 service IP로 들어오는 요청을 kube-proxy로 라우팅 되도록 netfilter에 설정한다. 이후 kube-proxy로 들어온 요청을 마스터 API 서버로부터 전달받은 실제 server POD의 IP:Port(10.0.2.2:8080)로 전달한다.
다만 user 영역에서 proxy를 수행하는 것은 모든 패킷을 커널 영역으로 변화해야 하는 비용이 필요하다. 때문에 쿠버네티스 1.2 버전 이후의 kube-proxy는 proxy 역할을 netfilter가 담당하고, kube-proxy는 netfilter의 규칙을 수정하는 것만을 담당한다.
kube-proxy는 system 서비스이나 daemonset으로 설치되기 때문에 프로세스가 예기치 못하게 중지해도 다시 동작할 수 있다. 따라서 자체적인 내구성을 가질 수 있다.
또한 kube-proxy는 마스터 API 서버로부터 정보를 받아 클러스터의 변화에 따라 iptables를 업데이트하며 netfilter 규칙을 최신화한다. service의 생성/삭제 시 kube-proxy에 알림이 가면서 관련된 규칙이 생성되거나 삭제되며, kubelet을 통해 서버의 health check가 이루어지고 문제 발생 시 마스터 API 서버를 통해 장애 pod의 엔드포인트를 제거한다.
Pod와 Service 간 통신
정리하면, Pod와 Service 간의 통신은 kube-proxy의 netfilter에 정의된 규칙에 의해 이루어진다.
참고 문서
https://coffeewhale.com/k8s/network/2019/05/11/k8s-network-02/
https://www.oreilly.com/library/view/kubernetes-cookbook/9781491979679/ch05.html