본문 바로가기

개발/docker + kubernetes

Kubernetes Networking : Part 1 - pods

 

크게 세 파트로 나눠 포스팅 할 예정이다.

 

1. 컨테이너, 파드

2. service

3. 클러스터 외부에서 파드로 들어오는 트래픽 확인

 

이러한 과정으로 모든 layer을 풀어서 쉽게 이해하고 이들간의 상호작용을 이해하도록 하자.

 

* 으로 표시된 단어는 포스팅 하단에 간단한 설명이 첨부되어 있습니다.

파드란?

 

파드는 Kubernetes application의 기본 단위다.

파드는 동일한 호스트에 배치된 하나 이상의 컨테이너로 구성되며 네트워크 스택 및 볼륨과 같은 다른 리소스를 공유하도록 구성된다.

그럼 "네트워크 스택을 공유한다"는 것은 어떤 의미일까?

이건 파드의 컨테이너가 로컬 호스트에서 서로 연결할 수 있다는 것을 의미한다.

 

 

 

예를 들면, 위의 Kubernetes Pod 1 은 2개의 컨테이너를 가지고 있다.

컨테이너 2가 scrapyd를 실행하는 동안 컨테이너 1이 nginx를 실행하고 80번 포트를 listening한다고 생각하면

이제 컨테이너 2는 http://localhost:80를 통해 컨테이너 1에 연결할 수 있다.

 

이 개념은 일반적인 예로, 로컬 컴퓨터에서 Docker 컨테이너를 시작하는 경우를 들 수 있다.

 

 

Image Source :  https://cdn-images-1.medium.com/max/800/1*0Xo-WpbTTGKZhJt7TvFLZQ.png

 

위의 이미지를 보면, 물리적인 네트워크 인터페이스 eth0가 있다.

그것에 연결된 *브릿지가 docker0이고,  가상 네트워크 인터페이스 veth0에 연결되어 있다.

여기서 중요한 것은 docker0과 veth0은 172.17.0.0/24인 같은 네트워크에 있다.

이 네트워크에서 docker0은 172.17.0.1이 할당되었고 veth0(172.17.0.2)의 *기본 게이트웨이다. 

컨테이너가 시작될 때 네트워크 namespace가 어떻게 구성되는지 프로세스는 내부에서 veth0만 보고 docker0 및 eth0을 통해 외부와 통신한다. 

 

이제 두번째 컨테이너를 이해해보자.

 

 

Image Address :  https://cdn-images-1.medium.com/max/800/1*ZdgIoY6tuOqK-r6wgL7d5A.png

 

두번째 컨테이너는 동일한 docker0 브릿지에 연결된 새로운 가상 네트워크 인터페이스인 veth1을 가져온다.

이 인터페이스(veth1)는 172.17.0.3에 할당되므로 브릿지 및 첫번째 컨테이너와 동일한 논리 네트워크에 있고,

두 컨테이너 모두 다른 컨테이너의 IP 주소를 어떠

가장 중요한 건 두 컨테이너가 다른 컨테이너의 IP 주소를 어떻게든 발견할 수 있으면, 브릿지를 통해 통신할 수 있다는 것이다.

 

하지만 이게 Kubernetes 파드의 "공유 네트워크 스택"으로 연결되는 건 아니다.

다행히 namespace는 굉장히 유연하다.

Docker는 컨테이너를 시작할 수 있고 컨테이너에 대해 새 가상 네트워크 인터페이스를 만드는 대신 기존 인터페이스를 공유하도록 지정할 수 있다.

여기에 대한 이미지는 위의 이미지들과 조금 다르다.

 

 

Image Address :  https://cdn-images-1.medium.com/max/800/1*akBBZKad2SAxSnJNaSHVmg.png

 

이렇게 되면 이제 두번째 컨테이너는 이전 이미지와 같이 자체 veth1을 얻는 대신 veth0을 보게 된다.

이건 뭘 의미하는 걸까? 

우선, 두 컨테이너 모두 172.17.0.2에서 이제는 외부로부터 주소 지정이 가능하며 내부에서는 localhost에서 다른 컨테이너가 열어 놓은 포트에 자유롭게 접근할 수 있다.

이는 두 컨테이너가 동일한 포트를 열 수 없다는 것을 의미하기도 한다.

이건 단일 호스트에서 여러 프로세스를 실행하는 경우와 다를바 없는 제한 사항이라고 보면 된다.

이러한 방식으로 일련의 프로세스가 컨테이너의 분리 및 격리를 최대한 활용할 수 있는 동시에 가장 심플한 네트워킹 환경에서 함께 공동작업할 수도 있다.

 

Kubernetes는 다른 컨테이너에 대한 네트워크 인터페이스를 제공하는 목적으로 각 파드에 특수 컨테이너를 생성하여 이 패턴을 구현한다. 

자, scheduled 파드를 가진 Kubernetes 클러스터 노드로 ssh하고 docker ps를 실행하면 pause 명령으로 시작된 하나 이상의 컨테이너가 표시된다.

pause 명령은 신호가 수신될 때까지 현재 프로세스를 일시 중단하여 Kubernetes가 SIGTERM을 보낼 때까지는 이 컨테이너가 절전 모드를 제외한 모든 작업을 수행하지 않는다.

 

이렇게 활동이 없는데도 불구하고 "pause" 컨테이너는 다른 모든 컨테이너가 서로 통신하고 외부와 통신하는 데 사용할 가상 네트워크 인터페이스를 제공하는 파드의 핵심이다.

그래서 가상 pod같은 것 내부에 이를 적용한 건 밑과 같다. 

 

Image Source :  https://cdn-images-1.medium.com/max/800/1*7JLi1Rl0G0FAeu-hiTGSGQ.png

 

파드 네트워크의 동작

 

이제부턴 파드 네트워크 시스템에 대해 이야기해 볼까한다.

"서비스"에 대해 알게되면 파드 네트워크를 이해하는데 꽤 도움이 된다.

자 그럼 지금부터 조금은 흥미진진한 얘기를 시작해보겠다.

 

Kubernetes 설계 요구 사항의 핵심은 로컬 호스트나 별도의 호스트에서 실행되는지에 관계없이 파드가 다른 파드와 통신 할 수 있어야 한다는 것이다.

이것이 어떻게 일어나는지 보기 위해선 레벨을 올려 클러스터의 노드를 살펴봐야 한다.

이 섹션은 네트워크 *라우팅과 라우트에 대한 언급이 불가피하기 때문에 IP 라우팅에 대한 지식이 있으면 더 이해하기 쉽다.

물론 여기에 관한 지식이 없으면 wikipedia에서 간단히라도 찾아보는 걸 ㅊㅊ한다.

 

 

 

Kubernetes 클러스터는 하나 이상의 노드를 포함한다. 

노드는 컨테이너 런타임과 종속성(주로 도커) 및 클러스터내 다른 노드에 연결할 수 있는 네트워크에 연결된  몇몇의 Kubernetes 시스템 구성요소를 가진 물리적 또는 가상의 호스트 시스템으로, 컨테이

또한 수많은 Kubernetes 시스템 구성요소로 이루어져 있다.

위 이미지는 완전한 Kubernetes 클러스터의 이미지이기 때문에 단순하게 만들어 보자.

 

음, 그래서 두 개의 노드가 있는 단순한 클러스터는 어떻게 생길까?

 

 

Image Source :  https://cdn-images-1.medium.com/max/800/1*XGG8e2tbP4bQbsS33gfwUw.png

 

 

각 노드의 상태가 클러스터의 일부이기 때문에 그 부분은 걱정하지 않아도 된다.

예를 들어, 각각의 노드가 잘 수행되지 않으면 이 모든 것을 관리해줄 사람이 있어야 한다.

모든 노드는 리소스를 함께 묶고 함께 강력한 머신을 만든다.

 

클러스터는 클라우드 플랫폼에서 실행된다.

GCP 또는 AWS와 같은 클라우드 플랫폼에서 클러스터를 실행하는 경우 단일 프로젝트 환경에 기본 네트워킹 아키텍쳐와 매우 비슷하다.

위 이미지에선 사설망 10.100.0.0/24을 사용했으므로, 라우터는 10.100.0.1이고 두 인스턴스는 각각 10.100.0.2와 10.100.0.3이다.

이 설정을 통해 각 인스턴스는 eth0에서 다른 인스턴스와 통신할 수 있다.

하지만 우리가 위에서 봤던 파드는 사설망에 있지 않다는 것을 기억하자.

파드는 가상 네트워크이고 특정 노드에만 존재하는 다른 네트워크의 브릿지에 매달려있다.

이 사실을 명확하게 하기위해 밑의 이미지와 같이 pod같은 것을 놓아보자.

 

다음 이미지를 보면 더욱 이해가 쉬울 것이다.

 

 

Image Source :  https://cdn-images-1.medium.com/max/800/1*RiLtoAdCfcJygwePVJzZOA.png

 

왼쪽 호스트는 10.100.0.2의 주소를 가진 인터페이스 eth0를 가지며, 기본 게이트웨이는 10.100.0.1의 라우터다.

이 인터페이스는 172.17.0.1인 브릿지 docker0와, 172.17.0.2인 인터페이스 veth0에 연결된다.

 

이전에 언급했던 pause 컨테이너가 기억나는가?

veth0 인터페이스는 pause 컨테이너로 생성되었고, 공유 네트워크 스택으로 세 컨테이너 모두에서 볼 수 있다.

브릿지가 생성될 때 설정된 로컬 라우팅 규칙으로 인해 목적 주소가 172.17.0.2인 eth0에 도착하는 모든 패킷은 브릿지로 전달되고 브릿지는 이를 veth0으로 보낸다.

 

지금까지 오 ㅇㅋ 하겠지만 아직 멀었다.

우리가 이 호스트에 172.17.0.2에 파드가 있다는 걸 알게 되면 라우터에 규칙을 추가하여 해당 주소의 다음 홉을 10.100.0.2로 설정하면 거기에서 veth0로 포워드되는 것이다.

 

오른쪽의 호스트도 10.100.0.3 주소를 갖는 eth0을 가지며 동일한 기본 게이트 웨이 10.100.0.1을 사용하고 172.17.0.1의 주소를 갖는 docker0 브릿지가 다시 연결된다.

하지만 이게 문제가 된다. 왜일까? 이제 이 주소는 host1의 다른 브릿지와 실제로 같지 않을 수 있기 때문이다.

이건 굉장히 최악의 경우기 때문에 위의 이미지에선 동일하게 만들었다.

선택한 네트워크가 다르더라도 근본적인 문제에 초점을 둬야한다.

뭔 소리냐면, 한 노드가 일반적으로 다른 노드의 브릿지에 할당된 사설 주소 공간을 모르고 있기 때문에 우리는 패킷을 보내려할 때 올바른 위치에 도착하게 해야한다.

패킷을 보내서 올바른 위치에 도착하게 하려면 분명히 어떠한 구조가 필요하다.

 

 

Image Source :  https://cdn-images-1.medium.com/max/800/1*oyGbXt7kStLd85ZT4it3oQ.png

 

Kubernetes는 두 가지 방식으로 이러한 구조를 제공한다.

첫째, 각 노드의 브릿지에 대해 전체 주소 공간을 할당한 다음, 브릿지가 구축된 노드를 기준으로 해당 공간 내의 브릿지 주소를 할당한다.

두번째로, 라우팅 규칙을 10.100.0.1인 게이트웨이에 추가하여 각 브릿지로 향하는 패킷을 라우트 해야하는 방식

즉, 브릿지가 도달할 수 있는 노드의 eth0을 알려준다.

이러한 가상 네트워크 인터페이스, 브릿지 및 라우팅 규칙의 조합을 일반적으로 오버레이 네트워크라고 한다.

Kubernetes대해 얘기할때 일반적으로 이 네트워크를 파드 네트워크라고 부른다.

파드가 모든 노드에서 앞뒤로 통신할 수 있게 해주는 오버레이 네트워크이기 때문이다.

위 이미지에서 브릿지의 이름을  "docker0"에서 "cbr"로 변경했다.

Kubernetes는 표준 도커 브릿지 장치를 사용하지 않고 "cbr"은 "custom bridge"의 약자이다.

이건 Kubernetes에서 실행되는 도커와 기본으로 설치된 도커 사이의 중요한 차이점 중 하나다.

위 이미지에서 브릿지에 할당된 주소 공간은 10.0.0.0/14이다.

또한 Google Cloud의 스테이징 클러스터 중 하나에서 가져온 것이므로 실제 사례다.

클러스터에 완전히 다른 범위가 할당될 수 있다.

안타깝게도, 현재로서는 kubectl 유틸리티를 사용해 이를 볼 방법은 없지만 GCP에서 "gcloud container clusters  describe <cluster>" 명령어를 실행해 "clusterIpv4Cidr" 등록정보(property)를 찾을 수 있다.

 

일반적으로 이 네트워크가 어떻게 작동하는지는 생각할 필요가 없다.

파드가 다른 파드와 대화할 때 가장 자주하는 일은 서비스의 추상화 즉, 다음 포스트에서 다루게 될 종류의 소프트웨어 정의(softwared-defined) 프록시다.

그러나 파드 네트워크 주소가 로그에 나타나고 디버깅할 때 일부 시나리오에서 이 네트워크를 명시적으로 라우팅해야 할 수도 있다. 

예를 들어, 10.0.0.0/8범위의 모든 주소에 Kubernetes 파드가 연결된 트래픽은 기본적으로 NAT가 아니므로 해당 범위의 다른 사설망에서 서비스와 통신하는 경우 패킷을 파드로 다시 라우팅하는 규칙을 설정해야 할 수도 있다.

 

 

 

* 라우터 : 패킷을 다른 네트워크로 보냄 + 최적의 네트워크 경로를 찾아줌

* 브릿지 : 호스트의 네트워크와 게스트의 네트워크를 브릿지하여(연결하여) 게스트 컴퓨터가 네트워킹 하는 방식

                즉, 호스트와 게스트를 하나로 연결해 두 개의 네트워크를 마치 하나의 네트워크처럼 쓰는 것

* 게이트웨이 : 네트워크에서 다른 네트워크(인터넷 등)로 이동하기 위해 반드시 거쳐야 하는 거점 (톨게이트와 비슷)

* 라우팅 : 어떤 네트워크 안에서 통신 데이터를 보낼 경로를 선택하는 과정

 

 

참고

https://medium.com/google-cloud/understanding-kubernetes-networking-pods-7117dd28727

https://medium.com/@tao_66792/how-does-the-kubernetes-networking-work-part-1-5e2da2696701

'개발 > docker + kubernetes' 카테고리의 다른 글

Kubernetes Networking : Part 2 - services  (0) 2019.06.22