전자책 출간 알림 [마이크로서비스패턴 쉽게 개발하기]

티스토리 뷰

Zuul 서버 이해

WHY ?

Zuul서버는 API Gateway입니다.

먼저 왜 API Gateway가 필요할까 이해하는게 중요합니다.

API Gateway는 API의 요청자인 Client(웹어플리케이션 또는 모바일앱)와 API의 제공자인 backend service를 연결하는 중계자입니다.

우리가 부동산중계인을 통해 거래를 하듯이, API를 사용하는 어플리케이션과 API를 제공하는 backend service는 API Gateway를 통해 데이터를 유통합니다. 

부동산중계인을 이용하면 거래도 안전하고 상대방과 민감한 얘기를 직접할 필요도 없으니 맘도 편합니다.

API Gateway가 필요한 이유 안전한 API유통과 Client 요청별로 유연하게 대처하기 위해서입니다.

Client요청에 유연하게 대응한다는 의미는 Client 유형(웹브라우저, 모바일앱 등)에 따라 맞는 API를 연결한다거나, Client를 사용하는 사용자의 권한이나 속성에 따라 적절한 결과를 리턴한다는 것입니다.

API Gateway가 필요한 이유를 정리하면 아래와 같습니다.

- 인증/인가: 부적절한 요청을 차단하여 Backend service를 보호

- L/B & Routing: Client의 요청에 따라 적절한 backend service를 로드밸런싱(L/B: Load Balancing)하고 연결(라우팅)

- Logging: 유통되는 트래픽 로깅

- Circuit Break: Backend service 장애 감지 및 대처

모든 frontend의 요청을 라우팅하므로 다음의 usecase에도 활용할 수 있습니다.

- 점진적으로 레거시 시스템을 신규 시스템으로 교체:  Strangler pattern 적용

- 트래픽 일부만 새로운 서비스로 라우팅: Canari(카나리) 배포 가능

 

참고로, 오픈소스 API Gateway에는 kong, Netflix Zuul, Spring cloud gateway, ServiceComb EdgeService등이 있습니다.

 

HOW ?

Netflix zuul은 인증/인가, L/B & Routing, Logging, Circuit Break를 어떻게 제공할까요 ?

zuul은 Java로 개발된 서버이고 커스터마이징할 수 있는 어플리케이션입니다.

class로 개발된 filter가 이용되며, overriding하여 필요한 수행을 추가할 수 있습니다.

보통 들어오는 요청에 대해 'pre' filter에서 인증/인가 처리를 하고,

routing filter에서 L/B, Routing, Circuit break를 처리하며,

Post filter에서 요청과 응답에 대한 Logging을 처리합니다.

L/B는 ribbon이라는 라이브러리가 사용되고, Routing은 zuul core라이브러리가 사용되며, Circuit break는 Hystrix라이브러리가 사용됩니다.

Request Life cycle

그림출처: https://medium.com/netflix-techblog/announcing-zuul-edge-service-in-the-cloud-ab3af5be08ee

 

참고) Zuul이라는 이름은 고스트버스터즈에 나오는 문지기 괴물의 이름에서 따온것 같습니다. (ghostbusters.fandom.com/wiki/Zuul)

 

전체적인 아키텍처는 일반적으로 아래와 같습니다.

상품주문 항목 중 주소란 옆의 [주소찾기]를 클릭한 후, 찾을 주소 keyword를 입력하고 [검색]버튼 클릭 시 '주소검색 마이크로서비스'의 '주소찾기 API'를 호출하는 경우를 예로 들어 설명하겠습니다.

- Web프로그램 또는 모바일앱에서 API를 호출:  /address/search_addr/{주소 keyword}

- zuul은 연결할 backend service의 id를 config파일에서 읽어 라우팅

아래 예와 같이 '/order'로 시작하는 API를 호출하면 'http://order:9001/{api}/*'를 라우팅합니다.

'/address/search_addr' API가 호출되었으므로 service id가 'address'인 마이크로서비스로 연결해야 합니다.

이때 zuul은 자동으로 eureka에서 service id가 'address'인 마이크로서비스를 찾아 그 주소로 라우팅합니다.

zuul:
  host: 
    connect-timeout-millis: 3000
    socket-timeout-millis: 3000
  routes: 
    order: 
      path: /order/** 
      url: http://order:9001  
    address: 
      path: /address/**
      service-id: address
  retryable: true 

 

Microservice Architecture 예시

config server는 git에서 config파일을 읽고, config 변경시 Message broker(예:rabbitMQ, kafka등)에 변경내용을 반영합니다.

그럼 각 마이크로서비스는 변경을 통지받고 config server를 통해 최신 config를 갱신하게 됩니다.

각 마이크로서비스는 ribbon을 이용하여 직접 다른 마이크로서비스를 load balancing하여 연결할 수 있습니다.

이때 Hystrix를 통해 circuit break를 적용할 수도 있습니다.

sleuth는 zipkin server에 traffic정보를 보내고, Zipkin서버는 마이크로서비스간의 tracing을 모니터링할 수 있는 대시보드를 제공합니다.

 

Netflix zuul vs Spring cloud gateway

Spring cloud Netflix zuul은 2018년 12월부터 더 이상의 기능 개선 없이 유지만 하는 Maintenance Mode로 변경되었습니다.

spring.io/blog/2018/12/12/spring-cloud-greenwich-rc1-available-now

이미 Spring boot 2.4.X부터는 zuul, hystrix가 더 이상 제공되지 않습니다.

Spring cloud 커뮤니티에서 zuul 대신 권고하고 있는 API Gateway가 Spring cloud gateway입니다.

아직 zuul을 많이 사용하고 있으므로, zuul서버 개발도 배우실 필요가 있습니다.

Spring cloud gateway는 다음 장에서 설명하도록 하겠습니다.

 

 

Zuul 서버 개발

1. Application 개발 및 git push

1) 프로젝트 생성

 

2) Main class 수정

@EnableZuulProxy, @EnableDiscoveryClient를 추가

@SpringBootApplication
@EnableZuulProxy
@EnableDiscoveryClient
public class ZuulApplication {

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

}

 

3) Spring cloud config 설정

bootstrap.yaml

port번호와 spring.application.name은 적절하게 변경합니다.

server:
  port: ${service_port:8081}
spring: 
  application: 
    name: zuul 
  profiles: 
    active: ${profile:local}
    include: common
  cloud: 
    config: 
      uri: ${config_servers:http://localhost:9001}
      searchPaths: ${spring.application.name}
      default-label: main

bootstrap-local.yaml

request의 path는 테스트로 /config/**과 /webhook/**를 지정합니다.

연결대상 backend service를 지정하는 방법은 url을 직접 지정하는 방법과 eureka에 등록된 service-id를 지정하는 방법이 있습니다.

kubernetes에서 url을 지정할 때는 k8s service이름을 사용하면 됩니다.

zuul:
  host:
    connect-timeout-millis: 3000
    socket-timeout-millis: 3000
  routes:      
    config:
      path: /config/**
      url: http://localhost:9001
      stripPrefix: true
    webhook:
      path: /webhook/**
      service-id: webhook
      stripPrefix: true
  retryable: true

stripPrefix

요청 경로에서 '**'앞의 경로를 제거할지 말지를 지정하는것이며, default값은 true입니다.

true로 지정하면 /webhook/greeting/hello로 요청이 왔을때, backend service에는 /greeting/hello로 proxying되고,

false로 지정하면 /webhook/greeting/hello로 proxying됩니다.

 

4) dependency, finalName 설정

pom.xml에 설정합니다.

spring boot버전은 2.3.x.RELEASE, spring cloud 버전은 Hoxton.SR9여야 합니다.

dependency에 spring-cloud-starter-bus-amqp를 추가합니다.

<?xml version="1.0" encoding="UTF-8"?>
<project ...>
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.3.8.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	...
	<properties>
		<java.version>1.8</java.version>
		<spring-cloud.version>Hoxton.SR9</spring-cloud.version>
	</properties>
	<dependencies>
		...
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-bus-amqp</artifactId>
		</dependency>
		...
	</dependencies>
	...
	<build>
		<finalName>zuul</finalName>
		...
	</build>
</project>

 

5) Application 개발

더 추가할것은 없습니다.

 

6) Local 테스트

local에 config, eureka, webhook앱을 먼저 실행하고, zuul을 실행하십시오.

'/config/**'로 proxying되는지 확인합니다.

'/webhook/**'으로 proxying되는지 확인합니다.

 

7) 환경변수 파일 생성

Pod내에 환경변수를 만들기 위해 사용되는 ConfigMap생성을 위한 변수값을 설정합니다.

run-cicd는 이 변수를 이용하여 ConfigMap을 자동으로 만듭니다.

cicd폴더와 그 아래 cm-common.env파일을 만들고 내용은 아래와 같이 합니다.

uri_config=http://config:9001

위 변수는 configmng의 zuul을 위한 config파일 'zuul-common.yaml'에서 사용됩니다.

 

8) git repository 생성 및 Push

github에 zuul repository를 만들고, push합니다.

 

 

2. CI/CD

 

1) configmng에 config 파일 추가

configmng하위에 기존 다른 디렉토리를 복사-붙여넣기한 후 이름을 아래와 같이 변경합니다.

zuul-cicd-common.properties

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

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

zuul-cicd-dev.properties

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

# Service info
service_target_port=8081
service_port=8081
service_host=zuul.169.56.84.41.nip.io
service_replicas=1

image_pull_policy=Always

zuul-cicd-prod.properties

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

# Service info
service_target_port=8081
service_port=8081
service_host=zuul.169.56.84.41.nip.io
service_replicas=2

image_pull_policy=Always

zuul-common.yaml

zuul:
  host:
    connect-timeout-millis: 3000
    socket-timeout-millis: 3000
  routes:      
    config:
      path: /config/**
      url: ${uri_config:http://localhost:9001}
      stripPrefix: false
    webhook:
      path: /webhook/**
      service-id: webhook
      stripPrefix: false
  retryable: true

zuul-dev.yaml, zuul-prod.yaml은 파일만 생성

zuul-secret-common.properties도 파일만 생성

zuul-secret-dev.properties, zuul-secret-prod.properties

mq_pw=guest

 

2) run ci/cd

NFS서버에서 git clone하여 소스를 다운로드한 후, run-cicd를 실행합니다.

[root@nfs ~]# su - hklee
마지막 로그인: 수  1월 20 02:50:09 CST 2021 일시 pts/0
[hklee@nfs ~]$
[hklee@nfs ~]$ cd work
[hklee@nfs work]$ git clone https://github.com/sc-hklee/zuul.git
Cloning into 'zuul'...
remote: Enumerating objects: 28, done.
remote: Counting objects: 100% (28/28), done.
remote: Compressing objects: 100% (18/18), done.
remote: Total 28 (delta 0), reused 28 (delta 0), pack-reused 0
Unpacking objects: 100% (28/28), done.
[hklee@nfs work]$ cd zuul
[hklee@nfs zuul]$ run-cicd hklee passw0rd . dev . java config

 

3) 배포확인

웹브라우저에서 아래 예와같이 zuul의 ingress host를 이용하여 테스트합니다.

zuul 서버에 대한 더 많은 주제들

zuul은 ribbon을 이용하여 Load balancing을 합니다.

이에 대해서는 ribbon편에서 좀 더 자세히 다루겠습니다.

Circuit break는 hystrix를 이용합니다.역시 hystrix편에서 좀 더 자세히 설명하도록 하겠습니다.

pre/route/post/error filter를 이용하여 인증/인가, 라우팅, 로깅등을 정교하게 할 수 있습니다.

이에 대해서는 깊게 다루지 않겠습니다.

왜냐하면 zuul은 곧 종료될 서비스이기 때문에 새로운 API Gateway인 spring cloud gateway를 학습하는게 맞기 때문입니다.

zuul filter에 대해선 인터넷에 많은 글들이 올라와 있습니다. 

아래 잘 정리된 글들을 링크합니다. 

eblo.tistory.com/69

 

Spring boot - API Gateway, Zuul 예제

1. Overview ZUUL은 넷플릿스에서 사용 하는 JVM 기반의 라우터로 마이크로 서비스에서 라우팅, 모니터링, 에러처리, 보안 등을 담당한다. 출처 : https://medium.com/netflix-techblog/announcing-zuul-edge-se..

eblo.tistory.com

그 중에 DebugFilter를 이용한 아래 글은 실무에 도움이 될듯하여 링크합니다. 

kangwoojin.github.io/programing/spring-cloud-zuul-debug-logging/

 

[Spring] Spring Cloud Zuul debug logging 설정하기

Spring Cloud Zuul에서 debug logging을 설정 해보자

kangwoojin.github.io

또한, 아래 Spring 공식 사이트인 spring.io의 매뉴얼을 참고하십시오.

docs.spring.io/spring-cloud-netflix/docs/2.2.7.BUILD-SNAPSHOT/reference/html/#router-and-filter-zuul

 

Spring Cloud Netflix

Spring Cloud Netflix offers a variety of ways to make HTTP requests. You can use a load balanced RestTemplate, Ribbon, or Feign. No matter how you choose to create your HTTP requests, there is always a chance that a request may fail. When a request fails,

docs.spring.io

 

'Micro Service > mSVC개발' 카테고리의 다른 글

[SC09] Spring Cloud Hystrix 란 ?  (0) 2021.02.14
[SC08] Spring Cloud Ribbon 이란 ?  (0) 2021.02.14
[SC06] Spring Cloud Config Monitor 란 ?  (0) 2021.02.14
[SC05] Spring Cloud Bus 란 ?  (0) 2021.02.14
[SC04] Spring Cloud Eureka 란 ?  (3) 2021.02.14
댓글

전자책 출간 알림 [마이크로서비스패턴 쉽게 개발하기]