위의 사진을 예시로 공유기에서 192.168.0.1로 시작하는 IP로 내부망이 형성된다고 가정했을 때 3대의 서버를 이용해서 Master와 Node로 Kubernetes Cluster가 만들어지고 Cluster안에는 Pod를 위한 IP 대역과 Service를 위한 IP 대역이 있다.
10번과 20번으로 시작하는 IP 대역은 쿠버네티스를 구성할 때 세팅할 수 있다.
이렇게 세팅을 한 상태에서 Pod가 생성이되면 20번대의 IP가 자동으로 할당이 되고 Service가 만들어지면 역시 10번대의 IP가 자동으로 할당된다.
그리고 Service와 Pod들은 연결이 되어있는 상태이다.
이 상태에서 만약 Service가 ClusterIP로 만들어진 Service라면 이 Service의 접근은 이 쿠버네티스를 구성하고 있는 서버에서만 호출이 가능하다.
오로지 이 서버에 접근할 수 있는 권한이 있는 사람만 ClusterIP를 접근할 수 있기 때문에 특정 Pod에 ClusterIP를 달았다면 내부 관리자만 접근할 수 있게 하기 위한 용도로 만들어 진 것이다.
위 사진의 주황색 그림을 보면 이 내부망에 IP를 할당받은 여러 기기들이 있을 수 있겠지만 여기에 직접 Service의 IP를 호출할 수는 없다.
내부망에 있는 다른 Server들이 접근하기 위해서 NodePort Service를 만들면 쿠버네티스를 구성하고 있는 서버들에게 30000번대의 Port가 생성이 되고 이 Port가 해당 Service로 연결이 된다. 그래서 쿠버네티스 관리자는 내부망에 있는 사람들에게 이 서버안에 있는 하나의 IP와 Port를 알려주면 이것을 통해서 내부망에 있는 사람들은 해당 서비스에 연결이 될 수 있다.
구글 Cloud, AWS, Azure같이 클라우드 Provider를 사용해서 쿠버네티스를 구축했을 경우 쿠버네티스에서 LoadBalancer 타입의 Server를 만들었을 때 NodePort가 생성이 되면서 이 Port에 LoadBalancer가 연결이 되고 외부망에 있는 사람들은 이 IP를 통해서 Service에 접근을 할 수 있게 된다.
사용자 입장에서는 쿠버네티스에 있는 망에 궁극적으로는 쿠버네티스에 연결되어있는 Pod에 접근을 하기 위해서 ClusterIP와 NodePort 그리고 LoadBalancer 타입을 만들었던 것이다.
사용자의 접근의 경우 Service가 만들어진 후에 IP를 확인하고 이 IP로 접근을 하면 되는데 Pod의 경우 이 자원들이 동시에 배포될 수 있다. 그리고 동시에 배포되었을 경우 PodA가 PodB에 접근해야 하는 상황인데 PodA의 IP를 넣기에는 IP는 파트너 Service가 생성시에 동적으로 할당되기 때문에 PodA 입장에서는 미리 IP를 알 수 없고 PodB의 겅우 문제가 생겨서 죽으면 자동으로 재생성되면서 IP가 변경되기 때문에 PodA가 IP를 알더라도 계속 쓸 수 없다.
이런 문제를 해결하기 위해 PodA가 PodB에 연결을 할 수 있기 위해서는 DNS와 Headless 서비스가 필요한 것이고 Pod가 외부에 특정 사이트에 접근을 해서 데이터를 가져오는 상황에서 접근 주소를 변경해야 하는 상황이 생기면 경로를 변경해주기 위해서 Pod를 수정하고 재배포를 해야 할까?
다행이 쿠버네티스에서는 ExternalName을 이용해서 외부 연결을 Pod의 수정없이 변경할 수 있도록 도와준다.
위의 사진을 보면 쿠버네티스 Cluster안에는 DNS Server가 별도로 존재한다. 이 DNS Server에는 Service의 도메인 이름과 IP가 저장되어 있기 때문에 Pod가 Service 이름을 지시하면 해당 IP를 알려준다.
그리고 내부망에서도 DNS Server가 구축되어 있다면 내부 서버들이 생겼을 때 해당 서버 이름들이 DNS Server에 등록이 되었을 것이고 Pod가 user1을 찾았을 때 쿠버네티스(Cluster) DNS Server에 없다면 DNS 메커니즘 상 상위 DNS Server(Interanl Network)에서 찾게 되고 해당 이름의 IP를 알려준다.
마찬가지로 외부에 있는 사이트도 도메인이 외부 DNS Server에 등록이 되어 있기 때문에
Google을 찾으면 Google의 IP주소를 알 수 있게 된다.
이렇게 Pod에서 DNS를 이용하여 원하는 Service나 외부에 접근을 할 수 있다.
그래서 Pod가 IP주소를 몰라도 DNS에 Service 이름으로 IP를 물어봐서 연결할 수 있다.
만약 Pod1과 Pod2를 연결해서 사용하고 싶을 때는 Pod에 Headless를 연결하게 되면 DNS Server에 Pod의 이름과 Service의 이름이 붙어져서 도메인 이름으로 등록이 되기 때문에 Pod의 입장에서는 Pod1에 접근하기 위해서 IP 주소는 필요가 없고 pod+service 도메인 이름으로 접근을 할 수 있다.
default 라는 Namespace에 Pod 2개와 Service가 연결되어있다. 이 Service는 ClusterIP로 만든 서비스다.
그리고 쿠버네티스 DNS가 있는데 이 DNS의 이름은 cluser.local이다.
이 DNS는 Pod건 Service건 긴 이름과 IP가 저장이된다.
이름의 구조를 자세히 살펴보자면
Service의 경우 Service의 이름.Namespace.Service의 약어인 svc.DNS 이름이 붙여져서 만들어 지고
Pod의 경우 Pod의 IP.Namespace.pod.DNS의 이름이 붙어서 만들어진다.
이렇게 규칙을 가지고 만들어 지는 이름을 FQDN (Fully Qualified Domain Name)이라 한다.
같은 Namespace안에서는 Service는 앞자리만 짧게 써도 되지만 Pod는 다 입력을 해야한다. 그리고 앞부분이 IP이기 때문에 쓸 수 없다.
Pod의 입장에서 Service의 도메인 이름을 DNS를 통해 IP를 가져오기 때문에 우리는 Service의 이름만 알아도 해당 Service에 접근을 할 수 있고 모든 이름들은 사용자가 직접 만드는 거니까 미리 Service의 이름을 예상해서 이 Pod에 심어놓을 수 있다. 그래서 단순히 Service에만 연결하는 데는 ClusterIP로 Service를 만들어도 큰 문제가 없다.
하지만 똑같은 상황에서 Pod가 다른 Pod가 직접 연결하고 싶다면 우리는 Service를 headless로 만들어야 한다.
만드는 방법은 clusterIP 속성에 None(Service의 IP를 만들지 않는다.)이라고만 넣으면 되고 Pod를 만들 때 hostname이라는 속성에 도메인 이름을 넣어야 하고 서브도메인에 이 Service의 이름을 넣어주면 된다.
이렇게 만들게 되면 DNS에는 Service의 IP가 없기 때문에 Service의 이름을 호출하게 되면 연결되어 있는 모든 Pod들을 준다.
그리고 Pod는 Pod의 hostname이 앞에 붙어 있다.
또한 ExternalName이라는 Service를 만들 수 있고 이 안에 특정 외부 도메인 주소를 넣을 수 있는데 위의 사진 처럼 google.com 이라는 구글 도메인 이름을 넣으면 DNS를 타고 타서 외부 망의 DNS Server에서 Google IP 주소를 가져올 수 있고 Pod는 이 Service를 통해서 데이터를 가져오도록 해놓으면 추후 데이터를 Google -> Github로 변경했을 경우 Pod의 수정없이 ExternalName의 이름만 변경해주면 된다.
우리가 Service와 Pod를 연결할 때 Label을 통해서 연결을 하는데 이것은 사용자 측면에서 이 둘의 연결을 하기 위한 도구일 뿐이지
쿠버네티스는 이렇게 매칭이 되었을 때 EndPoint를 만들어줘서 실제 연결고리를 만들어준다. 쿠버네티스가 어떻게 EndPoint를 만드냐면 Service의 이름과 동일한 이름으로 EndPoint의 이름을 설정하고 EndpOint안에는 Pod의 접속정보를 넣어준다.
이렇게 EndPoint의 구조를 알면 Label을 만들지 않더라도 직접 Service와 Pod를 연결시킬 수 있다.
- Service 와 Pod에 Label을 만들지 않으면 둘은 연결되어 있지 않는 상태다.
- EndPoint를 만드는데 Service의 이름과 Pod의 IP정보를 넣게되면 연결이 된다.
- 내부 뿐만 아니라 외부의 IP주소와 Port를 알면 외부와도 연결을 시킬 수 있다.
- 하지만 IP는 변경 가능성이 있기 때문에 우리는 일반적으로 도메인 이름을 사용한다.
- 그리고 그 도메인 이름을 사용하기 위해서 우리는 ExternalName을 사용한다.
Service에 ExternalName 속성을 달아서 이 안에 도메인 이름을 넣을 수가 있는데 도메인 이름을 넣으면 DNS 캐시가 외부와 내부 DNS를 찾아서 IP를 알아낸다.
그래서 결국 Pod는 Service를 가리키고만 있으면 Service에서 필요시마다 해당 도메인 주소를 변경할 수가 있어서 접속할 곳이 변경되더라도 Pod를 수정하고 재배포하는 일이 없어진다.