본문 바로가기

개발스터디/MSA 스터디 (22년)

[Spring Cloud] (1) 서비스 디스커버리 쿠버네티스 기능과 비교

Spring Cloud의 기능을 활용하기전에 사용했을때 어떤 이점을 얻을수 있고, 쿠버네티스의 기능을 이용했을때와는 어떤 차이점을 가지는지

 

  인그레스 컨트롤러

 

- AWS NLB 형태의 인그레스 컨트롤러를 배포

- Route 53 의 도메인과 NLB를 매핑 ( *.taskoo.net )

- 인그레스의 Routing Rule을 통해 ( Contour- http proxy 리소스 / istio - gateway / Virtual Service 리소스 ) 

 

  Core DNS

- CoreDNS 사용할 경우

- springboot.svc.cluster.local

  • 파드 도메인: <파드-ip>.<namespace>.pod.cluster.local
  • 서비스 도메인: <서비스이름>.<namespace>.svc.cluster.local

 

## 도메인 정보를 어디 저장하고 있는지 확인 해보겠다.

root@ip-172-31-5-75:~# kubectl run -it --rm 123netdebug -n springboot --image=nicolaka/netshoot --restart=Never -- /bin/sh
If you don't see a command prompt, try pressing enter.


** server can't find svc-clusterip: NXDOMAIN

~ # nslookup service1
Server:		100.64.0.10
Address:	100.64.0.10#53

Name:	service1.springboot.svc.cluster.local
Address: 100.68.205.190

 

Core DNS를 사용하면 서비스이름.namespace.svc.cluster.local의 형태로 해당 네임스페이스에 있는 모든 dns 레코드를 

각 파드에 env로 생성시켜준다.

 

~ # env
KUBERNETES_PORT=tcp://100.64.0.1:443
KUBERNETES_SERVICE_PORT=443
HOSTNAME=netshoot
SHLVL=1
HOME=/root
GATEWAY_PORT_9002_TCP_ADDR=100.69.44.146
GATEWAY_PORT_9002_TCP_PORT=9002
GATEWAY_PORT_9002_TCP_PROTO=tcp
GATEWAY_SERVICE_HOST=100.69.44.146
CONFIG_SERVICE_HOST=100.66.50.25
EUREKA_SERVICE_HOST=100.67.210.228
SPRINGBOOT_PORT_8012_TCP_ADDR=100.65.6.168
SERVICE1_PORT_8015_TCP_ADDR=100.68.205.190
TERM=xterm
EUREKA_PORT_8761_TCP_ADDR=100.67.210.228
SERVICE1_SERVICE_HOST=100.68.205.190
SPRINGBOOT_PORT_8012_TCP_PORT=8012
CONFIG_SERVICE_PORT=8088
GATEWAY_SERVICE_PORT=9002
EUREKA_SERVICE_PORT=8761
SERVICE1_PORT_8015_TCP_PORT=8015
GATEWAY_PORT=tcp://100.69.44.146:9002
EUREKA_PORT=tcp://100.67.210.228:8761
GATEWAY_PORT_9002_TCP=tcp://100.69.44.146:9002
KUBERNETES_PORT_443_TCP_ADDR=100.64.0.1
SPRINGBOOT_PORT_8012_TCP_PROTO=tcp
CONFIG_PORT=tcp://100.66.50.25:8088
CONFIG_PORT_8088_TCP_ADDR=100.66.50.25
SERVICE1_PORT_8015_TCP_PROTO=tcp
SPRINGBOOT_SERVICE_HOST=100.65.6.168

여기서 만약 service1 (서비스)를 delete 하면 어떻게 될까? 

  • pod를 재기동 해야 업데이트가 된다. 물론 이부분은 deployment를 이용해서 Canary 배포 형태의 식으로 처리하면 되겠지만 관리 Point가 커질수록, 불편한것 같다.

대표적으로 쿠버네티스에서 Server-side discovery 를 제공한다. 쿠버네티스는 서비스 라는 개념을 제공하여, 각 서비스 간 네트워크 엔드포인트를 추상화하여 제공한다. (참조 : 쿠버네티스 문서) Pod 혹은 Deployment 에 서비스를 등록하면 해당 오브젝트들에 고유한 IP 주소들과 단일 DNS 명을 부여해준다.

 

정의된 서비스의 이름으로 생성된 DNS 명을 호출하면 서비스가 인스턴스의 개수와 상관 없이 알아서 로드밸런싱을 해주게 된다. 이러한 형태의 Server -side discovery의 단점은 무엇일까? 

 

  • 로드밸런서가 필요하다. 즉, 쿠버네티스 형태의 어플리케이션은 NLB나 AVI를 통해 커버 가능하지만, 다른 형태의 어플리케이션들은 어렵다.
  • Client Side 보다 네트워크 hop이 증가된다. 큰 규모가 아닌 경우 별차이는 없어보인다.

 

그럼 그것을 코드단에서 아래 파이썬 os.environment와 같은 형태로 가져다 사용하는 것이다. 그런데 이렇게 사용하면 매우 불편한게 

한개 서비스의 레코드가 바뀌었을 경우 모든 pod들을 재기동 시켜야 dns 레코드를 변경시킬 수 있다.

따라서 MSA 형태에서 사용 하기 위해서는 별도의 장치가 필요하지 않을까 한다.

 

  • 해당 도메인을 다시 코드 단으로 가져오는것도 하나의 숙제

 

(참고: istio bookexample에서는  파이썬의 라이브러리 os.environ.get 과 기타 파싱 코드로 짜서 지저분 함 )

 

1️⃣ 특징 

JAVA 라이브러리와의 통합성

  • 어노테이션을 통한 간편한 빈등록 
  • 바로 URL, DNS레코드를 코드단으로 가져올수 있음
@Autowired
private DiscoveryClient discoveryClient;
public String serviceUrl() {
    List<ServiceInstance> list = discoveryClient.getInstances("STORES");
    if (list != null && list.size() > 0 ) {
        return list.get(0).getUri();
    }
    return null;
}

 

2️⃣ 특징

RestTemplate 라이브러리 보다 OpenFeign 라이브러리 사용하면 개발생산성 , 코드 리팩토링 효과 증대 

 

  • gradle.build 파일 작성
//중략 

ext {
	set('springCloudVersion', "2021.0.5")
}

dependencies {
	
    implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'

}

//중략


dependencyManagement {
	imports {
		mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
	}
}
  • 인터페이스 작성
package com.example.service1.RestTemplateTest.client;

import com.example.service1.RestTemplateTest.models.Person;
import com.example.service1.RestTemplateTest.models.Response;
import com.example.service1.RestTemplateTest.models.Store;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.data.domain.Pageable;
import org.springframework.web.bind.annotation.*;

import java.net.URI;
import java.util.Currency;

@FeignClient(name = "SERVICE1")
public interface ExampleClient {
    @GetMapping("/form/{id}")
    String getUserById(@PathVariable("id") Long id);
}
  • 컨트롤러 작성
package com.example.service1.RestTemplateTest.controller;

import com.example.service1.RestTemplateTest.client.ExampleClient;
import com.example.service1.RestTemplateTest.models.Person;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class FeignClientController {

        @Autowired
        private ExampleClient userClient;

        @GetMapping("/users/{id}")
        public String getUserById(@PathVariable("id") Long id) {
            System.out.println("hello");
            return userClient.getUserById(id);
        }
    }

 

3️⃣ 특징

쿠버네티스가 아닌 VM 또는 다른 형태의 어플리케이션도 아우를수 있음.

  • VM 에 JAR형태로 구동되는 어플리케이션도 등록된다.
  • 쿠버네티스 서비스 리소스로 구동되는 어플리케이션도 당연히 등록된다.