본문 바로가기

DevOps/Kubernetes

[번역] 쿠버네티스 초심자를 위한 네트워킹 가이드

[원본글]https://matthewpalmer.net/kubernetes-app-developer/articles/kubernetes-networking-guide-beginners.html

 

Kubernetes Networking Guide for Beginners - Kubernetes Book

Kubernetes Networking Guide for Beginners One of the hardest things to learn about Kubernetes as a software developer is how the networking works. I had never done anything with networks prior to learning Kubernetes – I just wanted to deploy my code! I s

matthewpalmer.net

번역본을 읽으며, 이해를 위해 반드시 원문에 설명된 gif도 참고해보시기를 추천드립니다.

 

쿠버네티스 초심자를 위한 네트워킹 가이드

 

소프트웨어 개발자로서 쿠버네티스를 배우는데 어려운 점 중 한 가지는 어떻게 네트워킹이 이루어지는 지 입니다.

 

저는 쿠버네티스를 배우기 전, 네트워크에 대해서는 어떠한 것도 해본 적이 없습니다. (단순히 코드배포를 해보았을뿐!)

 

따라서 이번 글을 통해 네트워킹 전문가가 아닌 사람들을 위한 쿠버네티스 네트워킹 가이드를 만들어 보았습니다.

 

아래는 쿠버네티스 네트워킹에서 이해해야하는 5가지 입니다.

  • 동일 Pod에서 컨테이너들의 커뮤니케이션
  • 동일 Node에서 Pod들 간의 커뮤니케이션
  • 다른 Node들의 Pod들 간의 커뮤니케이션
  • Pod와 Service들 간의 커뮤니케이션
  • DNS는 어떻게 동작하는가? 어떻게 IP주소를 발견할 수 있는가?

 

 

 

동일 Pod에서 컨테이너들의 커뮤니케이션

 

먼저 같은 Pod내에 두 컨테이너가 돌아가고 있다면, 이 둘 사이는 어떻게 서로 대화를 주고 받을 수 있을까?

 

같은 Pod에 있는 컨테이너들은 같은 네트워크 namespace에 존재하기 때문에,

로컬 PC에서 여러 서버를 띄워두고 localhost와 포트번호로 서비스를 접근하는 것과 동일하게 접근이 가능하다. 

 

그렇다면 Network namespace가 무엇인가?

 

네트워크 인터페이스들 그리고 라우팅 테이블들의 집합이라고 할 수 있다.

(네트워크 인터페이스 : 네트워크의 두 장비간의 연결, 라우팅 테이블 : 네트워크 패킷을 어디로 보낼지에 대한 안내 테이블)

 

네임스페이스는 충돌과 간섭없이 같은 가상머신 상에서 여러 네임스페이스를 가질 수 있기 때문에 매우 유용하다.

(같은 네트워크에서 복수개의 서비스가 같은 포트를 사용하면 충돌이 발생하게 된다! 당신은 이를 원하지 않을 것이다!)

 

쿠버네티스의 모든 Pod에는 비밀의 컨테이너를 실행하고 있는데, 이 컨테이너의 가장 첫번째 역할이 특정 Pod의 다른 컨테이너가 죽어도 namespace는 지속적으로 유지되도록 열어놓는 것이다.

그래서 각 pod는 각자의 네트워크 네임스페이스를 가지게되고, 같은 Pod에 있는 컨테이너는 같은 네트워크 네임스페이스에 있게 된다. 이것이 같은 Pod에 여러 컨테이너가 존재할 경우, localhost로 컨테이너들 사이에 통신이 가능한 이유이고 포트 충돌이 일어나지 않도록 주의해야하는 이유이다.

 

 

 

동일 Node에서 Pod들 간의 커뮤니케이션

 

한 노드 안에서 각 Pod은 각자의 네트워크 네임스페이스를 가지고 있고, 각자의 IP 주소를 가지고 있다.

각 Pod는 네트워크 요청을 보내기위해 eth0라고 하는 일반적인 이더넷 디바이스를 가지고 있다고 생각할 수 있지만, 이것은 맞지않다!

이는 단순히 쿠버네티스가 제공하는 가상의 이더넷 연결일 뿐이다.

 

각 Pod의 eth0 디바이스는 실제적으로 노드의 가상 이더넷 디바이스에 연결이 되어있다.

 

가상 이더넷 디바이스는 Pod의 네트워크와 노드를 연결하는 터널인데, 이는 다음의 두가지 측면이 있다.

  1. Pod 부분 : eth0
  2. Node 부분 : vethx

 

Node쪽은 왜 끝에 x 가 붙을까? 노드의 모든 pod은 vethx의 연결이 존재하기 때문에 다음과 같이 veth1, veth2, veth3 …으로 명명하기 위함이다.

 

Pod가 다른 노드의 주소로 요청을 보낼때, 자신의 eth0 인터페이스로 요청을 보내고, 이는 노드의 각 가상 vethX인터페이스로 전달된다.

 

그렇다면, 어떻게 요청이 다른 pod에 도착할 수 있을까?

 

노드는 네트워크 브릿지를 사용한다.

 

네트워크 브릿지란 무엇일까? 

 

네트워크 브릿지는 두 네트워크를 함께 연결하는 역할을 한다. 만약 한 요청이 브릿지에 도달하게 되면, 브릿지는 모든 연결된 디바이스(pod)에 최초의 요청을 핸들링할 올바른 IP 주소를 가진 곳이 있는지 질의하게 된다. (각 pod는 자신만읜 IP를 가지고 있고 자신의 IP를 알고 있음을 기억하라)

 

만약 해당하는 디바이스가 있다면, 그 브릿지는 이 정보를 저장하고 네트워크 요청을 마무리하기 위해 데이터를 다시 전달해준다.

 

쿠버네티스에서 이 브릿지는 cbr0라고 칭한다. 한 노드의 모든 pod은 브릿지의 일부분이다. 그리고 브릿지는 동일 노드에서 모두 연결하고있다.

 

 

다른 Node들의 Pod들 간의 커뮤니케이션

 

그렇다면 다른 노드에 있는 Pod사이의 통신은 어떻게 처리될까?

 

만약 네트워크 브릿지가 모든 연결된 디바이스(Pod)에 request 목적지의 IP와 일치하는 곳이 있는지 물어보았을 때, 일치하는 디바이스가 없을 경우를 생각해보자.

(참고. 이 부분은 클라우드 제공 업체나 네트워킹 플러그인에 따라 다를 수 있음.)

 

이 경우 브릿지는 기본 게이트웨이로 fallback 하여 클러스터 레벨로 올라가 IP 주소를 찾을 것이다.

 

클러스터 레벨에서는 다양한 노드들의 IP 주소 범위를 매핑해놓은 테이블이 있다. 그 노드들의 Pod는 그 범위에서 IP 주소가 할당되었을 것이다.

 

예를들어 Node1의 Pod의 IP가 100.96.1.1, 100.96.1.2 으로 주었다면, Node2의 Pod의 IP는 100.96.2.1, 100.96.2.2 이렇게 주어졌을 것이다.

따라서 클러스터 레벨의 매핑테이블에는 100.96.1.X는 Node1로 가야하고, 100.96.2.X는 Node2로 가야한다고 저장될 것이다.

 

이 프로세스를 보면 같은 노드의 Pod들 사이에서의 통신과 유사한 과정을 거친다는 것을 알 수 있다.

 

 

 

Pod와 Service들 간의 커뮤니케이션

 

마지막 남은 커뮤니케이션 패턴은 쿠버네티스에서 중요한 패턴이다.

 

쿠버네티스에서 서비스는 Pod들의 집합에 한 가지 IP가 매핑되도록 해준다. 

만약 한 가지 endpoint(도메인 네임/IP)로 요청을 보내면 해당하는 서비스는 그 요청을 pod에 proxy해주게 된다.

 

이것은 kube-proxy라는 작은 프로세스에 의해서 일어나는데, 

여기서 프로세스는 가상 IP주소를 실제 Pod IP주소의 그룹에 매핑시킨다.(모든 노드 각각에 kube-proxy가 존재한다!)

 

일단 kube-proxy가 서비스의 가상 IP와 Pod의 실제 IP를 매핑하게 되면, 요청은 위에 언급된 방식대로 진행이 된다. 

 

 

DNS는 어떻게 동작하는가? 어떻게 IP주소를 발견할 수 있는가?

 

DNS는 도메인 네임을 IP 주소로 변경해주는 시스템이며, 쿠버네티스 클러스터에는 DNS 관련 서비스가 존재한다.

 

모든 클러스터의 서비스는 my-service.my-namespace.svc.cluster.local과 같은 도메인 네임이 지정되어있는데,

Pods는 자동으로 DNS 네임이 주어지고 또한 YAML파일의 hostname과 subdomain 속성값을 통해 자신만의 이름을 지정할 수도 있다.

 

그래서 도메인 네임으로 서비스에 요청이 생성되면, DNS 서비스는 그 서비스의 IP 주소를 찾아준다,.

 

그러면 kube-proxy는 그 서비스의 IP주소를 pod의 IP 주소로 바꿔주게 된다.

이후, Pod가 같은 노드 혹은 다른 노드에 있는지에 기반하여, 그 요청은 위에 설명된 방식대로 처리가 된다.