K8S Networking - Single Node, Multiple Pods (1)

jaytiger·2021년 11월 30일
0

들어가며

쿠버네티스 네트워킹을 이해하기 위한 여정을 떠나보자. 이번 여정의 1차 도착지로는 Kristen Jacobs 의 Talks들을 이해하는 것으로 잡았다. 꽤 도전적이다.

이 글은 다른 누군가를 이해시키기 보다는 내가 이미 알고 있는 지식과 어울려 새로운 것을 알아가는 과정을 정리한 글이다.

Kubernetes 개념 아키텍쳐

쿠버네티스(k8s) 의 개념적인 아키텍쳐로부터 구성요소들을 먼저 확인해 두자.

▶ Kubernetes Components

쿠버네티스 시스템을 구성하는 컴포넌트들은 대략 아래와 같은 계층 구조를 이루고 있다.

K8S Cluster ⊂ Node ⊂ Pod ⊂ Container ⊂ App

  • 쿠버네티스는 클러스터 단위로 배포된다.
  • 쿠버네티스 클러스터노드라고하는 워커머신(Worker Machine)들로 구성된다.
  • 노드포드(Pod)를 호스트한다.
  • 포드컨테이너화(Containerized)된 어플리케이션 워크로드를 실행한다.

* 요즘은 Application보다는 Workload라는 용어를 더 선호하는 것 같다.

쿠버네티스 네트워킹 종류

▶ Kubernetes Networking Type

  • 가장 작은 단위인 컨테이너화된 워크로드간 통신 (container-to-container)
  • 컨테이너화된 워크로드을 실행시키는 포드간 통신 (pod-to-pod)
  • 포드서비스간 통신 (pod-to-service)
  • 외부와 서비스 사이의 통신 (external-to-service)

서비스는 여러 포드위에서 실행되는 하나의 앱(Application)을 하나의 네트워크 서비스로 노출시키는 추상화된 방식

쿠버네티스 네트워킹 모델

*아래는 Kristen Jacobs의 발표자료에서 발췌한 내용

The network needs to satisfy the following (Kubernetes) requirements:

  • All containers can communicate with all other containers without NAT
  • All nodes can communicate with all containers (and vice-versa) without NAT
  • The IP that a container sees itself as is the same IP that others see it as

Container-to-Container 통신

K8s Networking Type 중에 가장 단순한 형태부터 시작하자. (Kevin Sookocheff의 blog 내용이 가장 쉽게 읽힌다. 이걸로 시작해 보자.)

보통 가상머신에서 네트워크 통신이라고 하면 Ethernet 디바이스와 직접 상호작용하는 것을 생각하는데 실상은 이것보다 조금 더 복잡하다.

▷ 단순화한 Ethernet 디바이스

리눅스에서 프로세스는 하나의 네임스페이스(namespace) 안에서 통신을 하게 된다. 이 네임스페이스는 경로(routes), 방화벽규칙, 네트워크 디바이스로 구성된 논리적인 하나의 네트워킹 스택을 제공한다.

TODO - namespace 란? (나중에 따로 정리토록 하자)

$ ip netns add ns1 
$ ls /var/run/netns
ns1
$ ip netns
ns1

위 스크립트에서처럼 생성된 네임스페이스는 /var/run/netns 아래 마운트되어 영구적으로 유지된다.

리눅스는 모든 프로세스를 root 네임스페이스에 할당하여 외부세계와의 통신을 제공한다.

도커 구성 관점에서 보면 하나의 포드는 한 그룹의 도커 컨테이너들로 모델링되고 이들은 네임스페이스를 공유한다.

하나의 포드안에 위치한 모든 컨테이너들은 같은 IP주소를 가지고 포드에 지정된 네임스페이스의 가용 Port 공간(Port Space)를 공유한다. 같은 네임스페이스내에 상주하기 때문에 localhost를 통해서 서로를 찾을 수 있다.

하나의 가상머신내 각각의 포드를 위한 네임스페이스를 생성할 수도 있다.

Docker의 경우 Pod 컨테이너는 개방된 네트워크 네임스페이스를 가지고 있고, App 컨테이너들은 이 네임스페이스에 도커의 -net=container: 기능을 이용하여 가입하게 만든다. (net=container:pause, Pod 컨테이너는 GCP의 Shared VPC에서의 host project 개념과 유사해 보인다.)

아래 그림은 어떻게 각 포드들이 하나의 공유 네임스페이스안에서 복수의 도커 컨테이너(ctr*)들로 구성되어 있는지를 보여준다.

Applications within a Pod also have access to shared volumes, which are defined as part of a Pod and are made available to be mounted into each application’s filesystem.

Linux의 IPC 장치나 Shared Volume을 통한 프로세스간 통신을 설명하기 위한 밑밥인 듯

Pod-to-Pod 통신

이어서 하나의 노드내 Pod간 통신에 대해서 살펴보자.

쿠버네티스에서 모든 Pod들은 각자의 IP를 가지고 있고 이를 통해서 Pod간 통신을 하게 된다. 실제 하나의 노드상에서 어떻게 Pod-to-Pod 통신이 이루어지는지를 보도록 하자.

Pod 관점에서 보면 Pod는 자신의 Ethernet 네임스페이스안에 존재하며 같은 노드의 다른 네임스페이스와 통신을 해야 한다.

다행스럽게도 네임스페이스는 Linux Virtual Ethernet Device를 통해서 연결될 수 있다.
veth pair 라고도 불리며 2개의 가상 인터페이스로 구성된 이것은 여러개의 네임스페이스에 걸칠 수 있다.
(namespaces can be connected using a Linux Virtual Ethernet Device or veth pair consisting of two virtual interfaces that can be spread over multiple namespaces.)

The veth devices are virtual Ethernet devices. They can act as tunnels between network namespaces to create a bridge to a physical network device in another namespace

Pod 네임스페이들을 연결하기 위해서는 veth pair의 한쪽을 root 네임스페이스에 연결하고 다른 쪽을 Pod의 네트워크 네임스페이스에 연결한다. 각 veth pair는 패치 케이블처럼 동작한다. 이런 셋업은 머신상의 Pod 수만큼 복제될 수 있다.

*patch cable - 양쪽에 RJ45 커넥터가 달려있는 보통 얘기하는 랜선

다음은 VM상의 각 Pod를 root 네임스페이스에 연결하는 veth pair들을 보여준다.

각 Pod는 각자의 네임스페이스를 가지고 Node의 root 네임스페이스에 연결되어 있다. Pod가 root 네임스페이스를 통해서 통신이 되어야 하는데 이런 목적으로 네트워크 브리지를 사용한다.

Linux Ethernet 브리지는 Virtual Layer 2의 네트워킹 디바이스로 두 개 혹은 그 이상의 네트워크 세그먼트를 transparent하게 묶는데 사용된다.

브리지는 출발지와 도착지간 포워딩 테이블을 유지하며 통과하는 데이터 패킷의 도착지를 살펴보고 연결된 다른 네트워크 세그먼트로 보낼지 말지를 결정한다. (MAC주소 사용)

Future traffic with the same IP address uses the lookup table to discover the correct MAC address to forward the packet to.

브리지는 ARP 프로토콜을 구현하고 있다. 이를 사용하여 주어진 IP주소와 연관된 Link Layer MAC 주소를 발견한다. 브리지는 데이터 프레임이 도착하면 그 프레임을 모든 연결된 기기에 브로드캐스트한다. 이 후 응답한 기기는 Lookup 테이블에 기록된다. 이후로 동일 IP 주소를 가진 트래픽이 들어오면 Lookup 테이블을 사용하여 올바른 MAC 주소를 찾아 패킷을 포워드시킨다.

*cbr0 - cutom bridge 0

동일 노드상의 Pod간 통신 - 패킷의 삶

네트워크 스택을 분리시키는 각각의 네트워크 네임스페이스가 주어지고, 가상의 이더넷 디바이스가 각 네임스페이스를 root 네임스페이스에 연결한 후, 브리지가 이들 네임스페이스들을 묶어주면 마침내 동일 노드상의 Pod들 간에 트래픽을 보낼 수 있는 준비가 되었다.

  1. Pod 1 이 하나의 패킷을 자신의 이더넷 디바이스인 eth0으로 보낸다.
  2. Pod 1 에서 eth0는 가상 이더넷 디바이스인 veth0를 통해 root 네임스페이스에 연결된다.
  3. cbr0 브리지는 veth0를 브리지에 연결된 하나의 네트워크 세그먼트로 구성하였다.
  4. 패킷이 브리지에 도달하면 브리지는 ARP 프로토콜을 이용하여 올바른 네트워크 세그먼트인 veth1를 찾아(resolve) 전달한다. -
  5. 패킷이 veth1에 도달하면 Pod 2의 네임스페이스의 eth0 디바이스로 바로 전달된다.

트래픽이 흘러가는 동안 각 Pod는 오직 localhost의 eth0와만 통신하고 있다. 그래도 트래픽은 경로를 따라 올바른 Pod에 도달한다.

Kubernetes’ networking model dictates that Pods must be reachable by their IP address across Nodes. That is, the IP address of a Pod is always visible to other Pods in the network, and each Pod views its own IP address as the same as how other Pods see it.

나가며

  • 단일 노드, 단일 네임 스페이스 - 네임스페이스와 노드를 연결하는 하나의 veth pair
  • 단일 노드, 2개의 네트워크 네임스페이스 - veth pair들 + 리눅스 bridge

다음 글에서는 다른 블로그의 내용을 참고하여 이해를 높여보도록 하자.

참고자료

profile
Jaytiger

0개의 댓글