Micro Service/mSVC개발

[SC04] Spring Cloud Eureka 란 ?

Happy@Cloud 2021. 2. 14. 03:14

Eureka server 이해

WHY ?

한 마이크로서비스가 다른 마이크로서비스를 호출하려면, 대상 마이크로서비스의 IP 또는 FQDN(FQDN-Fully Qualified Domain Name)과 포트를 알아야 합니다.

대상 마이크로서비스는 증감될 수 있고, IP/FQDN과 포트가 변경될 수도 있습니다.

특히, kubernetes환경에서는 이러한 현상이 비일비재합니다. Pod 하나 하나가 마이크로서비스(정확히는 마이크로서비스 인스턴스)라고 할 수 있는데, Pod는 언제라도 증감되거나 재시작될 수 있고, 그때마다 IP는 변경됩니다.

각 마이크로서비스가 모든 마이크로서비스의 IP/FQDN과 PORT정보를 갖고 있는것은 매우 비효율적입니다.

따라서, 각 마이크로서비스의 IP/FQDN과 PORT정보를 저장하고 제공하는 Service Discovery가 필요합니다.

k8s service리소스와 eureka

kubernetes service리소스에 대해 이해하신분들은 k8s service가 service discovery역할을 하는데, 굳이 eureka가 필요할까? 라는 생각을 할 수 있습니다.

결론부터 말씀드리면 도메인의 모든 어플리케이션이 kubernetes위에서 서비스된다면 eureka는 필요없습니다.

예를 들어, 여러분이 온라인음식주문 도메인을 위해 여러 마이크로서비스를 개발했고, 모두 kubernetes에 배포하여 서비스한다면 eureka는 필요없습니다.

그러나 현실에서는 kubernetes와 다른 runtime환경이 함께 이용되는 경우가 많으므로, 모두를 지원할 수 있는 service discovery가 필요합니다. 그것이 eureka가 필요한 이유입니다.

kubernetes에 Pod가 배포되면 eureka에 Pod별로 등록이 됩니다. 예를 들어 'Replicas'를 3으로 배포했다면 eureka는 3개가 등록이 됩니다. 각 Pod를 접근하는 주소는 {Pod}.{Service}.{namespace}.svc.cluster.local:{service}:{port}로 등록이 됩니다.

실제 사용되는 주소는 {Pod}.{Service}.{namespace}.svc.cluster.local:{port}입니다.

주의할것은 반드시 Statefulset으로 배포해야 이 주소로 Pod를 직접 접근할 수 있다는것입니다.

 

 

HOW ?

1) Microservice와 Eureka server간 통신

각 마이크로서비스는 구동 시 Service Discovery(Eureka Server)에 자신의 IP/FQDN과 PORT를 등록합니다.

Eureka Server는 주기적으로 각 마이크로서비스의 실행여부를 체크합니다. 정지된 경우, registry에서 삭제합니다.

각 마이크로서비스는 등록된 모든 마이크로서비스의 정보를 주기적으로 갖고 와서 캐싱할 수 있습니다.

한 마이크로서비스가 다른 마이크로서비스를 연결할때는 캐싱된 registry정보를 이용하거나 Eureka server를 조회하여 대상 마이크로서비스의 IP/FQDN과 PORT를 구해 연결합니다.

그림출처: https://medium.com/@ijayakantha/microservices-service-registration-and-discovery-with-netflix-eureka-9a2aa729da96

 

참고) Eureka는 그리스어에서 유래했고 무언가의 답의 찾았을때 기쁨을 나타내는 말입니다.

 

2) Eureka 서버간 registry 동기화

이중화를 위해 Eureka Server를 여러대 띄우고, 각 Eureka Instance(peer라고 함)간에 registry 정보를 복제합니다.

Eureka peer간에 서로의 정보를 알아야 하므로 Eureka server에 Eureka peer 등록도 필요합니다.

이를 위해서는 아래 예제와 같이 eureka설정을 하면 됩니다.

peer1은 service_port를 8761로 하고, peer는 peer1으로 환경변수를 셋팅하고 실행합니다.

peer2는 service_port를 8762로 하고, peer는 peer2로 환경변수를 셋팅하고 실행합니다.

register-with-eureka를 true로 하여 spring.application.name인 'eureka'로 erureka server에 등록합니다. 

fetch-registry를 true로 하여 eureka peer간에 서로 registry정보를 복제하도록 합니다.

server:
  port: ${service_port:8761}
spring:
  application:
    name: eureka
 
eureka:
  instance: 
    hostname: ${peer}.eureka.com
  client: 
    register-with-eureka: true
    fetch-registry: true
    service-url: 
      defaultZone: http://peer1.eureka.com:8761/eureka,http://peer2.eureka.com:8762/eureka

그림출처: https://hu.pinterest.com/pin/398920479474860500/

 

Eureka server 개발

그럼 Eureka server를 개발해 보겠습니다.

실습 순서는 아래와 같습니다.

1. eureka 어플리케이션 개발 및 git repo에 푸시

2. CI/CD


1. eureka 어플리케이션 개발 및 git repo에 푸시

1) 프로젝트 생성

2) Main class 수정

Annotation @EnableEurekaServer을 추가합니다.

@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {

	public static void main(String[] args) {
		SpringApplication.run(EurekaApplication.class, args);
	}

}

 

3) Spring Cloud 옵션 설정

application.yaml

spring.profiles는 application에서 사용할 profile의 종류를 지정하는 것입니다. 

여기서 profile은 보통 구동될 서버의 환경을 의미하며, 일반적으로 local, dev, prod, common을 사용합니다.  

server:
  port: ${service_port:8761}

spring:
  application:
    name: eureka
  profiles:
    active: ${profile:local}

아래와 같이 각 profile별 설정 파일을 만들고, spring.profiles.active에서 사용하는 환경변수 'profile'을 배포 대상 환경에 맞게 변경하면 됩니다.

예를 들어 profile변수값이 'prod'라면 application-prod.yaml파일이 적용됩니다.

 

application-local.yaml

eureka:
  instance: 
    hostname: ${peer:peer1}.127.0.0.1.nip.io
  client: 
    register-with-eureka: true
    fetch-registry: true
    service-url: 
      defaultZone: http://peer1.127.0.0.1.nip.io:8761/eureka,http://peer2.127.0.0.1.nip.io:8762/eureka

 

application-dev.yaml, application-prod.yaml

${HOSTNAME}은 현재 instance의 hostname을 리턴하는 기본 환경변수입니다.

Pod로 배포되면 각 Pod의 이름(예: eureka-0, eureka-1)이 리턴됩니다. 따라서, 현재 Eureka peer Pod의 값이 hostname에 셋팅되게 됩니다.

eureka:
  instance: 
    hostname: ${HOSTNAME}.eureka
  client: 
    register-with-eureka: true
    fetch-registry: true
    service-url: 
      defaultZone: ${eureka_servers:http://localhost:8761/eureka}

 

4) pom.xml 셋팅

- Spring Boot 버전이 2.3.7.RELEASE이고, Spring Cloud 버전이 Hoxton.SR9인지 체크합니다.

- finalName을 지정합니다.

	<build>
		<finalName>eureka</finalName>
...
	</build>

 

5) Application 개발

추가 개발할 것은 없습니다.

6) Local 테스트

local에서 2대의 eureka peer를 띄우도록 하겠습니다.

- Runtime configuration 생성

아래와 같이 Run configuration창을 열어 config이름과 환경변수 service_port, peer를 지정합니다.

오른쪽 하단의 [Apply]를 클릭하여 저장합니다.

'eureka1'을 복사하여 'eureka2'를 만들고, 환경변수 service_port, peer를 지정합니다.

오른쪽 하단의 [Apply]를 눌러 저장하고, [Close]를 눌러 창을 닫습니다.

 

- Eureka peer 실행

아래와 같이 'eureka'하위에 2개의 Runtime configuration이 보일겁니다.

'eureka'를 선택하고, 상단 좌측의 'Run'아이콘을 클릭하여, 2개의 Eureka peer를 실행합니다.

 

- 정상실행 확인

웹브라우저에서 peer1.127.0.0.1.nip.io:8761를 오픈합니다.

아래와 같이 DS Replicas에 peer2.127.0.0.1.nip.io가 나오고, 'EUREKA'로 Application이 등록되어 있으면 성공입니다.

웹브라우저에서 peer2.127.0.0.1.nip.io:8762를 오픈합니다.

아래와 같이 DS Replicas에 peer1.127.0.0.1.nip.io가 나오고, 'EUREKA'로 Application이 등록되어 있으면 성공입니다.

 

 

7) CI/CD, 환경변수, Secret 파일 생성

project root에 cicd디렉토리를 만들고, 아래 파일들을 작성합니다.

cicd-common.properties

image_project, namespace, serviceaccount는 본인걸로 수정하십시오.

# Container Image info
image_registry=harbor.io
image_project=sc-hklee
image_repository=eureka
image_tag=0.0.1

# resources
req_cpu=128m
req_mem=128Mi
limit_cpu=1024m
limit_mem=1024Mi

# namespace, sa
namespace=hklee
serviceaccount=sa-hklee

# Service info
service_target_port=8761
service_port=8761
service_host=eureka.169.56.84.41.nip.io
service_replicas=3

image_pull_policy=Always

cm-common.env

k8s에 배포될 eureka peer의 Pod의 url을 'eureka_servers'변수에 지정합니다.

pod접근 주소는 http://<pod명>.<Service명>.svc.cluster.local:<PORT>입니다. 동일 namespace의 Pod를 접근할 때는 'svc.cluster.local'은 생략할 수 있습니다.

Statefulset으로 배포되므로 pod명은 eureka-0, eureka-1, eureka-2와 같이 일련번호가 붙습니다.

eureka_servers=http://eureka-0.eureka:8761/eureka,http://eureka-1.eureka:8761/eureka/,http://eureka-2.eureka:8761/eureka/

 

secret-common.env

비밀번호와 같은 보안이 필요한 변수는 없으므로, 파일만 만듭니다.

 

8) git repository생성 및 Push

github.com의 sc-hklee 하위에 eureka라는 이름으로 git repository를 생성합니다.

<HOME>/Documents/springboot/sc-hklee/eureka로 이동하여 수행합니다.

echo "# eureka" > README.md
git init
git checkout -b main
git remote add origin https://happycloudpak@github.com/sc-hklee/eureka.git
git add . && git commit -m "first commit" && git push -u origin main

TIP) Spring Tool Suite, Elipse에서 git commit & push 하기

 

2. CI/CD

이제 jar와 container image를 만들고 kubernetes상에 배포합니다.

1) hklee user로 로그인하여 eureka repository를 clone

[root@nfs ~]# su - hklee
마지막 로그인: 화  1월 12 08:44:00 CST 2021 일시 pts/0
[hklee@nfs ~]$ cd work
[hklee@nfs work]$ ll
total 8
drwxrwxr-x. 7 hklee hklee 4096 Jan 12 09:07 config
-rw-rw-r--. 1 hklee hklee 1122 Jan 12 02:47 deploy-mq.yaml
[hklee@nfs work]$ git clone https://github.com/sc-hklee/eureka.git
Cloning into 'eureka'...
remote: Enumerating objects: 32, done.
remote: Counting objects: 100% (32/32), done.
remote: Compressing objects: 100% (23/23), done.
remote: Total 32 (delta 1), reused 32 (delta 1), pack-reused 0
Unpacking objects: 100% (32/32), done.

 

2) Run CI/CD

[hklee@nfs work]$ cd eureka/

[hklee@nfs work]$ kubens hklee 

[hklee@nfs eureka]$ run-cicd hklee passw0rd . dev . java
************** RUN ALL ***************

...

************** END ALL ***************

 

3) 정상배포 확인

pod가 3개 실행될때까지 기다립니다.

$ watch k get po

모두 실행되면 CTRL-C를 눌러 console로 돌아옵니다.

 

eureka ingress주소를 구합니다.

[hklee@nfs eureka]$ k get ing
Warning: extensions/v1beta1 Ingress is deprecated in v1.14+, unavailable in v1.22+; use networking.k8s.io/v1 Ingress
NAME     CLASS    HOSTS                        ADDRESS        PORTS   AGE
config   <none>   config.169.56.84.41.nip.io   169.56.84.41   80      21h
eureka   <none>   eureka.169.56.84.41.nip.io   169.56.84.41   80      2m46s

 

웹브라우저에서 eureka ingress주소를 오픈합니다.

refresh할때마다 Load balancing된 eureka peer가 바뀌므로, DS Replicas도 변하여야 합니다.

아래 예제에서는 eureka-2로 분기되었기 때문에 다른 peer인 eureka-0과 eureka-1이 나온것입니다.

3개의 EUREKA client가 등록되어 있어야 합니다.

 

TIP) eureka server를 이용하여 마이크로서비스 정보 Query하기

eureka는 service discovery로서 마이크로서비스의 IP/FQDN과 포트를 Query할 수 있습니다.

아래와 같이 REST API를 이용하면 됩니다. 결과는 XML형식으로 리턴됩니다.

1) 모든 마이크로서비스 정보 얻기

http://<eureka host>/eureka/apps

예) http://eureka.169.56.84.41.nip.io/eureka/apps/

2) 특정 마이크로서비스의 instance정보 얻기

http://<eureka host>/eureka/apps/<application name>

예) http://eureka.169.56.84.41.nip.io/eureka/apps/eureka 

 

3) 특정 마이크로서비스의 특정 instance정보 얻기

http://<eureka host>/eureka/apps/<application name>/<instance id>

예) http://eureka.169.56.84.41.nip.io/eureka/apps/eureka/eureka-0.eureka.hklee.svc.cluster.local:eureka:8761