maven compile (testCompile 포함) 후에 테스트를 진행했더니

context 불러오는 과정에서 어떤 빈을 찾을 수가 없다는 에러가 뜬다.

그래서 context 불러오는 것을 실패하였다.

 

소스를 보니 빈을 못 찾을 이유가 없었다.

확인해보니 target/test-classes 폴더 안에 test용 resource 파일들이 복사되지 않았다.

spring application context xml 파일이 없으니 어떤 빈도 찾지 못하는 거 였다.

 

testCompile을 하면 생기는 줄 알았는데 생기지 않는다.

package 해보니 test resource 파일들이 target 폴더 안에 생성되었다.

 

흠.. 왜그런거지

 

혹시 아시는 분은 댓글에 적어주시면 고마워요

4장 서비스 디스커버리

학습 내용

  • 유레카 서버를 내장한 애플리케이션 배포하기
  • 클라이언트 측 애플리케이션에서 유레카 서버에 연결하기
  • 고급 디스커버리 클라이언트 설정
  • 클라이언트와 서버 사이의 보안 통신하기
  • 가용성을 높이기 위한 설정 및 동료 간 복제 메커니즘
  • 다른 가용 존에 클라이언트 측 애플리케이션 인스턴스 등록하기

유레카 서버를 내장한 애플리케이션 배포하기

서버 측에서 유레카 서버 실행하기

1. 의존성 추가

dependencies {
	implementation 'org.springframework.cloud:spring-cloud-stater-netflix-eureka-server'
}

2. @EnableEurekaServer 애노테이션으로 유레카 서버 활성화

@EnableEurekaServer  
@SpringBootApplication  
public class DiscoveryApplication {
	public static void main(String[] args) {  
		SpringApplication.run(DiscoveryApplication.class, args);  
	}  
}

3. 유레카 디스커버리 클라이언트 비활성화 (디스커버리 서버에서는 필요하지 않음), 포트 설정 등의 환경설정

server:  
	port: ${PORT:8761}  
eureka:  
	client:  
		register-with-eureka: false # 유레카 클라이언트 비활성화  
		fetch-registry: false

4. java -jar 명령으로 서버 실행. 인스턴스 목록은 /eureka/apps API로 확인.

클라이언트 측에서 유레카 실행하기

  • 유레카 클라이언트는 자신을 유레카 서버에 등록하고 호스트, 포트, 상태 정보 URL, 홈페이지 URL을 전송
  • 유레카 서버는 heartbeat (생존신호) 메시지를 전송
  • 유레커 서버는 heartbeat 메시지를 받지 못하면 레지스트리에 해당 클라이언트를 삭제
  • 클라이언트는 서버로부터 레지스트리 목록을 가져와서 캐싱하고 주기적으로 변경사항 점검

1. 의존성 추가

dependencies {
	...
	implementation 'org.springframework.cloud:spring-cloud-stater-netflix-eureka-server'
	...
}

2. 유레카 클라이언트 활성화

  • @EnableDiscoveryClient : spring-cloud-common에 존재, 다른 클라이언트 구현체(컨설,주키퍼 등)을 지원
  • @EnableEurekaClient : spring-cloud-netflix에 존재, 유레카만 지원
@EnableDiscoveryClient  
@SpringBootApplication  
public class ClientApplication

3. 유레카 클리아언트 환경 설정 (포트, 서비스명, 디스커버리 주소)

spring:  
	application: name: client-service  
server:  
	port: ${PORT:8081}  
eureka:  
	client: service-url: default-zone: ${EUREKA_URL:http://admin:admin123@localhost:8761/eureka/} # 디스커버리 주소

4. 애플리케이션 실행 (리스닝 포인트는 인스턴스 시작 시 재정의 가능)

java -jar -DPORT=8081 client-0.0.1-SNAPSHOT.jar
java -jar -DPORT=8082 client-0.0.1-SNAPSHOT.jar

5. 유레카 서버 대시보드에서 서비스 등록 확인!

클라이언트 서비스 종료 시 등록 해제하기

클라이언트 서비스 종료하기 (actuator/shutdown API 활용)

1. actuator 의존성 추가

dependencies {
	...
	implementation 'org.springframework.boot:spring-boot-starter-actuator'
	...
}

2. 설정하기

사용할 actuator API 설정 후, /shutdown 활성화

management:
	endpoints: 
		web: 
			exposure: 
				include: health, info, metrics, shutdown
	endpoint:
		shutdown:
			enabled: true
  • 하지만 종료를 해도 유레카 서버에는 해당 서비스가 등록되어있음 (만료되지 않음)
  • Self-preservation mode (자기보호모드) : 네트워크 장애가 발생하여 서비스와 통신이 되지 않아도 해당 서비스를 해제되는 것을 방지하는 모드 (운영 환경에서는 활성화, 개발시에는 비활성화)
  • 따라서 Self-preservation mode 비활성화
eureka:  
	client: 
		register-with-eureka: false # 유레카 클라이언트 비활성화  
		fetch-registry: false
	server: 
		enable-self-preservation: false # 자기 보호 모드(네트워크 장애가 발생하여도 서비스 해제를 방지하는 모드) 비활성화

고급 컨피규레이션 설정

  • Server : eureka.server.*, 서버 관련 설정
  • Client : eureka.client.*, 클라이언트가 레지스트리에서 다른 서비스의 정보를 얻을 수 있는 설정
  • Instance : eureka.instance.*, 포트나 이름 등의 현재 유레카 클라이언트의 행동을 재정의하는 설정

레지스트리 갱신하기

  • 비활성화를 하긴 했지만 해제 시간이 오래 걸림
  • 클라이언트는 서버로 30초(기본값)마다 하트비트 전송 (lease-renewal-interval-in-seconds 설정)
  • 서버는 하트비트를 받지 못하면 90초(기본값)을 대기 후 서비스 등록 해제 (lease-expiration-duration-in-seconds 설정)
  • 아래와 같이 설정 가능
eureka:  
	client:  
		service-url:  
			default-zone: ${EUREKA_URL:localhost:8761/eureka/} # 디스커버리 주소  
	instance:  
		lease-renewal-interval-in-seconds: 1 # 디스커버리한테 1초마다 하트비트 전송  
		lease-expiration-duration-in-seconds: 2 # 디스커버리는 서비스 등록 해제 하기 전에 마지막 하트비트에서부터 2초 기다림

클라이언트에서 등록된 서비스 목록 출력하기

  • com.netflix.discovery.EurekaClient : 유레카 서버가 노출하는 모든 HTTP API를 구현
  • org.springframework.cloud.client.discovery.DiscoveryClient : 넷플릭스 EurekaClient를 대체, 모든 디스커버리 클라이언트용 API 구현
  • 예제 소스
@RestController  
public class TestController {  
	private static final Logger LOGGER = LoggerFactory.getLogger(TestController.class);
	
	@Autowired  
	private DiscoveryClient discoveryClient;  
	
	@GetMapping("/ping")  
	public List<ServiceInstance> ping() {  
		List<ServiceInstance> instances = discoveryClient.getInstances("CLIENT-SERVICE");  
		LOGGER.info("INSTANCES: count = {}", instances.size());  
		instances.forEach(it -> LOGGER.info("INSTANCE: id={}, prot={}", it.getServiceId(), it.getPort()));  
		return instances;  
	}  
}

인스턴스 식별자 변경하기

스프링 클라우드 유레카는 다음과 같이 필드를 조합해 식별자를 자동 생성

${spring.cloud.hostname}:${spring.application.name}:${spring.application.instance_id}:${server.port}

IP 주소 우선하기

  기본적으로 인스턴스는 호스트명으로 등록된다. 그러나 DNS가 없는 경우에는 host 파일에 IP를 등록하지 않으면 인스턴스를 찾지 못하게 된다. 이에 대안으로 eureka.instance.prefer-ip-address 속성을 통해서 서비스의 IP 주소를 사용할 수 있다.

  서비스의 네트워크 위치를 결정하기 위한 방법으로 IP 주소를 사용하면 문제가 발생할 수 있다. 장비에서 하나 이상의 네트워크 인터페이스가 있을 경우에 발생한다. 이를 위해 컨피규레이션 파일에 무시할 패턴 또는 선호하는 네트워크 주소를 설정할 수 있다.

eh1로 시작하는 인터페이스 무시

spring:  
  cloud:  
		inetutils: ignored-interfaces: # 해당 인터페이스 무시 
			- eth1*

선호하는 IP 주소 설정(필수가 아닌 선호이다.)

spring:
	cloud:
		inetutils:
			preferred-networks: 
				- 192.168

응답 캐시

  • 유레카 서버는 기본적으로 인스턴스 리스트의 응답을 캐시한다
    • 즉, 새로운 인스턴스 호출 후 /eureka/apps API 호출 시 바로 표시가 되지 않음
  • 응답 캐시 타임 설정하기
eureka:  
	server:
		response-cache-update-interval-ms: 3000 # Response 캐싱 주기
  • 유레카 클라이언트에서도 레지스트리(인스턴스 목록, 서비스 목록)을 캐싱한다
  • 기본적으로 30초마다 비동기로 갱신
  • 설정 속성
    • registry-fetch-interval-seconds : 캐싱 시간 설정
    • disable-delta : 캐싱 시 변경된 부분만 업데이트할 지 여부 설정
eureka:  
	client:
		registry-fetch-interval-seconds: 3 # 서비스 목록 3초마다 캐싱  
		disable-delta: true #캐싱할 때 변경된 부분만 업데이트

클라이언트와 서버 간의 보안 통신 사용하기

지금까지 유레카 서버는 모든 클라이언트의 연결을 인증하지 않았다. 그러므로 기본 인증을 사용하기 위해 스프링 시큐리티 의존성을 추가하여 최소한의 보안을 적용해보자.

dependencies {
	...
	implementation 'org.springframework.boot:spring-boot-stater-security'
	...
}

그리고 http baisc authentication을 적용하기 위해 설정을 한다.

spring:  
	security:
		user:
			name: admin  
			password: admin123

이제 인증을 위해 클라이언트 application.yml에 아래와 같이 설정하자.

eureka:  
	client: 
		service-url: 
			default-zone: http://admin:admin123@localhost:8761/eureka/ # 디스커버리 주소

이 후에 SSL 연결 등 진보된 보안을 위해 '12장 API 보안 강화하기’에서 다룰 것이다.

안전한 서비스 등록하기

클라이언트에 SSL을 적용해보자.

  1. SSL 사설 인증서 생성하기 (예제로 JRE 폴더/bin/keytool을 이용해보자)
keytool -genkey -alias client -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore.p12 -validity 3650
  1. 위의 명령을 입력하고 나면 keystore.p12 파일이

유레카 API

스프링 클라우드 넷플릭스는 유레카 클라이언트가 JAVA, 스프링 프레임워크를 사용하지 않을 경우 (다른 프로그래밍 언어로 개발됐거나 배포 프로세스에서 등록된 서비스 목록 정보가 필요한 경우) 디스커버리 서버를 이용하기 위해 클라이언트 API를 제공한다.

클라이언트 API 목록

HTTP API 설명
POST /eureka/apps/appID 레지스트리에 새로운 서비스 인스턴스를 등록
DELETE /eureka/apps/appID/instanceID 레지스트리에서 서비스 인스턴스를 제거
PUT /eureka/apps/appID/instanceID 서버에 하트비트를 전송
GET /eureka/apps 등록된 모든 서비스 인스턴스의 상세 정보를 조회
GET /eureka/apps/appID 특정 서비스의 모든 인스턴스의 상세 정보를 조회
GET /eureka/apps/appID/instanceID 특정 서비스의 특정 인스턴스의 상세 정보를 조회
GET /eureka/apps/appID/instanceID/metadata?key=value 메타 정보 입력값을 갱신
GET /eureka/instances/instanceID 특정 ID를 사용하는 모든 등록된 인스턴스의 상세 정보를 조회
PUT /eureka/apps/appID/instanceID/status?value=DOWN 인스턴스의 상태를 갱신

복제와 고가용성

  • 디스커버리 서버의 장애에 대비해 적어도 두 개의 디스커버리 서버를 구성한다.
  • 유레카 디스커버리 서버는 다른 디스커버리 서버 간의 peer-to-peer 모델 기반으로 구성되어있다. 이는 모든 서버가 현재 서버 노드에 모든 서버에게 데이터를 복제하고 하트비트를 보낸다는 것을 의미한다.

예제 아키텍처

넷플릭스 주울 기반의 API 게이트웨이 적용하고 API 게이트웨이를 통해 하나의 서비스에 대해 각기 다른 존(zone)에 등록된 애플리케이션 인스턴스 간의 부하 분산 테스트를 할 수 있다.

다음 다이어그램은 예제 시스템의 아키텍처이다.

enter image description here

예제 애플리케이션 개발하기

'MSA' 카테고리의 다른 글

3장 스프링 클라우드 개요  (0) 2019.01.23
2장 마이크로서비스를 위한 스프링  (0) 2019.01.22
1장. 마이크로서비스 소개  (0) 2019.01.22

3장의 목적


스프링 클라우드 프로젝트와 관련된 기본을 인지하고, 마이크로서비스 기반 아기텍처에서 가장 중요한 요소가 무엇인지 알고 스프링 클라우드의 자주 사용되는 하위 프로젝트를 나열할 수 있어야 한다.



스프링 클라우드


12-팩터 앱(The Twelve-Factor App)이란? (참조 : https://12factor.net/ko/)


확장이 가능하고 쉽게 클라우드 플랫폼에 배포할 수 있으며 지속 배포 프로세스에 따라 배포되는 Software as a Service(SaaS)를 개발하는 방법론


스프링 부트와 스프링 클라우드는 12팩터룰에 부합하는 기능과 요소를 제공

  • 분산/버전 컨피규레이션
  • 서비스 등록 및 디스커버리
  • 라우팅
  • 서비스 간의 호출
  • 부하 분산
  • 서킷 브레이커
  • 분산 메시징



기본부터 시작하기


(책의 내용이 이해가 되지 않을 때 참조 : https://yaboong.github.io/spring-cloud/2018/11/25/spring-cloud-config/)


스프링 클라우드는 스프링 부트와 달리 원격 서버에서 컨피규레이션을 가지고 옴 (spring-cloud-starter-config를 사용)


부트스트랩 컨텍스트

  • 스프링 클라우드는 부트스트랩 컨텍스트에 의해 작동
  • 부트스트랩 컨텍스트는 메인 애플리케이션 컨텍스트의 부모
  • 애플리케이션 내에 필요한 최소 설정(이름, 컨피규레이션 서버 주소 등)을 설정해 스프링 클라우드 프로젝트가 외부 소스에서 컨피규레이션을 읽어 올 수 있도록 하는 역할
  • application.xml 대신 bootstrap.xml을 사용

일반적인 애플리케이션 이름과 스프링 클라우드 컨피그(Srping Cloud Config) 설정 (bootstrap.xml)

1
2
3
4
5
6
spring :
    application :
        name : person-service
    cloud :
        config :
            url : http://192.168.99.100:8888
cs


부트스트랩 컨텍스트 설정하기

  • spring.cloud.bootstrap.enabled - 활성화/비활성화 여부
  • spring.cloud.bootstrap.location - 설정 파일 위치 지정
  • bootstrap-{profile}.yml - 프로파일 지정 가능


부트스트랩 컨텍스트를 지원하는 스프링 부트 액추에이터 API

  • env : Environment를 위한 새로운 POST 메서드, 로그 레벨 변경, @ConfigurationProperties 리바인드
  • refresh : 부트스트랩 컨텍스트를 다시 로드하고 @RefreshScope 애노테이션이 있는 빈을 리프레시
  • restart : 스프링의 ApplicationContext를 재시작
  • pause : 스프링의 ApplicationContext를 중지
  • resume : 스프링의 ApplicationContext를 시작


스프링 클라우드 프로젝트는 서비스 디스커버리, 부하 분산, 서킷 브레이커 등의 메커니즘을 위한 공통의 추상 레이어를 제공



넷플릭스(Netflix) OSS(Open Source Software)


마이크로서비스의 선두주자인 회사 넷플릭스가 마이크로서비스 오픈 소스를 공유


스프링 클라우드는 넷플릭스의 유명한 오픈 소스 기능인 유레카(Eureka), 히스트릭스(Hystrix), 리본(Ribbon), 주울(Zuul) 등과 통합



유레카를 이용한 서비스 디스커버리


유레카(Eureka)는 클라이언트와 서버로 구분하여 사용


클라이언트
  • spring-cloud-starter-eureka 스타 사용
  • 클라이언트는 애플리케이션의 일부로 원격의 디스커버리 서버에 연결을 담당
  • 연결 후에 서비스 이름과 네트워크 위치 정보를 등록 메시지로 전송
  • 다른 마이크로서비스 API를 호출해야 할 경우 디스커버리 서버로부터 서비스 목록을 담은 최신 컨피규레이션을 수신

서버
  • spring-cloud-starter-eureka-server 스타터 사용
  • 독립적인 스프링 부트 애플리케이션
  • 각 서버의 상태를 다른 서버에 복제해 가용성이 높음


주울을 사용한 라우팅


주울(Zuul)

  • spring-cloud-starter-zuul 스타터 사용
  • JVM 기반 라우터로, 부하 분산과 필터링을 수행
  • 다양한 곳에 적용 가능 (넷플릭스에서는 인증, 부하 평균 분배, 정적 응답 처리, 부하 테스트에 사용)
  • 유레카와 같이 독립적인 스프링 부트 애플리케이션
  • API 게이트웨이 역할을 수행
  • 각 서비스의 네트워크 위치 정보를 알아야 하므로 유레카(디스커버리) 서버와 통신하기 위해 디스커버리 클라이언트를 클래스 경로에 포함


리본을 사용한 부하 분산


리본(Ribbon)

  • spring-cloud-starter-ribbon 스타터 사용
  • 클라이언트(서비스) 측 부하 분산기
  • TCP, UDP, HTTP 등 유명한 프로토콜 지원
  • 동기, 비동기, 리액티브 모델 지원
  • HTTP와 TCP 클라이언트를 한 단계 더 추상화
  • 라운드 로빈, 가용성 필터링, 응답 시간에 가중치를 두어 부하 분산 등의 규칙을 즉시 제공, 쉽게 확장 가능
  • 이름 기반 부하 분산(네임드 클라이언트)에 기반


자바 HTTP 클라이언트 작성하기


페인(Feign)

  • spring-cloud-stater-feign 스타터 사용
  • 선언적인 REST 웹 서비스 클라이언트
  • 애노테이션 선언만으로 애플리케이션이 실행될 때 실제 구현이 실행됨
  • 리본 클라이언트와 통합돼 디스커버리 서비스와의 통신, 부하 분산 같은 리본의 기능을 기본으로 제공


히스트릭스를 사용해 대기 시간 및 장애 내성 다루기


히스트릭스(Hystrix)

  • spring-cloud-starter-hystrix 스타터 사용
  • 서킷 브레이커 패턴을 구현
  • 기본적으로 리본과 페인 클라이언트를 통합 가능
  • 서킷 브레이커 시간 만료(timeout) 발생 시 폴백 로직을 쉽게 설정 가능


아카이우스를 사용한 컨피규레이션 관리


아카이우스(Archius)

  • 변경 전의 원본을 가져오거나 변경사항을 클라이언트에 전달하는 방법으로 컨피규레이션을 갱신



디스커버리와 분산 컨피규레이션


서비스 디스커버리와 분산 컨피규레이션 관리는 유연한 키-값 저장소 형태로 기술적으로 매우 비슷


서버는 단 하나의 저장소로, 애플리케이션을 위한 모든 외부 속성을 관리


컨피규레이션은 여러 버전과 프로파일로 동시에 유지하며, 깃을 사용


설정 파일은 파일 시스템, 서버 클래스 경로에 있을 수 있으며 또는 백엔드를 볼트(Valut)로 사용 가능

  • 볼트는 토큰이나 패스워드, 자격 증명을 관리할 수 있는 오픈 소스 도구
  • 볼트를 통해 컨피규레이션 서버의 접근 레벨을 안전하게 관리할 수 있음


컨피그 서버

  • 독립형 스프링 부트 애플리케이션
  • HTTP와 리소스 기반 API를 통해 속성에 쉽게 접근
  • 기본 인증으로 보호되며, 개인 키/공개 인증을 사용한 SSL 연결도 설정 가능함
  • 서버 스타터 : spring-cloud-config-server, 클라이언트 스타터 : spring-cloud-config-starter
  • 컨피규레이션 서버를 속성 저장소로 사용하는 모든 마이크로서비스 클라이언트는 클라이언트가 시작되고 스프링 빈이 생성되기 전에 컨피규레이션 서버에 접속함


또 다른 대안 - 컨설


컨설(Consul)

  • spring-cloud-starter-consul-discovery 사용
  • 넷플릭스 디스커버리와 스프링 분산 컨피규레이션의 대안
  • 컨설 서버에 연결하기 위해서는 애플리케이션에 에이전트가 필요 (별도로 분리된 프로세스로 실행)
  • 기본적으로 http://localhost:8500 주소 제공
  • 서비스 등록, 서비스 목록 수집, 속성의 컨피규레이션을 수행할 수 있는 REST API 제공
  • 넷플릭스 리본 동적 라우터, 넥플릭스 주울 필터 지원
  • 컨설 아키텍처 참조 (https://www.consul.io/docs/internals/architecture.html)


아파치 주키퍼


주키퍼(Zookeeper)

  • spring-cloud-stater-zookeeper-discovery, sping-cloud-starter-zookeeper-config, 아파치 큐레이터(Curator) 사용
    (큐레이터는 통합을 지원하는 API 프레임워크와 유틸리티를 제공)
  • 컨피규레이션과 이름을 유지하는 중앙 서비스로의 분산 동기화, 그룹 서비스가 가능
  • 예시 : 공통 애노테이션을 통한 통합제공, 설정 파일을 통한 컨피규레이션, 리본/주울과 상호작용하기 위한 자동-컨피규레이션


기타 프로젝트


프링 클라우드 쿠버네티스(Kubernetes)

  • 배포, 확장, 애플리케이션 컨테이너의 관리를 자동화하는 시스템
  • 컨테이너 오케스트레이션(orchestration) 및 서비스 디스커버리, 컨피규레이션 관리, 부하 분산 등의 기능을 제공


스프링 클라우드 에티시디(Etcd)

  • 분산 컨피규레이션, 서비스 등록, 디스커버리 기능 제공
  • 쿠버네티스에서 서비스 디스커버리, 클러스터 탄생, 컨피규레이션 관리에 쓰이는 백엔드로 사용



솔루션을 사용한 분산 추적


스프링 클라우드 슬루스(Sleuth)

  • spring-cloud-stater-sleuth 스타터 사용
  • 분산 추적 기능 (하나의 요청을 여러 마이크로서비스로 처리할 때 이어지는 요청을 연관 짓는 기능)
  • HTTP 헤더에 기반하여 추적을 구현
  • Slf4j, MDC로 개발됨
  • Slf4j는 특정 로깅 프레임워크의 추상화 퍼사드를 함
    (퍼사드 패턴 : 단순화된 인터페이스를 통해서 서브 시스템을 더 쉽게 사용할 수 있도록 하는 패턴)
  • MDC(mapped diagnostic context)는 다양한 소스의 로그 출력을 구분하고 부가 정보를 추가하는 솔루션
  • 트레이스(trage) ID와 스팬(span) ID를 Slf4j, MDC에 추가해 관련된 로그를 추출 가능


스프링 클라우드 슬루스는 지연 분석을 위한 기간 정보도 기록이 가능하며 집킨 도구에 제공할 수 있음


집킨(Zipkin)

  • spring-cloud-stater-zipkin 사용
  • 지연 문제를 분석하기 위해 설계된 분산 추적 시스템
  • 시간 정보를 질의하고 시각화함


모든 데이터를 분석할 필요는 없다

  • 스프링 클라우드 슬루스는 샘플링 정책을 지원 (집킨으로 보낼 트래픽 양을 결정)
  • 기본 HTTP API 대신 메시지 브로커에 통계 정보를 전송
    spring-cloud-sleuth-stream 추가하면 기능 활성화 (아파치 카프카나 래빗엠큐로 메시지를 전송하는 프로듀서가 됨)



메시징과 통합


스프링 클라우드 버스

  • spring-cloud-stater-bus-amqp 또는 spring-cloud-stater-bus-kafka
  • 컨피규레이션 속성 변경, 관리 명령 등의 상태 변경을 브로드캐스트로 애플리케이션에 알림
  • 공통 오퍼레이션을 위한 분산 메시지 기능 지원


스프링 클라우드 스트림

  • spring-cloud-stater-stream-kafka 또는 spring-cloud-stater-stream-rabbit
  • 메시지 중심 마이크로서비스로 구성된 시스템에 적합
  • 스프링 인테그레이션에 기반함
    (스프링 인테그레이션은 
    채널,애그리게이터,트랜스포머와 같은 엔터프라이즈 통합 패턴 프로그래밍 모델을 제공하는 프로젝트)
  • 마이크로서비스 시스템 내의 애플리케이션 스프링 클라우드 스트림 입력 및 출력 채널을 통해 통신



클라우드 플랫폼 지원


피보탈 클라우드 파운드리(PCF, Pivotal Cloud Foundry)

  • spring-cloud-services-stater-circuit-breaker, spring-cloud-services-stater-config-client, spring-cloud-services-stater-service-registry
  • 애플리케이션을 배포하고 관리하는 클라우드 네이티브 플랫폼
  • 스프링 부트의 실행 가능한 JAR, 컨피그 서버, 서비스 레지스트리, 서킷 브레이커 등 모든 스프링 클라우드 마이크로서비스 패턴을 지원


스프링 클라우드는 AWS를 지원하기 위해 유명한 웹 도구와 통합

  • Simple Queuing Service(SQS)
  • Simple Notification Service(SNS)
  • ElasticCache
  • Relation Database Service(RDS)
    (Aurora, MySQL, Oracle과 같은 엔진을 제공함)


주요 모듈

  • Spring Cloud AWS Core : spring-cloud-stater-aws 스타터로 활성화. EC2 인스턴스로 직접 접근을 활성화하는 핵심 구소 요소 제공
  • Spring Cloud AWS Context : S3 저장소, 이메일 서비스, 캐싱 서비스로의 접근 제공
  • Spring Cloud AWS JDBC : spring-cloud-stater-aws-jdbc 스타터로 활성화. 스프링에서 지원하는 데이터 접근 기술을 사용할 수 있는 데이터 소스 조회 및 컨피규레이션 제공
  • Spring Cloud AWS Messaging : spring-cloudstater-aws-messaging 스타터로 활성화. 애플리케이션이  SQS(점대점 메시징) 또는 SNS(게시/구독 메시징)로 메시지를 보내고 받을 수 있게 함


스프링 클라우드 커넥터 프로젝트

  • 클라우드 플랫폼에 배포된 JVM 애플리케이션를 위한 추상화를 제공
  • SMTP, 래빗엠큐, 레디스(Redis), 전통 DB에 접속할 수 있는 클라우드 플랫폼
  • 히로쿠(Heroku)와 클라우드 파운드리를 지원



다른 유용한 라이브러리


보안

스프링 클라우드 시큐리티

  • spring-cloud-stater-security
  • OAuth2, JWT, 기본 인증 메키너즘 API 구현
  • 싱글 사인온(single sign-on)과 토큰 리플레이(token replay) 패턴을 지원


테스트 자동화


스프링 클라우드 컨트랙트

  • 와이어목(WireMock)을 사용하여 트래픽을 기록
  • 메이븐 플러그인을 사용하여 스텁(stub) 생성


스프링 클라우드 태스크

  • spring-cloud-stater-task
  • 한 번만 실행하고 종료하는 마이크로서비스를 개발하도록 지원
  • 보통 로컬 컴퓨터나 클라우드 환경에서 실행


클러스터 기능


스프링 클라우드 클러스터

  • 주키퍼, 레디스, 해즐캐스트(Hazelcast), 컨설에 대한 추상화 및 구현을 사용해 리더 선출과 공통 상태유지 패턴을 위한 솔루션을 제공



프로젝트 개요


분산 컨피규레이션

  • Spring Cloud Config
  • Spring Cloud Zookeeper Config
  • Spring Cloud Consul Config
  • Spring Cloud Etcd Config
  • Spring Cloud Kubernetes Config

서비스 디스커버리

  • Spring Cloud Eureka
  • Spring Cloud Zookeeper Discovery
  • Spring Cloud Consul Discovery
  • Spring Cloud Etcd Discovery
  • Spring Cloud Kubernetes Discovery

상호 통신

  • Spring Cloud Hystrix
  • Spring Cloud Ribbon
  • Spring Cloud Feign
  • Spring Cloud Zuul

추적

  • Spring Cloud Sleuth
  • Spring Cloud Sleuth Zipkin
  • Spring Cloud Sleuth Stream

클라우드 플랫폼 지원

  • Spring Cloud Cloud Foundry
  • Spring Cloud AWS
  • Spring Cloud Function
  • Spring Cloud Connectors

메시징 및 통합

  • Spring Cloud Stream
  • Spring Cloud Bus
  • Spring Cloud Stream Apps
  • Spring Cloud Data Flow

기타

  • Spring Cloud Contract
  • Spring Cloud Security
  • Spring Cloud Task
  • Spring Cloud Cluster



릴리즈 트레인(release trains)


스프링 클라우드 내에 수많은 프로젝트가 있어 의존성 관리가 문제가 되어 모든 프로젝트 버전 간의 관계를 알아야 하는 문제가 발생

이를 위해 릴리즈 트레인 도입


릴리즈를 버전이 아닌 BOM에 기반한 이름으로 구분


BOM(Bill of materials)는 아티팩트 버전을 독립적으로 관리하는 표준 메이븐 개념


최신 릴리즈 목록 : https://project.spring.io/spring-cloud/


릴리즈 트레인 뒤에 붙은 표시

  • M[X] : M은 마일스톤, X는 버전 번호
  • SR[X] : 서비스 릴리즈로서 중요한 버그를 수정한 버전

메이븐 또는 그래들로 의존성을 포함할 시 올바른 릴리즈 트레인 이름을 사용해야 함



요약


목적 : 스프링 클라우드 프로젝트와 관련된 기본을 인지하고, 마이크로서비스 기반 아기텍처에서 가장 중요한 요소가 무엇인지 알고 스프링 클라우드의 자주 사용되는 하위 프로젝트를 나열할 수 있어야 한다.


















'MSA' 카테고리의 다른 글

4장 서비스 디스커버리  (0) 2019.02.01
2장 마이크로서비스를 위한 스프링  (0) 2019.01.22
1장. 마이크로서비스 소개  (0) 2019.01.22

1장에서는 마이크로서비스의 개요 및 아키텍처, 클라우드 개념 등에 대해서 알아보았습니다.

그럼 2장에서는 마이크로서비스를 스프링으로 구현하기 위한 스프링 부트에 대한 사전지식을 알아볼시다~



스프링 부트 소개


우선 마이크로서비스에서 표준 스프링 컨피규레이션이 아닌 스프링 부트를 사용하면 좋은 점과 스프링 부트의 장점을 알아보겠습니다.


스프링 부트는 표준 스프링 컨피규레이션과 다르게 독립 실행형 어플리케이션을 java -jar 명령어를 통해 실행합니다.

스프링 부트는 표준 스프링 컨피규레이션과 달리 웹 컨테이너를 애플리케이션에 포함할 수 있습니다.


마이크로서비스에서 표준 스프링 컨피규레이션보다 스프링 부트가 더 적합한 이유는 여기에 있습니다.

마이크로서비스는 다른 마이크로서비스와의 독립성이 중요합니다. 그러므로 데이터베이스와 웹 컨테이너와 같은 공통의 자원을 공유하지 않아야 합니다.


그러나 표준 스프링 컨피규레이션은 애플리케이션에 웹 컨테이너를 포함하는 대신 애플리케이션을 war 형태로 웹 컨테이너에 배포하므로,

하나의 웹 컨테이너에 여러 war 파일을 배포할 수 있기 때문에 마이크로서비스와는 적합하지 않습니다. 


스프링 부트의 가장 큰 장점은 표준 스프링에 비해 간단하다는 것입니다.


스프링 부트가 간단할 수 있는 이유는 크게 두 가지인데,

그 두 가지는 스타터와 자동 컨피규레이션(auto-configuration) 입니다.


스타터는 프로젝트 의존성에 포함될 수 있는 아티팩트입니다. (여기서 아티팩트는 빌드로 생성되는 결과물을 이야기합니다.)

스타터는 필요한 의존성을 패키지로 제공해주므로 별도의 설정이 필요 없이 즉시 사용할 수 있어 매우 빠르고 간단합니다.

스타터의 공식적인 명명 패턴은 spring-boot-start-* 입니다. 


자동 컨피규레이션은 스타터에 포함된 아티팩트의 기본 설정을 다른 속성이나 다른 유형의 스타터로 쉽게 재정의할 수 있게 해줍니다.

(추가된 의존성의 아티팩트 기반으로 스프링 애플리케이션을 자동적으로 설정해주며 다른 설정으로 변경할 수도 있다는 것을 말합니다.)




스프링 부트를 이용한 애플리케이션 개발


이제 스프링 부트를 이용하여 간단한 애플리케이션을 개발해보겠습니다.


스프링 부트와  메이븐(Maven)과 그래들(Gradle)과 같은 의존성 관리시스템을 활용하면 어떠한 의존성을 포함하는지 간단히 확인할 수 있어 매우 효율적입니다.

의존성 관리 도구를 통해 톰캣을 기본 컨테이너로 내장한 spring-boot-start-web (RESTful, Spring MVC 포함하는 기본 스타터)을 추가합니다.


다음은 메이븐 pom.xml 예제입니다.

1
2
3
4
5
6
7
8
9
10
11
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.7 RELEASE</version>
</parent>
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>
cs


또는 spring-boot-starter-parent POM에서 상속받지 않고 dependencyManagement 태그를 사용할 수도 있습니다. 

[dependencyManagement 부연설명 : https://examples.javacodegeeks.com/enterprise-java/maven/use-maven-dependency-management-2/]

1
2
3
4
5
6
7
8
9
10
11
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>1.5.7.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
cs


그래들 입니다.

1
2
3
4
5
6
7
8
plugins {
    id 'org.springframework.boot' 
    version '1.5.7.RELEASE'
}
 
dependencies {
    compile("org.springframework.boot:spring-boot-stater-web:1.5.7.RELEASE")
}
cs


이제 Main 애플리케이션 클래스를 생성하고 @SpringBootApplication 애노테이션을 추가합니다. 

@SpringBootApplication은 @Configuration, @EnableAutoConfiguration, @ComponentScan의 세 애노테이션과 같습니다.

1
2
3
4
5
6
7
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
 
cs


이제 실행 가능한 애플리케이션이 완성되었습니다.


그리고 모든 의존성을 포함하는 실행 가능한 JAR (팻JAR 또는 JARs)를 생성하기 위해서는 

메이븐의 spring-boot-maven-plugin이 필요합니다. pom.xml에 추가해줍니다.

1
2
3
4
5
6
7
8
<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>srping-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>
cs


이제 기본 스프링 라이브러리를(spring-core, spring-aop, spring-context, spring boot, 내장형 톰캣, 로깅 라이브러리 등) 포함한 팻JAR를 생성할 수 있습니다.




컨피규레이션 파일 사용자 정의하기


스프링 부트는 편리하고 쉽게 설정을 관리할 수 있는 메커니즘을 제공합니다.

가장 간단한 방법은 애플리케이션 JAR에 추가된 컨피규레이션 파일을 이용하는 것입니다.

스프링 부트는 application으로 시작하는 컨피규레이션 파일을 자동으로 찾으며, 지원하는 파일 타입은 .properties와 .yml입니다.

(설정 파일명은 일반적으로 application 또는 application-{profile}이며 이외의 특정한 이름을 부여하려면 환경변수를 통해 등록 가능합니다.)

또한 컨피규레이션 파일을 외부로 뽑아낼 수 있는데, 이 때 파일의 위치는 다음과 같아야 합니다.


  • 애플리케이션의 현재 디렉토리의 /config 하위 디렉터리
  • 애플리케이션의 현재 디렉토리
  • 클래스 경로 /config 패키지
  • 클래스 경로 root

일반적으로 application 컨피규레이션 파일은 src/main/resources 디렉토리에 위치하며, 메이븐 빌드 후에는 JAR의 루트에 위치합니다.


컨피규레이션 파일에서 두 종류의 설정을 지정할 수 있습니다. 첫 번째는 주로 spring-boot-autoconfiguration 라이브러리에서 사용되는 공통설정과 미리 정의된 스프링 부트 설정입니다. 두 번째는 사용자 정의 속성을 지정할 수 있으며 @Value 또는 @ConfigurationProperties 애노테이션을 통해 주입될 수 있습니다.


기본 서버 포트, 애플리케이션 이름, 로깅 속성 등을 재정의하는 예제 .yml 컨피규레이션 파일을 작성해봅시다.

1
2
3
4
5
6
7
8
9
10
11
12
server :
    port : ${port:2222}
spring :
    application :
        name : first-service
logging :
    pattern :
        console : "%d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n"
        file : "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"
    level : 
        org.springframework.web : DEBUG
    file : app.log
cs


외부 컨피규레이션 파일을 지정할 필요 없이 간단하게 설정을 할 수 있습니다. 로깅 컨피규레이션을 위해 log4j.xml 또는 logback.xml 등을 설정할 필요가 없다는 말입니다. 기본 로그 레벨을 DEBUG로 변경하고 로그 패턴을 수정하고 app.log 파일을 생성했으며, 기본 HTTP 포트는 2222라는 설정을 간단하게 하였습니다.


사용자 컨피규레이션도 같은 파일에 위치해야 한다. 사용자 정의 속성을 포함하는 예제를 작성해봅시다.

1
2
3
4
5
name : first-service
my :
    service :
        -dev.bar.com
        -foo.bar.com
cs


위와 같이 설정 후 @Value, @ConfigurationProperties 애노테이션을 활용해 주입할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Component
public class CustomBean {
    @Value("${name}")
    private String name;
    // ...
}
 
@ConfigurationProperties(prefix="my")
public class Config {
    private List<String> servers = new ArrayList<>();
    public List<String getServers() {
        return this.servers;
    }
}
cs


지금까지 간단한 애플리케이션을 설정하고 생성했습니다. 이와 다른 방법으로 웹에서 생성하는 방법도 존재합니다. 

스프링 이니셜라이즈 웹사이트(https://start.spring.io)를 통해서도 프로젝트를 생성할 수 있습니다.




RESTful 웹서비스 생성하기


이제 RESTful 웹서비스를 만들어봅시다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@RestController
@RequestMapping("/person")
public class PersonController {
    @GetMapping
    public List<Person> findAll() {
        return getPersons();
    }
    @GetMapping("/id") Person findById(@RequestParam("id") Long id) {
        return getPerson(id);
    }
    @PostMapping Person add(@RequestBody Person p) {
        p.setId(getPersons().size() + 1);
        addPerson(p);
        return p;
    }
    @DeleteMapping("/{id}")
    public void delete(@RequestParam("id") Long id) {
        removePerson(id);
    }
    @PutMapping("/{id}")
    public void update(@RequestBody Person p) {
        updatePerson(p);
    }
}
cs


@RestController는 HTTP 요청을 처리하는 컨트롤러 빈 클래스에 설정하면 RESTful 웹서비스를 지원합니다.

@RequestMapping은 컨트롤러 메소드와 HTTP URL 주소를 대응해줍니다.

@GetMapping, @PostMapping, @DeleteMapping, @PutMapping은 특정 HTTP 메서드를 지정해 주며 @RequestMapping(method=RequestMethod.GET)과 동일합니다.

@RequsetParam은 요청의 경로와 입력값을 객체로 바인딩한다.

@RequestBody는 입력 JSON을 잭슨 라이브러리를 사용해 객체로 바인딩한다.


자 이제 우리는 몇 개의 REST API를 제공하는 첫 마이크로서비스를 만들었습니다.

그렇다면 다른 클라이언트가 이 API를 호출하기 위해서는 API 문서가 필요합니다.

여기서 스웨거(Swagger)가 필요합니다. 스웨거는 API문서를 제공하고 자동으로 생성해주는 도구입니다.




API 문서화


스웨거는 RESTful API를 설계하고 빌드하고 문서화하는데 가장 많이 사용되는 도구입니다.

스웨거를 통해 API를 설계하고 소스코드를 생성할 수 있고, 소스코드로 API 문서인 스웨거 파일을 생성할 수 있습니다.




스웨거2를 스프링 부트와 같이 사용하기


스프링 부트와 스웨거2는 스프링폭스(Springfox) 프로젝트에 구현되어 있습니다.

스웨거를 사용하기 위해 의존성을 추가하고 Main 애플리케이션에 @EnableSwagger2 애노테이션과 Docket 빈을 추가합니다.

1
2
3
4
5
6
7
8
9
10
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>2.7.0</version>
</dependency>
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>2.7.0</version>
</dependency>
cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Bean
public Docket api() throws IOException, XMLPullParserConfiguration {
    MavneXpp3Reader reader = new MavenXpp3Reader();
    Model model = reader.read(new FileReader("pom.xml"));
    ApiInfoBuilder builder = new ApiInfoBuilder()
            .title("Person Service Api Documentation")
            .description("Documentation automatically generated")
            .version(model.getVersion())
            .contact(new Contact("Piotr Minkowski""piotrminkowski.wordpress.com""hyyg123@gmail.com"));
    return new Docket(DocumentationType.SWAGGER_2).select()
            .apis(RequestHandlerSelectors.basePacakge("pl.pimin.services.boot.controller"))
            .paths(PathSelectors.any()).build()
            .apiInfo(builder.build());
}
cs


애플리케이션이 시작될 때 스웨거에 의해 API 문서가 자동으로 생성됩니다. 

API 문서 대시보드는 /swagger-ui.html 에서 확인할 수 있습니다.


이러한 것들은 Main에 등록된 Docket 빈에 의해 처리되며,

APi의 타이틀, 저자, 설명 같은 속성을 설정할 수 있습니다. 또한 패키지 별로 설정이 가능합니다. (예제에서는 pl.pimin.services.boot.controller)




스웨거 UI를 통한 API 테스트


또한 스웨거 UI를 통해서 포스트맨(API 테스트 도구)과 유사하게 API 테스트를 할 수 있습니다. 자세한 내용은 생략하겠습니다.




스프링 부트 액추에이터의 기능


마이크로서비스에서는 애플리케이션의 모니터링과 메트릭 수집이 중요합니다. 

이를 위해 스프링 부트에서는 스프링 부트 액추에이터를 지원합니다.

스프링 부트 액추에이터는 모니터링과 상호작요을 하기 위한 내장된 수 많은 API를 제공합니다.

스프링 부트 액추에이터를 사용하기 위해서는 spring-boot=starter-actuator 의존성을 추가해야 합니다.

다음은 몇 개의 API 목록입니다.

  • /beans - 모든 스프링 빈의 목록을 표시
  • /health - 상태 정보 표시
  • /info - 애플리케이션 임의 정보 표시 (예를 들어 build-info.properties나 git.properties 파일 정보 표시)
  • /loggers - 로거 정보 표시
  • /metrics - 메모리 사용량, 실행 중인 스레드 수, RST 메서드의 응답 시간 등의 메트릭 정보를 표시

비활성화 방법은 application.yml에 다음과 같이 추가합니다.

1
2
3
management :
    security :
        enabled : false
cs



애플리케이션 정보


/info API를 통해 호출합니다. 기본적으로 어떤 정보도 노출하지 않으므로 InfoContributer 빈을 변경하거나 자신만의 빈을 작성해야 합니다.


EnvironmentInfoContributor는 환경 정보를 노출합니다.

GitInfoContributor는 git.properties를 통해 브랜치 이름이나 커밋ID와 같은 커밋 정보를 노출합니다. 

(git-commit-id-plugin을 pom.xml에 추가해야 합니다.)

BuildInfoGontributor는 META-INF/build-info.properties 정보를 통해 API를 노출합니다. 

(spring-bood-maven-plugin을 수정해 build-info.properties를 자동 생성하도록 해야 합니다.)




상태 정보


/health API를 통해 호출합니다. 

디스크 사용량, 메일 서비스, JMS, 데이터 소스, 몽고디비 또는 카산드라와 같은 NoSQL 등을 모니터링 할 수 있습니다.




매트릭스


/metrics API를 통해 호출합니다.

로딩된 클래스의 개수, 활성화된 스레드의 수, API 메서드의 평균 실행 시간 등을 표시합니다.

CountService와 GaugeService를 통해 자신만의 메트릭을 생성할 수 있습니다.


CountService는 값의 증가와 감소, 초기화를 위한 메서드를 제공합니다.

GaugeService는 단순히 현재의 값을 전달하는 메서드를 제공합니다.


모든 메트릭은 분석하거나 표시할 수 있는 저장소로 전달이 가능합니다.

레디스(Redis), Open TSDB, Statsd, 인플럭스디비 등에 저장하여 활용할 수 있습니다.


이러한 문서화, 메트릭, 상태 점검은 마이크로서비스를 개발하고 유지보수하는데 매우 중요한 역할을 합니다.



개발자 도구


spring-boot-devtools 의존성을 추가하면, 소스 파일 변경사항을 감지하여 자동으로 반영해줍니다. (마치 jrebel과 비슷하게 작동합니다.)


'MSA' 카테고리의 다른 글

4장 서비스 디스커버리  (0) 2019.02.01
3장 스프링 클라우드 개요  (0) 2019.01.23
1장. 마이크로서비스 소개  (0) 2019.01.22

'마스터링 스프링 클라우드' 책을 통해 배운 것들을 설명하겠습니다. 

총 15장의 단원이 있는데 일반적으로 장당 글 하나씩 쓰도록 할 것입니다.


그럼, 1장을 다같이 공부해볼까요~?

'MSA' 카테고리의 다른 글

4장 서비스 디스커버리  (0) 2019.02.01
3장 스프링 클라우드 개요  (0) 2019.01.23
2장 마이크로서비스를 위한 스프링  (0) 2019.01.22

+ Recent posts