마이크로 서비스 아키텍처: 구성 서버

xellos·2022년 6월 18일
0

MSA

목록 보기
3/5

Intro: 스프링 클라우드 컨피그 서버로 구성 관리

많은 개발자들이 애플리케이션 코드에서 구성 정보를 분리한다. 이것으로 컴파일을 거치지 않고 구성은 쉽게 변경할 수 있게 되었지만 애플리케이션과 함께 관리되고 배포되어야 하는 산출물이 추가되어 복잡해진다.

많은 개발자가 구성 정보를 저장하기 위해 저수준의 프로퍼티 파일(YAML, XML)로 전환한다. 대게 이런 파일은 DB 및 미들웨어의 접속 정보와 앱의 행동 양식을 저장하는 메타데이터가 존재하는 서버에 둔다.

이러한 방식은 애플리케이션이 적은 상황에서는 적용될 수 있지만 수백 개의 마이크로서비스의 수많은 인스턴스가 실행되는 클라우드 기반의 애플리케이션 상황에서는 관리가 힘들다.

따라서 클라우드 기반의 마이크로서비스 개발에서는 다음 사항이 강조된다.

  • 배포되는 실제 코드에서 앱의 구성을 완전히 분리
  • 서버 및 앱을 빌드하고 배포 환경에 따라 절대 바뀌지 않는 불변 이미지를 빌드한다.
  • 서버를 시작할 때 환경 변수나 앱의 마이크로서비스가 읽어올 수 있는 중앙 저장소를 이용해 앱의 구성 정보를 주입한다.

1. 구성(그리고 복잡성) 관리

1) 애플리케이션 구성 관리를 위한 네가지 원칙

분리 (Segregation)

실제 물리적인 서비스의 배포와 서비스 구성 정보를 완전히 분리하자. 시작하는 서비스에 환경변수로 전달하거나 중앙 저장소에서 읽어와 구성 정보를 전달한다.

추상화 (Abstract)

서비스 인터페이스 뒷단에 있는 구성 데이터의 접근 방식을 추상화한다. 애플리케이션이 REST 기반의 JSON 서비스를 사용해 구성 데이터를 조회하게 만들자.

중앙 집중화 (Centralize)

클라우드 기반의 앱은 수백 개의 서비스가 존재할 수 있으므로 구성 정보를 보관하는 저장소의 개수를 최소로 줄이는 것이 매우 중요하다.

견고성 (Harden)

앱 구성 정보를 배포된 서비스와 완전히 분리하고 중앙 집중화하므로 어떤 솔루션을 사용하더라도 고가용성과 다중성을 구현할 수 있어야 한다. 핵심적으로 기억할 사항은 구성 정보를 실제 코드 외부로 분리하면 코드 외부로 관리하고 버전 제어를 해야할 외부 의존성이 생긴다는 것이다.


2) 구성 관리 아키텍처

구성 관리는 MSA의 부트스트래핑(2단계) 에서 일어난다. 이를 자세히 보면 아래의 과정과 같다.

2. 스프링 클라우드 컨피그 서버 구축

스프링 클라우드 컨피그 서버는 스프링 부트로 만든 REST 기반의 애플리케이션이다. 독립형 서버로 제공되지 않아 기존 스프링 부트 애플리케이션에 내장하거나 새로운 스프링 부트 프로젝트를 만들어 내장하는 방법으로 시작할 수 있다.

1) 스프링 클라우드 컨피그 서버를 위한 gradle 추가

스프링 부트 버전과 클라우드 버전이 다르면 호환이 되지 않으므로 버전을 명시하는 의존성 추가시 주의하자.

//모든 스프링 클라우드 프로젝트에 사용된다. 
implementation 'org.springframework.cloud:spring-cloud-starter-config' 

//스프링 클라우드 컨피그 서버를 위한 핵심 라이브러리이다. 
implementation 'org.springframework.cloud:spring-cloud-config-server'

설정 서버를 동작하게 하려면 여러 파일을 설정해야 한다. 우선 application.yml 파일을 resources 디렉토리에 생상하고 설정을 해야한다. .yml 파일에는 스프링 클라우드 컨피그 서버가 수신 대기할 포트, 구성 데이터를 제공하는 백엔드 위치 등 정보를 명시한다.

다음으로는 구성 데이터를 보관할 백엔드 저장소를 지정해야 한다. 여기서는 세가지 환경 기본(default), 개발(dev), 운영(prod) 환경을 위한 애플리케이션 구성 데이터를 설정한다.

스프링 클라우드 컨피그에서 모든 것은 계층 구조로 동작한다. 앱 구성은 앱 이름을 먼저 표시하고 구성 정보가 필요한 각 환경별 프로퍼티 파일로 구분한다.

스프링 클라우드 컨피그는 환경별 프로퍼티를 HTTP 엔드포인트로 노출한다.

  • 애플리케이션 구성 파일의 명명 규칙은 {appName}-{env}.yml 이다.

  • 파일 트리 구조

└── src 
	├── main 
	│   ├── java 
	│   │   └── springcloud 
	│   │   	└── microservice 
	│   │   		└── MicroserviceApplication.java 
	│   └── resources 
	│   	├── application.yml 
	│   	├── config 
	│   	│   └── licensingservice 
	│   	│   	├── licensingservice-dev.yml 
	│   	│   	├── licensingservice-prod.yml 
	│   	│   	└── licensingservice.yml 
	│   	├── static 
	│   	└── templates 
	└── test 
		└── java 
			└── springcloud 
				└── microservice 
					└── MicroserviceApplicationTests.java

2) YML 파일 구성

  • licensingservice.yml
example.property: "I AM IN THE DEFAULT" 
spring.jpa.database: "POSTGRESQL" 
spring.datasource.platform: "postgres" 
spring.jpa.show-sql: "true" 
spring.database.driverClassName: "org.postgresql.Driver" 
spring.datasource.url: "jdbc:postgresql://database:5432/eagle_eye_local" 
spring.datasource.username: "postgres" 
spring.datasource.password: "{cipher}4788dfe1ccbe6485934aec2ffeddb06163ea3d616df5fd75be96aadd4df1da91" 
spring.datasource.testWhileIdle: "true" 
spring.datasource.validationQuery: "SELECT 1" 
spring.jpa.properties.hibernate.dialect: "org.hibernate.dialect.PostgreSQLDialect"
  • licensingservice-dev.yml
spring.jpa.database: "POSTGRESQL"
spring.datasource.platform: "postgres"
spring.jpa.show-sql: "false" 
spring.database.driverClassName: "org.postgresql.Driver" 
spring.datasource.url: "jdbc:postgresql://database:5432/eagle_eye_dev" 
spring.datasource.username: "postgres_dev" 
spring.datasource.password: "{cipher}d495ce8603af958b2526967648aa9620b7e834c4eaff66014aa805450736e119" 
spring.datasource.testWhileIdle: "true" 
spring.datasource.validationQuery: "SELECT 1" 
spring.jpa.properties.hibernate.dialect: "org.hibernate.dialect.PostgreSQLDialect"
  • licensingservice-prod.yml
example.property: "I AM A PROD PROPERTY OVERRIDE" 
spring.jpa.database: "POSTGRESQL" 
spring.datasource.platform: "postgres" 
spring.jpa.show-sql: "true" 
spring.database.driverClassName: "org.postgresql.Driver" 
spring.datasource.url: "jdbc:postgresql://database:5432/eagle_eye_prod" 
spring.datasource.username: "postgres_prod" 
spring.datasource.password: "{cipher}217b23d6209b10bd82c49a9df6490670052a9cd9d4403dcc1288db21c35c48ac"
 spring.datasource.testWhileIdle: "true" 
spring.datasource.validationQuery: "SELECT 1" 
spring.jpa.properties.hibernate.dialect: "org.hibernate.dialect.PostgreSQLDialect"

3) 스프링 클라우드 컨피그 부트스트랩 클래스 구성

  • @EnableConfigServer : 서비스를 스프링 클라우드 컨피그 서버로 사용가능하게 한다.
@SpringBootApplication
@EnableConfigServer
public class ConfigServerApplication {
	public static void main(String[] args) {
    	SpringApplication.run(ConfigServerApplication.class, args);
    }
}

4) 파일 시스템과 스프링 클라우드 컨피그 서버 사용

스프링 클라우드 컨피그 서버는 resources/application.yml 파일의 항목을 사용해 구성 데이터를 보관할 장소를 지정한다. 파일 시스템을 기반으로한 저장소를 가장 쉽게 만들 수 있다.

  • application.yml
server: 
	port: 8888 
spring: 
	profiles: 
		active: native # 구성 정보를 저장할 백엔드 저장소(파일 시스템) 
	cloud: 
		config: 
			server: 
				native: 
					searchLocations: file:///Users/zeros/Downloads/microservice/src/main/resources/config/licensingservice #{전체 경로}

실행 결과

3. 스프링 클라우드 컨피그와 스프링 부트 클라이언트의 통합

1) 시스템의 흐름

  • 라이선싱 서비스를 처음 시작할 때는 명령줄로 두 가지 정보(스프링 프로파일과 스프링 컨피그 서버와 통신할 때 사용하는 엔드포인트) 를 전달한다.
  • 스프링 프로파일은 스프링 서비스가 추출하는 프로퍼티 환경에 매핑된다.
  • 라이선싱 서비스가 처음 부팅하면 전달받은 프로파일과 엔드 포인트를 사용해 스프링 클라우드 컨피그 서버와 통신하고 컨피그 서버는 구성 정보를 조회한 후 라이선싱 서비스에 전달한다.


2) 스프링 클라우드 컨피그를 위한 라이선싱 서비스 구현

스프링 클라우드 컨피그를 사용하는 스프링 부트 서비스의 구성정보는 bootstrap.ymlapplication.yml 파일 중 한 곳에 설정된다.

  • bootstrap.yml 파일은 구성 정보가 사용되기 전에 애플리케이션 프로퍼티를 먼저 읽는다. 일반적으로 bootstrap.yml 파일에 서비스 애플리케이션 이름, 프로파일과 컨피그 서버에 접속할 수 있는 URI가 명시된다.
  • 로컬에 유지하고 싶은 구성정보는 appliation.yml 파일에 설정할 수 있다. 대게 컨피그 서비스가 가용하지 않을 때도 사용할 수 있는 구성 데이터를 저장한다.

라이선싱 서비스의 bootstrap.yml 구성

  • spring.application.name : 애플리케이션 이름이며 스프링 클라우드 컨피그 서버의 디렉터리 이름과 일치해야 한다. 따라서 라이선싱 서비스를 위해 licensingservice 디렉터리 가 컨피그 서버에 있어야 한다.
  • 별 다른 프로퍼티 없이 라이선싱 서버를 실행한다면 자동으로 bootstrap.yml 에 있는 정보를 보고 클라우드 컨피그 서버에 접속을 시도한다.
spring: 
	application: 
		name: licensingservice #스프링 클라우드 컨피그 클라이언트가 어떤 서비스를 조회하는지 
	profiles: 				   #알 수 있도록 라이선싱 서비스 이름 지정 
		active: 
			defualt #서비스가 실행할 기본 프로파일을 지정한다. 프로파일은 환경에 매핑된다. (dev, prod) 
	cloud: 
		config: 
			uri: http://localhost:8888 #스프링 클라우드 컨피그 서버의 위치를 지정한다.

실행시 프로퍼티 값을 재설정

프로젝트를 JAR 파일로 컴파일하고 시스템 프로퍼티를 재정의하는 옵션인 -D와 함께 JAR를 실행하면 기본 프로파일 값을 재정의하고 다른 환경을 지정할 수 있다.

java -Dspring.cloud.config.uri=http://localhost:8888 
	 -Dspring.profiles.active=dev 
	 -jar target/licensing-service-0.0.1-SNAPSHOT.jar

도커로 환견설정을 지정하고 실행하는 방법

  • docker-compose.yaml
licensingservice: 
	image: licensing-service 
	ports: 
		- "8080:8080" 
	environment: 
		PROFILE: "dev" 
		CONFIGSERVER_URI: "http://configserver:8888" 
		CONFIGSERVER_PORT: "8888" 
		DATABASESERVER_PORT: "5432"
  • run.sh
java -Dspring.cloud.config.uri=$CONFIGSERVER_URI 
	 -Dspring.profiles.active=$PROFILE 
     -jar /usr/local/licensingservice/ @project.build.finalName@.jar

3) 스프링 클라우드 서버로 데이터 소스 연결

애플리케이션에서 컨피그 서버에 접근하여 데이터를 가져오면 구성 정보가 마이크로 서비스에 직접 주입된다. DB구성 정보로 라이선싱 마이크로서비스 구성을 설정하면, 표준 스프링 컴포넌트를 이용해 데이터를 빌드하고 조회할 수 있다.

@Value 애노테이션으로 프로퍼티 직접 읽기

스프링 데이터가 자동으로 DB의 구성 데이터를 연결해서 주입하지만 다른 프로퍼티는 @Value 애노테이션을 사용하여 주입해야 한다.

  • 여기서는 컨피그 서버에서 example.property 를 가져와서 주입한다.
import org.springframework.beans.factory.annotation.Value; 
import org.springframework.stereotype.Component; 

@Component
public class ServiceConfig {

	@Value("${example.property}")
    private String exampleProperty;
    
    public String getExampleProperty() {
    	return exampleProperty;
    }
}

4) Git과 스프링 클라우드 컨피그 서버 사용

그러나 파일 시스템은 컨피그 서버의 저장소로 좋은 선택이 아니다. 그 이유는 개발팀이 컨피그 서버의 모든 인스턴스에 마운트될 공유 파일 시스템을 설정하고 관리해야 하기 때문이다. 스프링 클라우드 컨피그 서버는 앱 구성 프로퍼티를 호스팅하는 다양한 백엔드 저장소와 통합될 수 있다. GIT을 사용하면 구성 관리 프로퍼티를 저장할 때 소스 관리의 모든 혜택을 누리고 빌드 및 배포 파이프라인에서 프로퍼티 구성 파일의 배포를 쉽게 통합할 수 있다.

스프링 클라우드 컨피그용 application.yml 파일

server: 
	port: 8888 
spring: 
	cloud: 
		config: 
			server: 
				encrypt.enabled: false 
				git: #저장소로 깃을 사용한다로 알린다. 
					uri: https://github.com/klimtever/config-repo/ #깃 서버와 repo URL을 전달 
					searchPaths: licensingservice,organizationservice #리포지토리 내 폴더(내부에 구성 파일이 위치) 
					username: native-cloud-apps
					password: 0ffended

5) 스프링 클라우드 컨피그 서버에서 프로퍼티 갱신

컨피그 서버를 사용할 때 나오는 첫 질문은 프로퍼티가 변경될 때 컨피그 서버가 어떻게 동적으로 앱을 갱신하는지 하는 점이다. 스프링 클라우드 컨피그 서버는 항상 최신 버전의 프로퍼티를 제공한다. 하부 저장소의 프로퍼티를 변경하면 바로 반영한다.

하지만 서비스 애플리케이션은 시작할 때만 프로퍼티를 읽어오며 컨피그 서버에서 변경된 프로퍼티를 자동으로 읽어오지 않는다. 스프링 부트 액추에이터는 @RefreshScope 애노테이션을 제공하므로 스프링 부트 서비스 애플리케이션이 /refresh 엔드포인트 를 사용해 앱 구성 정보를 다시 읽어올 수 있다.

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

주의사항

  • 이 애노테이션은 애플리케이션 구성에 있는 사용자 정의 스프링 프로퍼티만 다시 로드한다. 즉 DB 구성정보처럼 스프링 데이터에서 정의된 구성은 다시 로드하지 않는다.
  • 업데이트를 수행하기 위해 POST http://<server>:8080/actuator/refresh 엔트포인트를 호출한다.

6) 번외: 마이크로서비스 구성정보 업데이트

  • 스프링 클라우드 컨피그 서비스는 이 서비스를 이용하는 모든 클라이언트에게 변경이 일어났다고 알려주는 푸시 기반의 메커니즘을 제공한다. 이는 RabbitMQ 같은 미들웨어를 추가해야 한다. 유용한 방법이지만 모든 컨피그 서버가 지원하는 메커니즘은 아니다.
  • 다른 방법은 스프링 클라우드 컨피그의 앱 프로퍼티를 업데이트 한 후 서비스 디스커버리 엔진으로 모든 서비스 인스턴스를 조회해 /refresh 엔트포인드를 직접 호출하는 간단한 스크립트를 작성하는것이다.
  • 마지막 방법은 모든 서버와 컨테이너가 새로운 프로퍼티를 업데이트하도록 재시작하는 것이다.

0개의 댓글