Kubernetes Networking - 1. Pod

squareBird·2022년 4월 20일
0

Kubernetes

목록 보기
12/17
post-thumbnail

Kubernetes Networking - 1. Pod

Kubernetes 환경에서 Pod들은 서로 문제없이 통신을 합니다.

저는 같은 Worker NodePod이 있다면, 동일한 Container Runtime환경에서 동작할 것이고 가상 IP 대역을 공유하기 때문에 통신이 잘 되는것이 맞다고 생각했습니다.

하지만, 다른 Worker Node에 존재하는 Pod들은 어떻게 서로 통신을 하는 것인지 궁금해졌습니다.

이에 대해 검색을 하던 도중 좋은 글들을 발견하여 저 나름대로 해당 글을 정리해보려고 합니다.

참고한 글
1. [번역] 쿠버네티스 네트워킹 이해하기 - 커피고래
2. [Kubernetes] Pause 컨테이너의 역할과 원리 (원문: The Almighty Pause Container) - alice님 블로그


Container

먼저 Pod의 네트워크에 대해 설명하기 전에, Pod을 통해 실행되는 Container들이 어떤 방식으로 통신하는지 알아보겠습니다.

위의 그림은 10.100.0.2라는 IP주소를 가진 한대의 Host에 하나의 Container가 실행된 것을 보여주는 그림입니다.

docker0은 해당 Host에서 사용하는 Container RuntimeDocker가 사용하는 네트워크 대역의 bridege이며, veth0Container 1의 가상 인터페이스입니다 두 인터페이스는 172.17.0.0/24라는 서브넷에 위치하고 있으며,docker0172.17.0.0/24 서브넷의 Default Gateway역할을 수행합니다.

Host에서는 docker0을 통해 Contaienr Runtime내부의 Container 1 과 통신이 가능합니다.

다음 그림은 해당 Host에 하나의 Container를 더 실행한 그림입니다.
기존의 Container 1과 동일한 서브넷에 Container 2가 생성되었습니다.

이 그림에서도 Hostdocker0을 통해 내부의 Container 1, Container 2와 통신이 가능합니다.
또한, Container 1Container 2docker0을 통해 서로 통신이 가능합니다.

Docker에서는 link를 이용해 Container들이 IP가 아닌 Container Name으로도 통신이 가능하게 해줄 수 있지만, link에 대해서는 여기서는 설명하지 않겠습니다.


Pod

같은 시리즈의 이전 글에서 Pod에 대해 정리하면서 PodKubernetes에서 Container가 배포되는 최소 단위이며, Pod안에는 하나 이상의 Container가 있다는 것을 알 수 있었습니다.

위에서 보여드린 그림들을 보면 하나의 인터페이스에 하나의 Container가 실행되고 있습니다.
실제로 Docker runtime이 설치된 환경에서 docker run nginx --name=nginx와 같은 명령어로 Container를 실행한 뒤 docker ps명령어를 수행하면 nginx라는 이름의 하나의 Container만이 동작하고 있는것을 확인할 수 있습니다.

그런데, 동일한 역할을 수행하는 ContainerKubernetes 환경에서 배포하면 어떻게 될까요?

kubectl run nginx --image=nginx 명령어를 수행한 뒤 docker ps 명령어를 수행하면 하나의 Container만 확인할 수 있을까요?

답은 그렇지 않습니다. KubernetesPod으로 Container를 배포한 뒤 docker ps명령어를 수행하면 commandpause인 컨테이너를 확인할 수 있습니다.

저는 분명 nginx 이미지로 Pod 하나를 배포해달라고 했는데 누가, 왜 마음대로 pause 컨테이너를 실행시킨 걸까요?


동일한 interface를 공유하는 Container

이번 그림은 두 개의 Container가 하나의 인터페이스를 공유하고 있습니다.
이것은 LinuxNamespace를 통해 가능한 일입니다.

우리는 Container 단위로 애플리케이션을 배포하지만 Container는 실제로 존재하는 것이 아닌 Container Runtime 환경이 LinuxNamespacecgroup을 적절히 활용하여 만들어낸 논리적으로 독립적인 공간입니다.

이제 외부에서는 veth0이라는 하나의 인터페이스를 통해 2개의 Container에 접속할 수 있고, 두 Container는 서로 localhost로 통신이 가능합니다.

하지만, 동일한 인터페이스를 공유하기 때문에 동일한 port를 동시에 사용할 수 없다는 단점도 있습니다. 예를들어 두 Container가 모두 WEB Server일 경우 하나의 Container에서 80번 Port를 사용하면 다른 하나의 Contaienr는 80번이 아닌 다른 port를 개방해 http서비스를 제공해야 합니다.

이것은 제약사항이기도 하지만 이전 글의 Multi Container Pod에서 설명드렸듯이 하나의 Pod은 하나의 책임만 가지는 것을 원칙으로 하기 때문에 큰 문제는 아닙니다.


Pause Container

Pod 섹션의 서두에서 말씀드린 것 처럼 Kubernetes의 한 Worker Node에 접속해서 docker ps라는 명령어를 수행하면 commandpauseContainer를 확인할 수 있습니다.

Container 환경에서 다수의 Container에 대해 공유되는 자원 환경의 수준은 누구나 Docker를 통해서 설정할 수 있습니다.

하지만, Container들 사이에서 공유되는 자원들에 대해서 정확히 인지하거나 생명주기를 관리하는 일은 쉬운 일은 아닙니다.

pause ContainerPod 내부의 Container들을 위해서 일종의 부모와 같은 역할을 수행합니다.

먼저, 같은 PodContainer들이 동일한 Linux Namespace를 공유할 수 있도록 해줍니다.
또한, Pod에서 PID 1(Init Process)로서의 역할 뿐 아니라 좀비 프로세스를 거두는 기능도 함께 수행합니다.

pause ContainerSIGTERM(Containr 삭제 명령)을 받기 전, 평상시에는 sleep상태로 존재하지만 핵심적인 역할을 수행합니다.

즉, Podpause ContainerPod가 자유롭게 생성되고 삭제되는 환경에서 사용자가 관리하기 어려운 Namespace의 공유나 좀비 프로세스의 리소스 회수 등의 일을 수행하며, 서로 다른 Container 및 바깥과의 통신을 담당하는 핵심 Container입니다.

결국 n개의 Container를 가진 Pod은 위와 같은 형태로 배포됩니다.

Podveth0이라는 하나의 가상 인터페이스를 사용하며, 사용자가 선언한 n개의 Container와 1개의 Pause Container로 구성됩니다.

PodPause Container를 사용해 veth0인터페이스를 생성하고, 다른 Container들은 해당 인터페이스를 공유합니다.


Container Network

Pod의 구조 다음으로는 어떻게 Pod들이 네트워크 통신을 하는지에 대해 알아보겠습니다.

먼저 네트워크의 구성도를 살펴보겠습니다.
위의 구성도에서 Worker NodeDocekr가 사용하고 있는 IP 대역은 다음과 같습니다.

Worker Node : 10.100.0.0/24
Docker Runtime : 172.17.0.0/24

먼저 위의 그림을 통해 알 수 있는 것은 Dockerdocker0이라는 bridege를 통해 전혀 다른 가상 IP대역을 사용한다는 것입니다.

10.100.0.2의 IP를 가진 Worker Node와 10.100.0.3의 IP를 가진 Worker Node는 각 Nodeeth0router/gateway를 이용해서 통신이 가능합니다.

10.100.0.2과 10.100.0.3의 Worker Node는 10.100.0.1을 Default Gateway로 하고 설정된 라우팅 테이블에 따라 패킷을 전달하게 됩니다.

만약, 왼쪽 그림의 Worker Node만이 존재한다고 가정해보겠습니다.

Default Gateway에 172.17.0.2을 Destination으로 하는 패킷이 도달할 경우 라우팅 테이블이 설정되어 있지 않다면 해당 패킷은 정상적으로 Container에 전달되지 않을 것입니다.
하지만, Default Gateway에 172.17.0.2로 가는 패킷은 10.100.0.2라는 IP를 가진 Worker Node로 라우팅해주는 정책이 설정되어 있다면, 해당 패킷은 Worker Node에 도달하고, Worker Node는 내부 iptables에 의해 해당 패킷을 docker0 브릿지로 전달할 것입니다.
그리고 docker0 브릿지는 veth0 인터페이스로 패킷을 전달하고, 해당 인터페이스는 port에 따라 적절한 Container에 패킷을 전달할 것입니다.

하지만, 위의 그림을 보면 두 Worker NodeDocker Runtime이 사용하는 IP대역이 172.17.0.0/24로 동일합니다.

이럴 경우 10.100.0.1의 Default Gateway에 172.17.0.0/24 대역에 대해 설정할 수 있는 라우팅 정책은 하나이기 때문에 두 Worker Node중 하나의 Docker Runtime 환경에는 패킷을 전달할 수 없게 됩니다.

환경 구성시 별다른 설정을 하지 않는다면 이렇게 Container Runtime의 IP대역이 겹치는 일은 어렵지 않게 발생 할 것입니다.


Kubernetes Pod Network

Kubernetes는 환경을 구성할 때 Contaienr Runtime환경끼리 IP가 겹치는 문제점을 해결해줍니다.

첫번째로, 각 Nodebridge IP가 겹치지 않도록, Kubernetes 환경에서 사용할 bridge의 IP 주소 대역을 생성하고, 각 Node에 위치한 bridge들에 해당 주소 대역안에서 겹치지 않는 주소 대역을 할당합니다.

Node별로 Container Runtime의 서브넷 대역을 나눠줍니다.
10.100.0.2 Node는 10.0.1.0/24 주소대역만을 사용하도록 하고, 10.100.0.3 Node는 10.0.2.0/24 주소대역만 사용하도록 하면 Container Runtime의 IP가 겹칠 수 있다는 문제점을 해결할 수 있습니다.

두번째로, 10.100.0.1이라는 Gateway에 어떤 Node로 가야 bridge를 통해 원하는 IP를 가진 veth0에 접근할 수 있는지 라우팅 테이블을 설정합니다.

이런 가상 네트워크 인터페이스와 bridge와 라우팅 rule의 조합을 overelay network라고 합니다.

위의 그림은 Kubernetes 네트워크의 구성도입니다.
위의 Gateway에는 Container Runtime으로 가는 트래픽들에 대한 라우팅 정책이 설정되어 있을 것입니다.

Kubernetes에서 실제 통신은 Service라는 추상화된 layer를 통해서 이루어집니다.
Service는 일종의 softeware-defined-proxy 역할을 수행하며 Kubernetes Cluster에서 Pod이나 Node가 변경되더라도 동일한 기능을 제공하는 애플리케이션에 접근할 수 있도록 해줍니다.


Default Gateway

위의 구성도에서 10.100.0.1이라는 IP를 가진 Gateway를 통해 Overray Network가 구성되며, 통신을 수행하게 됩니다.

그렇다면, 이 Gateway는 뭘까요?
Kubernetes 환경에 따라 다르지만 실제 Gateway 장비일수도 있고, 아니면 Node들에 Calico와 같은 CNI에 의해 설정된 Routing Table일수도 있습니다.

위의 환경은 임의로 구성한 Minikube Kubernetes ClusterNode에서 ifconfig 명령어를 수행한 화면입니다.
보시다시피 docker0라는 bridge를 확인할 수 있습니다.

위의 사진은 동일한 Node에서 route -n 명령을 통해 라우팅 설정 정보를 조회한 화면입니다.

보시다시피 172.17.0.0/16 대역은 docker0 인터페이스로 패킷을 전달하라는 설정 정책을 확인할 수 있습니다.

현재 환경은 Minikube로 구성되어있어 다른 NodeContainer Runtime으로 가는 패킷에 대한 정보가 기재되어 있지 않지만, 만약 여러대의 Worker Node가 존재할 경우

Worker Node들에서 route -n명령어를 수행했을 때,

10.0.1.0/24로 가는 패킷은 10.100.0.2로 전달하고,
10.0.2.0/24로 가는 패킷은 10.100.0.3으로 전달하는 설정값이 추가 될 것입니다.

profile
DevOps...

0개의 댓글