서비스 디스커버리란?
서비스 디스커버리는 IP주소를 할당하거나 앤드포인트를 구성하지 않아도 서비스가 동적으로 서로 검색할 수 있게하는 것입니다. 최근에 개발되고 있는 애플리케이션은 확장성 문제로 Microservice Architecture를 도입했을텐데 서비스끼리 통신할 때 어떻게하면 효율적으로 할 수 있을까 고민하여 나온 것이 서비스 디스커버리입니다. 마이크로서비스가 종료되거나 IP가 바뀌어도 자동으로 이를 검색할 수 있는 메커니즘이 필요해진 것이죠.
클라이언트 사이드 서비스 디스커버리
클라이언트 사이드 디스크버리는 마치 우리가 배달앱에 들어가서 음식점에 원하는 음식을 직접 주문하는 방식으로 생각하면 쉽습니다.
작동방식은 서비스 레지스트리에서 서비스 목록을 조회하고, 선택한 서비스에 직접 요청합니다.
예를 들면 유명한 라이브러리중에 Netflix Eureka를 예시로 들 수 있습니다.
DiscoveryClient discoveryClient = new DiscoveryClient(applicationName, eurekaClientConfig);
List<ServiceInstance> instances = discoveryClient.getInstances(serviceName);
// Select a service instance based on load balancing algorithm
ServiceInstance selectedInstance = loadBalancer.choose(instances);
// Send request to the selected service instance
String targetUrl = selectedInstance.getUri().toString();
HttpResponse response = httpClient.sendRequest(targetUrl, request);
위 자바코드의 예시처럼 직접 원하는 서비스를 목록에서 조회하고, 찾아서 서비스를 요청합니다.
서버 사이드 디스커버리
서버 사이드 디스커버리는 마치 고객센터에 우리가 전화를 걸면 민원을 들어주기 위해서 상담원이 전화를 연결받습니다.
우리가 상담원 누구에게 전화해달라고 요청하지 않고 대표번호로 전화만해도 나에게 서비스를 제공해줄 상담원에게 연결이 됩니다. 이것이 서버 사이드 디스커버리입니다. 예를들면 Nginx, AWS ELB가 있습니다.
쿠버네티스 서비스 디스커버리
쿠버네티스에서 구현되는 서비스 디스커버리는 대표적인 서버 사이드 방식으로 구현됩니다. 각 pod의 ip를 몰라도 DNS로 접속할 수 있습니다. 서비스명.네임스페이스명.svc.cluster.local 이렇게 요청만해도 해당 서비스의 pod로 트래픽을 라우팅 해줍니다. 파드가 늘어나거나 줄어들어도 혹은 다시 만들어져서 ip가 변경되어도 트래픽을 라우팅 해줍니다.
쿠버네티스 디스커버리 원리
이는 서버 사이드 디스커버리 형태로 구현되었습니다. kube-proxy에게 요청만 하면 알아서 어떤 pod는 응답하게 되는거죠.
위 아키텍처의 구성요소 먼저 설명드리겠습니다.
Service
서비스 리소스는 pod들의 접점을 제공하며 가상 IP(Cluster IP)를 자동으로 할당합니다. 기본적으로 서비스 타입을 지정하지 않으면 ClusterIP로 지정됩니다.
ClusterIP
# 서비스 예시
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: my-app
ports:
- protocol: TCP
port: 80
targetPort: 8080
NodePort
노드포트 타입으로 지정하면 노드 IP:NodePort를 통해서 외부의 트래픽을 받을 수 있게 됩니다. 이를 통해서 내부 서버의 curl 뿐 아니라 외부 클라이언트에서도 curl 응답을 받을 수 있게 됩니다. 물론 pod가 http 응답을 제공할 때 가능합니다.
Load Balancer
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
type: LoadBalancer
selector:
app: my-app
ports:
- protocol: TCP
targetPort: 8080
로드 벨런서 타입은 클라우드 로드 벨런서와 통합할 수도 있으며 NodePort와 똑같이 외부에서 접속 가능하도록 트래픽을 연결해줍니다. NodePort와 차이점은 pod에 트래픽을 분산해준다는 점이 다릅니다.
ExternalName
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
type: ExternalName
externalName: user-service.usernamespace.svc.cluster.local
ExternalName 타입은 외부의 서비스를 내부에서 DNS 이름으로 사용할 수 있도록 만들어줍니다.
예를들어서 default 네임스페이스에서 user-service 네임스페이스의 서비스에 접근하고 싶다면 위처럼 설정해주어서 default 네임스페이스에 등록된 Ingress로도 유저 서비스에 트래픽을 전달할 수 있습니다.
네임스페이스는 논리적으로 네트워크가 격리되어 있기 때문에 같은 로컬이어도 외부로 취급하게 됩니다. 내부에 존재하는 다른 네임스페이스 서비스가 아닌 다른 외부 서비스도 똑같이 사용 가능합니다.
Endpoints
서비스에 대한 설명이 길었는데 위의 아키텍처의 구성요소중인 EndPoint에 대해 설명드리겠습니다.
Endpoint는 Service가 선택한 Pod들의 IP주소 목록입니다. 이들은 Service Selector에 의해 자동 관리 되며 pod의 상태에 따라서 동적으로 업데이트 됩니다. 서비스 레벨에서 pod들을 리스트로 관리하고 있다고 생각하면 좋습니다.
"frontend-service" → ["10.244.0.5:80", "10.244.0.6:80"]
CoreDNS
쿠버네티스 클러스터 내에서 DNS 서버 역할을 담당합니다. 서비스 이름을 IP로 변환해주고 DNS 레코드를 관리합니다.
EndPoints는 서비스에 해당하는 ip:port 목록을 가지고 있다면 CoreDNS는 클라이언트가 DNS를 요청했을 때 바로 ClusterIP를 반환합니다.
Controller
클러스터의 상태를 지속적으로 모니터링하고, Service, Endpoints의 리소스를 생성하고 관리하는 역할을 합니다. 만약에 DNS 레코드가 변경되거나 Proxy 규칙이 업데이트가 되면 Controller는 해당 변경사항을 클러스터에 반영합니다.
내부에서의 통신
1. Pod A가 my-service를 호출하려 함
2. CoreDNS에 my-service 조회
3. CoreDNS가 ClusterIP 반환
4. kube-proxy가 ClusterIP를 실제 Pod IP로 변환
5. 요청이 적절한 Pod로 전달
내부에서는 위와 같이 통신되며 마이크로서비스에서 자주 활용됩니다.
최종 정리
서비스 디스커버리는 동적으로 생성,삭제 되는 pod들을 자동으로 찾고 통신할 수 있게 해줍니다. 이는 서버 사이드 방식으로 구현되어 있으며 이 덕분에 마이크로서비스 환경에서도 파드가 죽었다 살아나도 바로 파드를 찾아 통신할 수 있습니다.
CoreDNS가 쿠버네티스의 DNS 서버 역할을 하며 서비스명.네임스페이스명.svc.cluster.local 형식의 DNS로 요청할 수 있습니다.
DNS 해석단계
1. 클라이언트 Pod가 "my-service.default.svc.cluster.local" 호출
2. CoreDNS에 DNS 쿼리
3. CoreDNS가 서비스 이름을 ClusterIP(예: 10.96.45.10)로 응답
라우팅 단계
4. 클라이언트가 ClusterIP(10.96.45.10)로 요청
5. kube-proxy가 생성한 iptables 규칙에 따라 처리
6. iptables가 Endpoints 목록에서 Pod IP 선택
7. 선택된 Pod IP(예: 10.244.0.5)로 요청 전달
*참고자료*
https://microservices.io/patterns/server-side-discovery.html
https://www.youtube.com/watch?v=tq9ng_Nz9j8
https://kubernetes.io/docs/concepts/services-networking/service/index.html
'Kubernetes' 카테고리의 다른 글
[Kubernetes] Kubernetes(K8s)에서 Pod가 생성될 때 내부동작 (0) | 2025.02.02 |
---|---|
[Kubernetes] 온프레미스 환경에 Kubeadm으로 쿠버네티스(K8s) 설치하기 (0) | 2025.01.30 |
[Kubernetes] 쿠버네티스 CNI 플러그인에서 Overlay를 사용하는 이유 (0) | 2025.01.28 |
[Kubernetes] Replica Controller / ReplicaSet 차이점과 잘 사용하는 법 (1) | 2025.01.19 |
[Kubernetes] 쿠버네티스 트래픽 전달방식 아키텍처 (0) | 2025.01.17 |