K8s 파드 간 네트워킹

snooby·2024년 11월 2일
0

🐳 kubernetes

목록 보기
57/63
post-thumbnail

쿠버네티스의 각 파드는 고유한 IP 주소를 가지고 (중복이 절대 없음) 다른 모든 파드와 NAT 없이 플랫 네트워크로 서로 통신하고 있다.

이게 어떻게 가능할까…?
오늘은 이것을 알아보자.

정답은 먼저 말하고 들어가자면,
쿠버네티스가 하는 건 아니다!!

네트워크는 쿠버네티스 자체가 아닌, 시스템 관리자 또는 컨테이너 네트워크 인터페이스 플러그인 (CNI)에 의해 제공된다.

쿠버네티스 네트워크 방향

쿠버네티스는 특정한 네트워크 기술을 사용할 것을 요구하지는 않지만, 파드가 동일한 워커 노드에 떠있듯 아니든 상관없이 파드끼리 서로 통신할 수 있어야한다.

그러기 위해, 파드가 서로 통신할 때 통신을 받는 파드는 해당 통신을 쏜 출발지 파드의 IP 주소를 그대로 받아야한다.


사진의 예시를 보자.
파드 A가 네트워크 패킷을 파드 C에게 보내기 위해 연결할 때,
파드 C가 보는 출발지 IP는 파드 A의 IP 주소(10.1.1.2)와 동일해야한다.

즉, 패킷은 네트워크 주소 변환 NAT 없이 파드 A에서 파드 B로 출발지와 목적지 주소가 변경되지 않은 상태로 도착해야한다.

이것은 매우 중요하다!!
파드 내부에서 실행중인 애플리케이션의 네트워킹이 동일한 네트워크 스위치에 접속한 시스템에서 실행되는 것처럼 간단하고 정확하게 이뤄지도록 해주기 때문이다.
파드 사이에 NAT가 없으면 내부에서 실행중인 애플리케이션이 다른 파드에 자동으로 등록되도록 할 수 있다.

위 사진 예시를 다시 들어보자.
파드 A가 파드 C에 접속할 때 “나는 파드 A이고 IP는 10.1.1.2입니다. 갱신되는 내용은 이 IP 주소로 보내주세요.“
서비스를 제공하는 파드는 수신한 IP 주소를 이용해 파드 A에 접속할 수 있다.

파드 간 NAT 없이 통신해야한다는 요구 사항은 파드와 노드 / 노드와 파드 간에 통신할 때 동일하게 요구된다.

하지만!! 파드가 인터넷에 있는 서비스와 통신할 때는 패킷의 출발지 IP를 변경하는 것이 필요하다.
Why? 파드의 IP는 사설이기 때문이다.
외부로 나가는 패킷의 출발지 IP는 호스트 워커 노드의 IP로 변경된다.

적절한 쿠버네티스 클러스터를 구축하려면 이러한 요구사항에 맞춰 네트워킹 설정을 해야한다.

네트워킹 동작 방식

puase 컨테이너

파드의 IP 주소와 네트워크 네임스페이스는 인프라스트럭처 컨테이너 (pause 컨테이너)에 의해 설정되고 유지되는 된다.

쉽게 말해, 사진에서 보이는 것처럼 pause가 pod내 다른 모든 container와 networkspace를 공유하고 있습니다.

퍼즈 컨테이너는 네임스페이스를 모두 보유하는 게 유일한 목적인 인프라스트럭처 컨테이너로 퍼즈 컨테이너가 갖고 있는 네트워크 네임스페이스를 사용해 pod 내 모든 컨테이너는 동일한 네트워크와 리눅스 네임스페이스를 공유할 수 있는 겁니다.

pause 컨테이너를 알고 싶다면 해당글을 읽어보세요.
https://velog.io/@baeyuna97/K8s-pause-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88![](https://velog.velcdn.com/images/baeyuna97/post/703bdbf1-f3c4-42aa-88d1-3374b9ca01e1/image.png)

리눅스 네트워크 네임스페이스

네트워크 네임스페이스란, 하나의 호스트에서 여러 개의 격리된 네트워크 공간을 형성하는 기술이다.

각 네트워크 네임스페이스마다 네트워크 인터페이스, 라우팅 테이블, 포워딩 테이블을 가지며,
격리된 네임스페이스에 할당된 프로세스는, 다른 네임스페이스에 영향을 줄 수 없다

리눅스는 네트워크 외에도 다양한 네임스페이스를 지원하며,

네임스페이스는 컨테이너(container) 기반의 가상화 기술을 지탱하는 주요 기능.

파드간의 통신

인터페이스를 생성하는 방법과 생성한 인터페이스를 모든 다른 파드 인터페이스에 연결하는 방법을 살펴보자.

인프라스트럭처 컨테이너가 시작되기 전에, 컨테이너를 위한 가상 이더넷 인터페이스 쌍 (veth 쌍)이 생성된다.
이 쌍의 한쪽 인터페이스는 호스트의 네임스페이스 (노드에서 ifconfig를 실행할 때 볼 수 있는 )에 남아있고, 다른 쪽 인터페이스는 컨테이너의 네트워크 네임스페이스 안으로 옮겨져 eth0으로 변경된다.

두 개의 가상 인터페이스는 파이프의 양쪽 끝과 같다.
= 한쪽으로 들어가면 다른 쪽으로 나온다.

호스트의 네트워크 네임스페이에 있는 인터페이스는 컨테이너 런타임이 사용할 수 있도록 설정된 네트워크 브리지 (docker 0)에 연결된다.

컨테이너 안의 eth0 인터페이스는 브리지의 주소 범위 안에서 IP를 할당 받는다.
컨테이너 내부에서 실행되는 애플리케이션은 eth0 인터페이스(컨테이너 네임스페이스의 인터페이스)로 전송하면, 호스트 네임스페이스의 다른쪽 veth 인터페이스로 나와 브릿지 (docker 0)로 전달된다.
이는 브리지에 연결된 모든 네트워크 인터페이스에서 수신할 수 있다는 것을 의미한다.
(브릿지로 연결된 곳은 브릿지로 패킷 보내면 브릿지 연결된 다른 곳으로 보낼 수 있다는 의미이므로)

그래 알겠다.. 그런데.. 다른 노드에서 실행중인 컨테이너랑 통신하려면 그럼 브릿지가 어떻게 있어야하지..?
사진에 보이는 브릿지 (docker0)은 동일 호스트 노드 안에 있는데.. 호스트 노드 밖의 컨테이너는 이 브릿지에 연결되어 있지 않지 않은가?

서로 다른 노드에서 파드간의 통신 활성화

서로 다른 노드 사이에 브리지를 연결하는 방법은 여러가지가 있다.
오버레이, 언더레이 네트워크 아니면 layer 3 라우팅..

파드 IP 주소는 전체 클러스터 내에서 유일해야 하기 때문에, 노드 사이에 브리지는 겹치지 않는 주소범위를 사용해 다른 노드에 있는 파드가 같은 IP 주소를 얻지 못하도록 해야한다.

Layer 3 네트워킹으로 두 노드에서 노드 간 통신을 가능하게 하려면 노드의 물리 네트워크 인터페이스(노드의 eth0)도 브리지에 연결해야한다.
노드 A의 라우팅 테이블은 10.1.2.0/24로 향하는 모든 패킷이 노드 B로 전달되도록 설정해야하고, 노드 B에서는 10.1.1.0/24로 향하는 패킷이 노드 A로 전달되로고 설정하는 것이 필요하다.

이러한 유형의 설정을 사용하면 패킷을 다른 노드에 있는 컨테이너로 보낼 때 패킷이 먼저 veth 쌍을 통과한 다음 브리지를 통해 노드의 물리 어댑터로 전달된다.
이것은 두 노드가 라우터 없이 같은 네트워크 스위치에 연결된 경우에만 동작한다.
그렇지 않다면 라우터는 패킷이 참조하는 파드의 IP가 프라이빗 대역에 속하기 때문에 패킷을 삭제한다. (물론 우회 설정은 있지만 복잡해 진다..)

SDN (소프트웨어 정의 네트워크)를 이용하면 하부 네트워크 토폴로지가 아무리 복잡해지더라도 노드들이 같은 네트워크에 연결된 것을 볼 수 있다.
파드에서 전송한 패킷은 캡슐화되어 네트워크로 다른 파드가 실행 중인 노드로 전달되고, 디캡슐화 단계를 거쳐 원래 패킷형태로 대상 파드에 전달된다.

컨테이너 네트워크 인터페이스

컨테이너 네트워크를 쉽게 연결하기 위해, CNI (컨테이너 네트워크 인터페이스)를 사용한다.
Ex) calico, cilium 등..

컨테이너 네트워크 인터페이스는 하나하나 보자면 길어지므로 해당 내용은 따로 포스팅을 작성하도록 하겠습니다.

profile
DevOps 🐥

0개의 댓글