새소식

Recent Study/Kubernetes 학습과정

20. Networking - Pod / Service Network(Calico), Pause Container

  • -

 

 

 1. 먼저 Master, Worker 노드들이 있고, Pod Network에 대한 영역이 있습니다. 처음 클러스터 설치 시 pod-network-cidr로 이 네트워크 대역에 대한 영역을 설정했던 부분입니다. Pod Network에 대해 알아볼 영역은 Pod내에 컨테이너로 가는 네트워킹을 하는 부분입니다. Pod가 생성되면 Pod Network 범위 내에서 고유 IP를 가지고 있는 인터페이스가 생기는데 이것을 통해서 어떻게 여러 컨테이너들 간에 통신이 되는지 알아보겠습니다. 그리고 또 다른 Pod가 생성되었을 때 이 두 Pod간의 통신은 k8s에서 Node마다 설치가 되는 Network Plugin에 의해 통신이 됩니다.

 

 k8s에서 기본적으로 제공하는 큐브넷이라는 네트워크 플러그인이 있는데 이건 네트워크 기능에 많이 제한적이라 많이 사용되지 않고, CNI(Container Network Interface)라고 해서 이것을 통해 다양한 오픈소스 네트워크 플러그인들을 설치할 수 있습니다. 그래서 이 네트워크 플러그인이 하는 역할은 같은 노드 위에 Pod들 간의 통신과 그리고 외부 네트워크를 통한 타 노드 위에 있는 Pod들 간의 통신을 담당합니다. 

 

 

 2. Service Network는 Pod에 Service를 붙이게 되면 Service도 고유 IP가 생성되고, 이 Service가 생성됨과 동시에 kube-dns에 이 서비스 이름과 IP에 대한 도메인이 등록됩니다. 그리고 api-server가 워커 노드들마다 Pod의 형태로 띄워져있는 kube-proxy에 이 서비스의 IP가 어느 IP와 연결이 되어있는지 정보를 알려줍니다.

 

 이제 여기서 Service IP를 Pod IP로 바꾸는 NAT 기능이 필요한데 kube-proxy가 iptables나 IPVS를 어떻게 이용하느냐에 따라서 Proxy Mode(user space, iptables, ipvs) 라고 해서 작동 방식이 세 가지가 있습니다. 이 상태에서 Pod가 Service 이름을 호출하게 되면 위처럼 (1) kube-dns를 통해 IP를 알게되고, 얻은 (2) Service IP를 NAT 영역으로 호출을 하게 되는데, 여기에 해당 Service에 대한 Pod 맵핑 정보가 있기 때문에 (3) 트래픽은 Network Plugin인을 통해 해당 Pod로 가게 됩니다.  

 

 결국 Service Object의 실체는 이 NAT 영역 내의 설정이고 Service를 삭제하게 되면 다시 api-server가 이를 감지해서 kube-proxy에게 이 설정을 지우라고 합니다.

 

 

Pod Network (with Calico)

1. Pause Container

 

Pause Container

 

: Pod를 만들게 되면 본인이 생성한 Container도 있겠지만, Pause Container라고 해서 네트워킹을 담당하는 컨테이너가 자동으로 생깁니다. 이 컨테이너에 인터페이스가 달려있고, IP도 생기는데 k8s가 이 Pause Container의 Network Namespace를 이 Pod내에 모든 컨테이너들한테 같이 쓰도록 구성을 해줍니다. IP에 대한 네트워크를 같이 공유하게 되고, 컨테이너간의 공유는 Port를 통해 할 수 있게 됩니다. 한편 이 Worker Node에 Host Network Namespace가 있고, 여기에 이 호스트 IP 인터페이스가 있는데, Pause Container가 생기면 이 Host Network Namespace에 가상 인터페이스가 하나 생기면서 Pause 컨테이너의 인터페이스와 연결이됩니다.

 

Pod를 만들 때마다 이렇게 가상 인터페이스가 생겨서 1대1 매칭이 되고, Worker Node에서 IP와 Port를 날려보면 20.111.156.66 :8000 이렇게 트래픽이 흐르게 됩니다. 이렇게 외부에서 Pod로 들어오는 트래픽을 받는 것과 특정 컨테이너로 트래픽을 전달하는 건 Pause Container가 리눅스의 Network Namespace를 별도로 생성해서 가상의 인터페이스를 만들고, 그것을 관련 컨테이너들에게 공유하고 있기 때문에 가능한 것이라고 알고 있으면 됩니다. 

 

 

 

2. Network Plugin (kubenet, cni)

 

 

 먼저 위에서 설명했던 것은 Pod를 생성하면 새로운 Network 인터페이스를 만드는데 이 부분이 Pod Network에 관한 부분입니다. 그리고 나머지 위 부분이 Host 자체 네트워크 입니다. 그래서 Pod의 인터페이스와 Host 네트워크의 가상 인터페이스가 연결이 된다는 내용이었습니다.

 

 1) kubenet [왼쪽 사진]

: 여기서부터 Network Plugin인 kubenet이 하는 역할은 이 가상 네트워크를 cbro0 이라는 컨테이너 bridge에 포함을 시켜줍니다. 그리고 이 bridge 네트워크 대역은 Pod 네트워크 대역을 참고해서 이보다 낮은 단계의 대역으로 세팅이 됩니다. 그리고 이 bridge 내에서 생성되는 Pod IP는 이 bridge CIDR 범위 내에서 만들어집니다.

 

bridge에 세팅된 CIDR을 보면 이렇게 총 255개의 Pod IP를 할당할 수 있다고 보면 되고, 한 노드 위에 그 이상은 안만들어집니다. 이것이 kubenet을 썼을 때 단점 중 하나이기도 하고, 이렇게 bridge를 통해 Pod간의 통신이 가능해지는 것 입니다. 또한 Network Plugin에 Router 역할도 있는데 NAT를 통해 기능이 제공되고, 얘가 하는 일은 IP가 Pod IP 대역이면 해당 트래픽은 bridge 쪽으로 내려주고, 그 외의 IP에 대해서는 위로 올려줍니다.

 

2) cni : Calico [오른쪽 사진]

: Calico 플러그인은 Host Network에 생성된 가상 인터페이스가 Router에 바로 연결되어 있는 구조입니다. 때문에 Pod 간 통신은 이 Router가 담당하고, 여기에 CIDR은 kubenet보다 더 큰 범위를 가지고 있어서 한 노드에서 Pod에게 더 많은 IP를 부여할 수 있습니다. 또한 제공되는 보안적인 요소가 많은데 firewall 같은 부분에서 그런 기능을 처리해줍니다. 그리고 Router 윗 단에는 Overlay 네트워크를 제공해주는 층이 있습니다. IPIP, VXLAN 방식이 있고 Overlay가 하는 일은 이 노드 위에 있는 Pod가 타노드 위에 있는 Pod와 통신을 할 수 있도록 해줍니다. 

 

 

Pod D -> Pod B

 Calico 플러그인을 썼을 때 각각의 워커노드 위에 있는 Pod들이 어떻게 간략하게 통신되는지 알아봅시다. Pod D -> Pod B로 트래픽을 날린다고 했을 때 D에서는 B의 IP인 111.156.7로 호출을 하면 이게 그대로 Router에 있는 가상 인터페이스를 지나 해당 IP는 현재 Router 내에 없기 때문에 Overlay 네트워크 층까지 올라갑니다.

 

 Calico는 이 Pod의 IP 대역이 어느 노드에 있는지 알고 있기 때문에 이 Overlay 네트워크 층에서 패킷을 위처럼 해당 노드의 IP로 변경을 해주고 실제 Pod의 IP는 안에 숨겨져 있습니다. 이를 Pod의 IP가 인캡슐레이션 되었다고 하는 것 입니다. 그래서 이 트래픽은 22번 Host Interface로 가게 되고 노드 1번으로 들어온 트래픽이 이 노드의 Overlay 네트워크 층에서 디캡슐레이션 되면서 원래의 Pod IP로 변환이 됩니다. 그리고 이 IP 대역에 있는 라우터로 트래픽이 전해지고, 라우터 안에서 해당 IP에 맞는 가상 인터페이스를 지나 최종적으로 요청한 Pod까지 도착을 하게 됩니다.

 

 

Service Network (with Calico)

Proxy 모드에 대한 트래픽 흐름

 

1. Proxy Mode

: k8s가 제공하는 서비스 네트워크의 프록시 모드에는 3가지 종류가 있고, 이 3가지 모드를 설명하기 위한 전제 상황부터 설명을 하면 Pod를 만들고 서비스를 붙이는데 Pod가 정상적으로 기동된 상태라면 중간에 Endpoint라는 오브젝트가 생성이 돼서 실제 연결 상태를 담당합니다. Service IP는 서비스 네트워크 CIDR 범위 내에서 생성이 되는 것 입니다. 이때 api-server는 Endpoint를 감시하고 있다가 모든 노드 위에 데몬셋으로 설치되어 있는 kube-proxy에게 이 Service의 IP는 해당 Pod의 IP로 포워딩 된다는 정보를 줍니다.

 

 Userspace Mode 

: 리눅스 워커 노드에 기본적으로 설치되어 있는 iptables에 Service CIDR로 들어오는 트래픽은 모두 kube-proxy에게 주도록 설정이 되어 있습니다. 만약 Pod에서 Service IP를 호출할 경우 이 트래픽은 iptables를 거쳐 kube-proxy한테 가게 됩니다. 그리고 해당 kube-proxy는 자신에게 있는 맵핑 정보를 보고 이 트래픽을 Pod IP로 바꿔줍니다. 이렇게 Pod IP로 변경된 트래픽은 다음부터 Pod Network Area 으로 통신이 되어 위처럼 Pod가 전달되는 방식입니다. 단점은 모든 트래픽이 kube-proxy를 지나야 되는 구조인데, 모든 트래픽을 감당하기에는 이 kube-proxy 자체 성능이나 안정성이 좋지 않습니다.

 

Iptables/IPVS Mode

: kube-proxy가 해당 ip 맵핑 정보를 iptables에 직접 등록하는 방식이라 이 Pod에서 보내는 Service IP는 iptables에서 집접 Pod IP로 변환이 됩니다. 이는 기본 모드이고 안정성이 위보다 더 좋습니다. 그리고 리눅스에서는 IPVS라고 L4 로드 밸런서라는 걸 제공하는데, Service 모드를 IPVS 모드로 설정을 하면 얘가 iptables와 같은 역할을 해줍니다. 성능은 비슷하지만 부하가 커질수록 IPVS가 더 좋다고 합니다.

 

 

 

2. Service Type (ClusteIP, NodePort)

Calico Plugin(ClusterIP)

 

: Calico Plugin 사용시 서비스를 만들 때 ClusterIP 타입 서비스를 만들었을 때 설명을 하겠습니다. 이것은 Calico의 Pod Network 구조이고, Router 부분에서 Service IP를 Pod IP로 변환해주는 NAT 역할을 하는 기능이 있습니다. Pod D -> Pod B로 트래픽을 날린다고 가정했을 때 먼저 Pod B에 ClusterIP 타입의 Service를 연결합니다. Pod D에서 이 Service IP로 트래픽이 날라가면 NAT에서 해당 Service IP와 매칭되는 Pod IP로 변환이 됩니다. 다음부터 Overlay 네트워크에서 IP가 캡슐화되고, 이전에 배운 Pod 네트워크 단에 통신을 하게 됩니다.

 

 

Calico Plugin(NodePort)

 

: Calico Plugin 사용시 NodePort 타입의 서비스를 연결했을 때 모든 노드에 있는 1) kube-proxy가 자신의 Node에 30000대 NodePort를 열어줍니다. 2) 외부에서 이 Host IP의 포트로 트래픽이 오면 iptables에서 이 트래픽을 Calico Network plugin으로 보내줍니다. 여기서는 ClusterIP와 같이 NAT 기능을 통해 해당 IP로 변환이 되면서 Pod 네트워크 영역으로 넘어가게 됩니다.

Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.