정윤진 피보탈 랩 프린시플 테크놀로지스트

[컴퓨터월드]

▲ 정윤진 Pivotal Lab Principal Technologist

1. 스프링 부트  <2017.5월호>
2. 스프링 클라우드 Config server  <이번호>
3. 스프링 클라우드 Service discovery
4. 스프링 클라우드 Zuul
5. 스프링 클라우드 Hystrix
6. 스프링 클라우드 Zipkin


코드와 설정, 그리고 참조

서버에서 동작하는 대부분의 애플리케이션, 심지어 서버 자체도 올바르게 동작하기 위해서는 다양한 설정을 필요로 한다. 리눅스 기반의 운영체제라면 대부분 /etc 하위에 있는 다양한 설정 파일들을 구성해 사용할 것이다. 이 시스템 또는 OS 관련 설정들은 보통 파일로 저장돼 서비스 데몬이나 시스템이 시작할 때 참조된다. 따라서 이 설정의 내용에 변경이 발생하게 되면 관련 있는 서비스 데몬이나 시스템 자체를 재시작해야 한다.

문제는 이 재시작이다. 시스템의 재시작은 곧 서비스의 중단을 의미한다. 재부팅이 필요하다면 재부팅이 진행되는 동안 역시 서비스가 중단된다. 웹서비스를 구동하는 서버들이라면 밸런서 사용을 통해 서비스의 중단을 막을 수 있겠지만, 만약 DB라면 전혀 다른 이야기가 된다. DB의 클러스터링 동작은 보통 굉장한 비용이 들어가는 작업이며, DB가 중단되거나 클러스터링을 통해 전환되는 동안 속도 관련 문제가 발생하거나 한다면 전체 서비스에 치명적인 문제가 발생하기 때문이다.

시스템 운영에 필요한 설정의 변경에는 다양한 위험이 따르므로 잘 변경하지는 않지만, 해야 한다면 지금까지 운영에 사용한 다양한 기법들을 사용해 다운타임을 줄이는 방법으로 적용하는 것이 일반적이다. 하지만, 코드 안에 설정이 종속돼있는 경우라면 더욱 더 심각한 문제가 발생한다. 코드에 종속된 설정은 아래와 같이 매우 많은 문제를 야기한다.

첫째, 일단 설정의 변경 자체가 쉽지 않다. 코드의 어느 부분에서 어떤 설정을 참조하는지 모르는 경우가 현업에서는 매우 많다. 심지어는 코드 내에 적용된 설정을 변경하더라도 애플리케이션에 반영되지 않는 경우도 있다. 이것은 애플리케이션에서 참조하는 설정이 어디서 어떻게 중첩되는지 모르는 경우에 발생한다.

둘째, 새로운 빌드를 만들어야 한다. 코드가 실행되려면 언어나 프레임워크에 따라 빌드가 필요할 수 있는데, 이 빌드는 애플리케이션의 크기, 서드파티 의존성, 수행해야 할 테스트의 단계 및 시간에 따라 빌드에 시간을 빼앗길 가능성이 많고, 이 빌드 환경을 구성하고 유지하는 것 자체가 매우 큰 일이 될 수 있다.

셋째, 빌드가 다시 필요하다는 것은 설정의 변경을 반영한 빌드가 다시 전체 서버에 배포돼야 함을 의미한다. 어떠한 기능 추가나 변경도 없이, 그저 설정 변경만을 위해 전체 서비스에 배포가 진행돼야 하는 것이다.

서비스를 운영하면서 설정 변경은 매우 필수적이라고 할 수 있다. 더욱 높은 안정성 및 더 빠른 속도 제공을 위해 메모리나 디스크와 같은 자원 배분이나 애플리케이션의 문제 추적을 위한 로깅의 옵션, 그리고 애플리케이션의 기능을 조정하기 위한 feature flag 같은 다양한 사항의 변경이 필요할 수 있다. 하지만 이런 설정을 변경할 때마다 새로운 배포가 필요하고, 새로운 빌드와 새로운 QA가 필요하다면 서비스의 개선속도에 문제가 발생하는 것은 당연하거니와, 꼭 필요한 설정의 변경으로 인해 잘못되면 서비스에 문제가 발생하는 위험을 안고 있기도 하다.

클라우드에 최적화된 애플리케이션에서는 이런 문제를 다양한 방법으로 처리할 수 있다. 대부분의 클라우드 서비스에서는 가상머신을 사용하는 경우 사용자가 설정한 ‘템플릿’을 사용하도록 하는 방법을 권장한다. 예를 들어 원하는 용도의 서버를 선택해 기동하고, 원격으로 접근해 필요한 서비스들의 설치를 수행한다. 애플리케이션 구동에 필요한 패키지, 웹 서버, 필요에 따라서는 DB와 같은 사항들이 포함된다. 필요한 설정을 모두 마치고 난 후에는 이 서버를 ‘이미징’이라는 과정을 통해 ‘템플릿화’한다.

템플릿화돼 매번 동일한 서버를 생성할 수 있도록 준비된 서버는 언제나 동일한 원본을 사용해 복제되는 파일들과 같다. 동일한 원본을 사용해 복제되는 서버들은 클라우드 서비스 공급자가 제공하는 옵션에 따라 다양한 성능으로 기동될 수 있으며, 필요에 따라 로드밸런서에 연결되거나 ‘오토스케일링’이라 불리는 부하에 따른 자동 수량 조정 옵션 등을 적용할 수 있기도 하다.

이러한 클라우드 서비스가 제공하는 서버의 즉시 조달에 의한 장점은 필요한 리소스를 ‘사전에 정의된 형태로’ 빠르게 생성하고, 그 효용이 없어지면 바로 삭제하는 형태로 서비스를 구성하도록 하는 방법을 지원한다는 것이다. 이는 동일한 역할을 하는 다른 서비스에 문제가 발생한 경우에도 해당 서버를 빠르게 추가해 서비스에 예상되는 다운타임을 줄이는 역할을 하기도 한다.

하지만, 이런 템플릿을 사용하는 방법만으로는 애플리케이션의 설정 변경을 직접 지원하는 방법은 해결이 쉽지 않다. 빌드된 애플리케이션이나 소스코드를 이미지 과정에서 추가해 템플릿에 사용한다면 매번 동일한 버전의 애플리케이션을 구동하는 것에는 용이하겠지만, 애플리케이션에 업데이트나 설정의 변경이 필요할 때마다 템플릿을 다시 생성해야하는 문제가 생긴다.
이런 문제를 해결하기 위해 부트 스트래핑의 과정에서 별도의 커스텀 스크립트를 추가해 외부에서 참조하는 형태로 구성하는 방법이 많이 사용된다. 예를 들면 빌드가 필요 없는 언어의 경우 필요한 설정이나 애플리케이션 코드를 부팅 과정에서 git clone 커맨드를 수행하는 스크립트를 추가해 원하는 위치에 원하는 버전의 소스코드가 배치될 수 있도록 하는 것이다.

부트스트래핑을 사용하는 방법은 궁극적으로는 업데이트가 필요할 때마다 다시 가상머신의 재부팅이 필요하다는 문제를 발생시킨다. 이런 방법으로 서비스를 구성했다면 ‘롤링 업데이트’라 불리는 방식으로 서비스의 다운타임 없는 배포를 위해 서버를 순차적으로 재부팅하거나, 스크립트를 어떠한 방법으로든 트리거를 해주거나, 또는 일시적 네트워크 장애로 인한 예기치 못한 예외 처리가 필요할 수 있기 때문에 구조적으로 스크립트가 복잡해진다. 어쨌든 중요한 것은 업데이트에 따른 서비스 다운타임이 발생한다는 점이다.

이런 문제를 해결하기 위해, 클라우드에 최적화된 애플리케이션은 조금 다른 방법을 사용한다. 즉, 애플리케이션이 구동될 때 참조하는 별도의 설정만을 관리하는 애플리케이션 서버가 존재하는데, 이 서버는 보통 REST 방식으로 설정이 필요한 클라이언트 애플리케이션들에 설정을 전달한다. 그리고 이 설정을 관리하는 서버는 설정의 원본으로 git 같은 형상관리 도구를 사용할 수 있도록 구성한다.

이런 동작을 하는 대표적인 오픈소스로는 2012년에 발표된 넷플릭스의 Archaius (링크)가 있다. 이 설정 관리 도구는 아래와 같은 기능을 제공한다.

▶ 동적 타입 프로퍼티 변경
▶ 수많은 서비스에 높은 가용성으로 설정과 관련된 오퍼레이션 제공
▶ polling 프레임워크는 설정 서버가 참조하는 설정이 변경됐을 때 즉시 변경 반영 가능
▶ 관련 서비스 설정에 필요한 파일들을 외부에서 중앙집중적으로 관리 가능
▶ property mutation 위한 콜백 메커니즘 제공
▶ JConsole 통해 접근 가능한 JMX MBean

넷플릭스의 Archaius는 ‘카멜레온’을 가리킨다. 이미 동작하고 있거나 신규로 기동되는 애플리케이션에 필요한 설정의 제공, 그리고 더 중요한 변경의 동작을 매우 안전하게 지원하는 도구라고 할 수 있다. 아래 이미지는 넷플릭스 Archaius 위키페이지(링크)에 소개된 간단한 참조 모델을 보여준다.

 

일견 보기에 복잡해 보이지만, 실제로 주요 콘셉트는 매우 간단하다. 거의 대부분의 애플리케이션은 그 구동을 위해 필요한 설정을 다양한 단계로 참조 가능하도록 구성할 수 있다. Archaius에서는 컨테이너 설정부터 애플리케이션 구동에 필요한 라이브러리 및 모듈, 애플리케이션 자체 설정과 런타임 설정을 그 참조 구조에 따라 애플리케이션에 REST 방법으로 제공하는 역할을 한다.

자바 애플리케이션의 구동에 필요한 설정들은 현업의 대부분 서비스에서는 이렇게 중앙집중화돼 관리되는 경우가 매우 드물다. 어떤 설정이 default를 참조하는지, 또는 환경 변수에 의해 오버라이드된 값을 참조하는지 도무지 알 수 없는 문제를 발생시켜 서비스를 더욱 불안정하게 하는 이 같은 위험을 Archaius 같은 도구를 통해 관리가 가능하다.

더욱 중요한 사항은, 이와 같은 설정을 동적으로 변경할 수 있는 방법을 제공한다는 것이다. 설정이 기록된 원본을 URL로 지정할 수도 있으며, 필요에 따라 RDB나 key-value 저장소를 사용하는 것도 가능하다.

넷플릭스가 제공하는 이 Archaius를 직접 사용하기 원한다면 위 위키 링크를 통해 필요한 정보를 얻을 수 있을 것이다. 하지만 이러한 도구에 대한 이해를 바탕으로 빠르게 구현을 원하는 자바 스프링 개발자라면 지금 바로 적용할 수 있는 방법이 있다. Config server와 client 는 아래와 같이 도식화 될 수 있다.

 

위 그림에서 몇 가지 주목할 만한 내용은 아래와 같다.

▶ Config server는 설정 파일들이 위치한 소스로 Git을 사용하고 있다.
▶ Config server는 Git에 저장된 설정 파일들을 REST 방법으로 App A, App B, App C에 제공하고 있다.
▶ App A, App B, App C는 Config Server로부터 Pull 통해 설정 업데이트를 받는다.
▶ App A, App B, App C는 모두 Cloud Bus에 pub/sub 구조를 통해 메시지를 주고받을 수 있다.
▶ App A, B, C는 각각 Config Client가 제공하는 /bus/refresh의 엔드포인트를 통해 설정을 업데이트할 수 있다.
▶ App A, B, C 중 하나가 /refresh를 통해 업데이트를 받으면 Cloud Bus를 통해 설정 업데이트를 다른 App에 전달한다.

개발자는 애플리케이션의 설정에 필요한 파일들을 구성하고, 이를 Git에 push한다. git 외에도 다양한 소스를 사용할 수 있지만, 설명의 편의를 위해 일단은 git을 사용한다. 그럼 Config server는 업데이트된 내용을 즉시 반영해 요청이 오는 경우 응답할 준비가 된다. 이때 클라우드에서 신규로 생성되는 애플리케이션은 Config Server로부터 자신의 애플리케이션 설정에 필요한 내용을 내려받게 된다. 하지만 기존에 동작하는 애플리케이션들은 업데이트되지 않은 상태가 되는데, 이것은 Config Server / Client의 안전에 기반 한 디자인 철학이 반영된 것이다.

기존에 이미 설정을 내려 받아 동작하고 있는 애플리케이션에 업데이트가 필요하면, /refresh의 엔드 포인트에 비어있는 POST 요청을 함으로써 업데이트가 필요한 사실을 알릴 수 있다. 그리고 이렇게 동작하는 애플리케이션이 하나가 아니라 다수 컨테이너 또는 서버에서 동작하고 있다면, 메시지 버스를 사용해서 /refresh 요청이 있을 때 모두 업데이트를 수행하는 것이 가능하다. 즉, 설정이 반영될 시점을 결정할 수 있다는 것이다.

코드에 들어가기에 앞서, 이 같은 구성의 장점에 대해 살펴볼 필요가 있다.

▶ 모든 설정은 별도의 코드 저장소에 저장돼 제공되며, 따라서 설정의 잘못이나 새로운 반영이 필요한 경우 코드 저장소에 업데이트하면 된다. 이는 설정 변경에 대한 이력이 관리된다는 뜻이기도 하다.
▶ 코드와 설정이 분리된다. 빌드는 릴리즈돼야 하는 대상에 따라 필요한 설정을 구동시점에 참조한다. 환경에 따라 서로 다른 DB, 파일 저장소, 메시지 버스, 캐시 등을 코드 변경 없이 참조할 수 있게 하는 장점을 가진다. 테스트와 스테이징, 프로덕션으로 이동할 때 서로 다른 설정으로 인해 코드를 변경하지 않아도 된다.
▶ 애플리케이션 재시작 없이 설정을 반영할 수 있다. 모든 설정이 가능한 것은 아니지만, 애플리케이션 내에서 사용될 수 있는 A/B 테스트 등을 위한 Feature flag와 같은 것이 대표적이다. 애플리케이션 재시작 없이 설정을 변경할 수 있는 장점은 서비스를 더욱 더 높은 가용성으로 유지한 채 업데이트 할 수 있는 기능성을 제공할 것이다. 코드 내에서 refresh를 통해 신규 설정이 반영될 부분을 @RefreshScope를 통해 지정할 수 있다.

아래는 Config server 를 시작하는 방법이다.

▶ 스프링 이니셜라이저(링크)에 접근한다. Artifact 이름을 config-server 로 지정한다.
▶ 의존성에 Config Server와 Actuator를 추가한다.
▶ Generate Project를 눌러 프로젝트 압축파일을 다운로드한다.
▶ 압축을 해제하고 IDE로 연다.

 

src/main/java/com.example/ConfigServerApplication.java 파일

package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer; ## 추가

@SpringBootApplication
@EnableConfigServer ## 추가
public class ConfigServerApplication {

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

src/main/resources/application.properties 파일

spring.application.name=config-server
spring.cloud.config.server.git.uri=https://github.com/younjinjeong/spring-cloud-event-sourcing-config

management.security.enabled=false
server.port=8888

코드의 변경 내용은 많지 않다. @EnableConfigServer를 통해 Config Server를 활성화한다. 그리고 애플리케이션에 이름을 주고, 이 Config Server가 참조할 github의 주소를 application.properties에 명시한다. YAML을 사용하는 것도 가능하다.

 

준비는 끝났다. 빌드하고, 구동해본다.

$ mvn clean package


$ java -jar target/config-server-0.0.1-SNAPSHOT.jar

별 문제가 없다면 8888 포트로 Config server가 기동됐을 것이다. 브라우저나 curl을 통해 해당 주소(http://localhost:8888/account-service/default)로 GET 요청을 수행해보자. JSON view나 jq 같은 도구가 설치돼있다면 더 가독성 좋은 결과화면을 볼 수 있을 것이다.

 

아래의 커맨드도 실행해보도록 하자.

$ curl http://localhost:8888/account-service/cloud


curl http://localhost:8888/account-service/docker | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1088 0 1088 0 0 366 0 --:--:-- 0:00:02 --:--:-- 366
{
"name": "account-service",
"profiles": [
"docker"
],
"label": null,
"version": null,
"state": null,
"propertySources": [
{
"name": "https://github.com/younjinjeong/spring-cloud-event-sourcing-config/account-service.yml#docker",
"source": {
"spring.profiles": "docker",
"spring.datasource.url": "jdbc:mysql://mysql:3306/dev",
"spring.datasource.username": "root",
"spring.datasource.password": "dbpass",
"spring.datasource.initialize": true,
"security.oauth2.resource.userInfoUri": "http://${DOCKER_IP:192.168.99.100}:8181/uaa/user",
"security.oauth2.client.client-id": "acme",
"eureka.instance.prefer-ip-address": true,
"eureka.client.registerWithEureka": true,
"eureka.client.fetchRegistry": true,
"eureka.client.serviceUrl.defaultZone": "http://discovery-service:8761/eureka/"
}
},
{
"name": "https://github.com/younjinjeong/spring-cloud-event-sourcing-config/account-service.yml",
"source": {
"spring.profiles.active": "development"
}
},
{
"name": "https://github.com/younjinjeong/spring-cloud-event-sourcing-config/application.yml",
"source": {
"info.id": "${spring.application.name}",
"logging.level.org.springframework.security": "DEBUG"
}
}
]
}

브라우저를 사용해 application.properties에 지정한 github의 주소(https://github.com/younjinjeong/spring-cloud-event-sourcing-config)에 접근해본다.

 

github 링크에 보면 다양한 이름의 애플리케이션 설정을 확인할 수 있다. 즉, 지금까지 처리한 일은 다음과 같다.

▶ 스프링 부트를 사용해 Config server를 기동했다.
▶ Config server는 지정된 github 주소에 있는 다양한 애플리케이션 설정을 8888번 포트를 통해 클라이언트에 전달할 수 있는 상태가 됐다.
▶ 각 설정은 별도의 ‘프로파일’을 가질 수 있으며, 이는 지정되지 않는 경우 default, 또는 개발자가 지정한 ‘cloud’와 같은 프로파일을 사용할 수 있다.

서비스에 필요한 다양한 애플리케이션의 설정을 이 같은 방법으로 관리하는 것은 매우 효율적이다. 팀이 관리하고 있는 서비스에 필요한 설정들을 한눈에 살펴볼 수 있고, 무엇보다 코드와 설정이 분리돼있으며 애플리케이션이 기동할 때마다 가장 새로운 설정을 참조하도록 구성할 수 있기 때문이다.

설정을 중앙집중 방식으로 관리하지 않는 개별 스프링 부트 애플리케이션은 application.properties나 bootstrap.properties와 같은 파일을 설정용도로 사용할 수 있다. 하지만 여러 개의 애플리케이션에 사용되는 설정을 Config server를 통해 관리하는 경우에는 모두 동일한 이름을 사용할 수 없는 일이다. 따라서 이 경우에는 다음과 같은 규칙을 적용할 수 있겠다.

▶ application.properties / application.yml 파일은 Config server를 참조하는 모든 클라이언트에 공통적으로 적용할 수 있는 일종의 전역 설정을 배치할 수 있다.
▶ Config client들은 spring.application.name 으로 지정된 자신의 애플리케이션 이름으로 된 설정을 찾는다.
▶ profile은 환경별로 서로 다른 설정을 적용할 수 있도록 사용할 수 있다.

서버가 준비됐으니, 간단한 클라이언트를 만들어보도록 하자. 마찬가지로 스프링 이니셜라이저 페이지에서 시작한다.

 

Dependencies 부분에서 Config client를 잊지 말고 추가하도록 한다. 완료됐다면, Generate project 버튼을 눌러 프로젝트를 다운로드하고 압축을 해제, IDE에서 열도록 한다. Actuator 의존성을 추가하는 것을 잊지 말자.

application.properties 파일

spring.application.name=simple-client
spring.cloud.config.uri=http://localhost:8888
server.port=${PORT:9988}

 

com/example/SimpleClientApplication.java 파일

package com.example;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
public class SimpleClientApplication {

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

}

@RefreshScope
@RestController
class MessageRestController {

@Value("${message}")
private String message;

@RequestMapping("/msg")
String message() {
return this.message;
}
}

 

github 설정 파일(https://github.com/younjinjeong/spring-cloud-event-sourcing-config/blob/master/simple-client.yml)
message: “hello spring boot users!”

Config client 의존성이 추가된 스프링 부트 애플리케이션은 bootstrap.properties 또는 application.properties 등을 통해 Config server의 uri를 설정할 수 있다. 여기에 지정된 Config server를 통해 application.yml과 spring.application.name에 지정된 이름의 설정을 가져와 merge한 후 애플리케이션에 적용한다.

코드는 message 설정을 참조해 /msg로 유입되는 요청에 대해 응답하는 간단한 구성이다. 그리고 여기서 @RefreshScope이 지정됐음을 눈여겨보도록 하자. $ curl http://localhost:9988/msg 커맨드를 통해 원하는 메시지(hello spring users!)가 응답되는지 확인해보도록 하자.

간단한 데모지만, 이 구성을 정리하면 다음과 같이 동작한다. 먼저 github에 원하는 설정을 배치한다. 해당 config server를 참조하는 모든 애플리케이션에 동일하게 적용돼야 할 내용이 있다면, application.yml과 같은 파일을 사용하고, 각 애플리케이션별 독립적으로 필요한 설정이 있다면 애플리케이션이름.yml 파일로 준비한다.

이후 Config server의 설정에 github 설정의 위치를 지정하고 기동하면 config server는 이를 참조해 다른 클라이언트 애플리케이션에 설정을 전달할 준비가 된다. 이후 이 config server의 주소를 가진 config server 클라이언트가 포함된 스프링 부트 애플리케이션들은 config server를 통해 github에 있는 설정을 갖고 자신의 애플리케이션 구동에 사용한다. Config client가 적용된 스프링 부트 애플리케이션은 시작과 동시에 아래 메시지를 확인할 수 있을 것이다.

2017-04-18 03:24:26.337 INFO 10368 --- [ main]
c.c.c.ConfigServicePropertySourceLocator : Fetching config from server at: http://localhost:8888

위 curl 명령어를 사용해 반영한 설정이 정상적으로 동작함을 확인했다. 여기서 스프링 부트 애플리케이션의 변경 없이 message 값을 바꿔보면 어떨까. 만약 스프링 부트를 사용하는 경우에도 프로젝트 파일 내 bootstrap이나 application.yml을 사용하는 경우라면 필요에 따라 새로 애플리케이션을 빌드하고 배포해야 할 것이다. 하지만 스프링 클라우드의 Config 서버와 클라이언트는 전술한 바와 같이 설정을 변경할 수 있는 방법을 제공한다.

이를 직접 사용해 보기 위해서는 위 github 주소를 fork해서 코드 저장소에 대한 쓰기 권한이 필요할 것이다. github를 fork했거나 권한을 가진 코드 저장소가 준비됐다면, Config server 설정에서 github uri 값을 변경해주는 것을 잊지 말도록 하자. Config server 자체는 설정이 변경되면 재기동해야 한다.

이후엔 github에 있는 simple-client 파일 이름을 원하는 대로 변경해준다. (message: “hello, world!”) 저장 후 add, commit, push가 정상적으로 완료되면 먼저 아래와 같은 커맨드로 Config server 설정을 확인해보자.

$ curl http://localhost:8888/simple-client/default | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 444 0 444 0 0 319 0 --:--:-- 0:00:01 --:--:-- 319
{
"name": "simple-client",
"profiles": [
"default"
],
"label": null,
"version": null,
"state": null,
"propertySources": [
{
"name": "https://github.com/younjinjeong/spring-cloud-event-sourcing-config/simple-client.yml",
"source": {
"message": "hello world!"
}
},
{
"name": "https://github.com/younjinjeong/spring-cloud-event-sourcing-config/application.yml",
"source": {
"info.id": "${spring.application.name}",
"logging.level.org.springframework.security": "DEBUG"
}
}
]
}

확인해보면, Config server는 변경된 설정을 즉시 참조하고 있음을 알 수 있다. 하지만 이 상태에서 이전에 시작한 simple-client 애플리케이션에 요청해보면 아직 이전의 값이 그대로 나오는 것을 확인할 수 있는데, 이는 디자인 상으로 그렇게 만들어진 것이므로 바뀌지 않았다고 걱정하지 말자.

 

애플리케이션에 message 설정의 변경된 값의 반영은 아래와 같이 수행할 수 있다.

curl -d '{}' http://localhost:9988/refresh
[“message"]

변경된 설정이 있다면 위와 같이 응답될 것이며, 이미 설정이 변경됐다면 [ ]와 같이 응답 할 것이다. 그리고 클라이언트 애플리케이션의 로그를 확인해보면 Config server로부터 설정을 새롭게 업데이트했음을 알리는 로그를 찾아볼 수 있다.

2017-04-18 03:54:08.577 INFO 12676 --- [nio-9988-exec-4] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@3f40fc91: startup date [Tue Apr 18 03:54:08 KST 2017]; root of context hierarchy
2017-04-18 03:54:08.591 INFO 12676 --- [nio-9988-exec-4] trationDelegate$BeanPostProcessorChecker : Bean 'configurationPropertiesRebinderAutoConfiguration' of type [org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration$$ EnhancerBySpringCGLIB$$882b350f] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2017-04-18 03:54:08.643 INFO 12676 --- [nio-9988-exec-4] c.c.c.ConfigServicePropertySourceLocator : Fetching config from server at: http://localhost:8888/
2017-04-18 03:54:09.972 INFO 12676 --- [nio-9988-exec-4] c.c.c.ConfigServicePropertySourceLocator : Located environment: name=simple-client, profiles=[default], label=null, version=null, state=null

이제 다시 curl을 사용해 클라이언트 애플리케이션에 요청해보도록 하자.

$ curl http://localhost:9988/msg
hello world!

이와 같은 동작은 동일한 애플리케이션 여러 개가 동작하는 클라우드 환경에서 각 애플리케이션이 동작하는 컨테이너나 가상머신별로 처리하는 것은 매우 비효율적인 일이다. 따라서 전술한 바와 같이 rabbitMQ와 같은 메시지큐를 사용해 동일한 애플리케이션에 대해 모두 적용하는 것도 가능하다.

 

코드와 설정의 분리는 매우 중요한 토픽이다. 설정 관리에 필요한 추가 업무를 줄이는 동시에, 코드가 특정 환경에 종속되는 문제를 해결한다. 이것은 특정 버전의 코드가 새로운 빌드 없이 동일한 설정만 있다면 다른 환경에서도 동일하게 동작할 수 있는 방법을 제공하므로, 가상서버나 컨테이너가 동적으로 생성되고 사라지는 클라우드에서 더욱 유용할 수 있다.

비단 클라우드뿐만 아니라, 애플리케이션 수준에서 데이터센터와 클라우드 간의 애플리케이션 마이그레이션 역시 가능할 수 있다. 이런 방법이 스프링 부트와 스프링 클라우드, 그리고 JVM이 설치된 환경만 있다면 동작할 수 있는 것인 만큼, 스프링을 알고 있는 개발자라면, 그리고 클라우드 환경을 고려중인 사업장이라면 반드시 살펴봐야 할 기술이라고 생각한다.

다음 회에는 스프링 클라우드의 유레카를 사용한 서비스 디스커버리에 대해 알아보도록 하자.

저작권자 © 아이티데일리 무단전재 및 재배포 금지