많은 개발자들이 애플리케이션 코드에서 구성 정보를 분리한다. 이것으로 컴파일을 거치지 않고 구성은 쉽게 변경할 수 있게 되었지만 애플리케이션과 함께 관리되고 배포되어야 하는 산출물이 추가되어 복잡해진다.
많은 개발자가 구성 정보를 저장하기 위해 저수준의 프로퍼티 파일(YAML, XML)로 전환한다. 대게 이런 파일은 DB 및 미들웨어의 접속 정보와 앱의 행동 양식을 저장하는 메타데이터가 존재하는 서버에 둔다.
이러한 방식은 애플리케이션이 적은 상황에서는 적용될 수 있지만 수백 개의 마이크로서비스의 수많은 인스턴스가 실행되는 클라우드 기반의 애플리케이션 상황에서는 관리가 힘들다.
따라서 클라우드 기반의 마이크로서비스 개발에서는 다음 사항이 강조된다.
실제 물리적인 서비스의 배포와 서비스 구성 정보를 완전히 분리하자. 시작하는 서비스에 환경변수로 전달하거나 중앙 저장소에서 읽어와 구성 정보를 전달한다.
서비스 인터페이스 뒷단에 있는 구성 데이터의 접근 방식을 추상화한다. 애플리케이션이 REST 기반의 JSON 서비스를 사용해 구성 데이터를 조회하게 만들자.
클라우드 기반의 앱은 수백 개의 서비스가 존재할 수 있으므로 구성 정보를 보관하는 저장소의 개수를 최소로 줄이는 것이 매우 중요하다.
앱 구성 정보를 배포된 서비스와 완전히 분리하고 중앙 집중화하므로 어떤 솔루션을 사용하더라도 고가용성과 다중성을 구현할 수 있어야 한다. 핵심적으로 기억할 사항은 구성 정보를 실제 코드 외부로 분리하면 코드 외부로 관리하고 버전 제어를 해야할 외부 의존성이 생긴다는 것이다.
구성 관리는 MSA의 부트스트래핑(2단계) 에서 일어난다. 이를 자세히 보면 아래의 과정과 같다.
스프링 클라우드 컨피그 서버는 스프링 부트로 만든 REST 기반의 애플리케이션이다. 독립형 서버로 제공되지 않아 기존 스프링 부트 애플리케이션에 내장하거나 새로운 스프링 부트 프로젝트를 만들어 내장하는 방법으로 시작할 수 있다.
스프링 부트 버전과 클라우드 버전이 다르면 호환이 되지 않으므로 버전을 명시하는 의존성 추가시 주의하자.
//모든 스프링 클라우드 프로젝트에 사용된다.
implementation 'org.springframework.cloud:spring-cloud-starter-config'
//스프링 클라우드 컨피그 서버를 위한 핵심 라이브러리이다.
implementation 'org.springframework.cloud:spring-cloud-config-server'
설정 서버를 동작하게 하려면 여러 파일을 설정해야 한다. 우선 application.yml
파일을 resources 디렉토리
에 생상하고 설정을 해야한다. .yml
파일에는 스프링 클라우드 컨피그 서버가 수신 대기할 포트, 구성 데이터를 제공하는 백엔드 위치 등 정보를 명시한다.
다음으로는 구성 데이터를 보관할 백엔드 저장소를 지정해야 한다. 여기서는 세가지 환경 기본(default)
, 개발(dev)
, 운영(prod)
환경을 위한 애플리케이션 구성 데이터를 설정한다.
스프링 클라우드 컨피그에서 모든 것은 계층 구조로 동작한다. 앱 구성은 앱 이름을 먼저 표시하고 구성 정보가 필요한 각 환경별 프로퍼티 파일로 구분한다.
애플리케이션 구성 파일의 명명 규칙은 {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
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"
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"
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"
@EnableConfigServer
: 서비스를 스프링 클라우드 컨피그 서버로 사용가능하게 한다.@SpringBootApplication
@EnableConfigServer
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
스프링 클라우드 컨피그 서버는 resources/application.yml
파일의 항목을 사용해 구성 데이터를 보관할 장소를 지정한다. 파일 시스템을 기반으로한 저장소를 가장 쉽게 만들 수 있다.
server:
port: 8888
spring:
profiles:
active: native # 구성 정보를 저장할 백엔드 저장소(파일 시스템)
cloud:
config:
server:
native:
searchLocations: file:///Users/zeros/Downloads/microservice/src/main/resources/config/licensingservice #{전체 경로}
스프링 클라우드 컨피그를 사용하는 스프링 부트 서비스의 구성정보는 bootstrap.yml
과 application.yml
파일 중 한 곳에 설정된다.
bootstrap.yml
파일은 구성 정보가 사용되기 전에 애플리케이션 프로퍼티를 먼저 읽는다. 일반적으로 bootstrap.yml
파일에 서비스 애플리케이션 이름, 프로파일과 컨피그 서버에 접속할 수 있는 URI가 명시된다.appliation.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
licensingservice:
image: licensing-service
ports:
- "8080:8080"
environment:
PROFILE: "dev"
CONFIGSERVER_URI: "http://configserver:8888"
CONFIGSERVER_PORT: "8888"
DATABASESERVER_PORT: "5432"
java -Dspring.cloud.config.uri=$CONFIGSERVER_URI
-Dspring.profiles.active=$PROFILE
-jar /usr/local/licensingservice/ @project.build.finalName@.jar
애플리케이션에서 컨피그 서버에 접근하여 데이터를 가져오면 구성 정보가 마이크로 서비스에 직접 주입된다. DB구성 정보로 라이선싱 마이크로서비스 구성을 설정하면, 표준 스프링 컴포넌트를 이용해 데이터를 빌드하고 조회할 수 있다.
스프링 데이터가 자동으로
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;
}
}
그러나 파일 시스템은 컨피그 서버의 저장소로 좋은 선택이 아니다. 그 이유는 개발팀이 컨피그 서버의 모든 인스턴스에 마운트될 공유 파일 시스템을 설정하고 관리해야 하기 때문이다. 스프링 클라우드 컨피그 서버는 앱 구성 프로퍼티를 호스팅하는 다양한 백엔드 저장소와 통합될 수 있다. GIT을 사용하면 구성 관리 프로퍼티를 저장할 때 소스 관리의 모든 혜택을 누리고 빌드 및 배포 파이프라인에서 프로퍼티 구성 파일의 배포를 쉽게 통합할 수 있다.
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
컨피그 서버를 사용할 때 나오는 첫 질문은 프로퍼티가 변경될 때 컨피그 서버가 어떻게 동적으로 앱을 갱신하는지 하는 점이다. 스프링 클라우드 컨피그 서버는 항상 최신 버전의 프로퍼티를 제공한다. 하부 저장소의 프로퍼티를 변경하면 바로 반영한다.
하지만 서비스 애플리케이션은 시작할 때만 프로퍼티를 읽어오며 컨피그 서버에서 변경된 프로퍼티를 자동으로 읽어오지 않는다. 스프링 부트 액추에이터는 @RefreshScope
애노테이션을 제공하므로 스프링 부트 서비스 애플리케이션이 /refresh 엔드포인트
를 사용해 앱 구성 정보를 다시 읽어올 수 있다.
@SpringBootApplication
@RefreshScope
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
POST http://<server>:8080/actuator/refresh
엔트포인트를 호출한다.푸시
기반의 메커니즘을 제공한다. 이는 RabbitMQ 같은 미들웨어를 추가해야 한다. 유용한 방법이지만 모든 컨피그 서버가 지원하는 메커니즘은 아니다./refresh
엔트포인드를 직접 호출하는 간단한 스크립트를 작성하는것이다.