12 Factor App 원칙에서는...
애플리케이션의 환경설정 정보를 코드로부터 분리하여 외부화하는 것을 그 원칙 중 하나로 제시하고 있다.
이는 환경에 따라 달라지는 설정 정보를 소스로부터 분리하여 둘 간의 결합도를 낮추어 유지보수성을 좋게하기 위한 설계 원칙이다.
이런 관점에서, 가장 최악의 선택은 설정정보를 소스코드의 상수로 관리하는 것이다.
요즘 왠만한 프로젝트에서는 상수기반 설정정보를 잘 사용하지는 않는다.
* 설정정보를 소스코드에서 분리시키기
설정 정보를 별도의 소스코드와 분리된 별도의 파일로 관리하는 것이 일반적이다.
스프링 부트 기반으로 볼 때,
application.properties 또는 application.yml 파일에 설정정보를 관리한다.
단, 이 방식도 (설정파일이) 프로젝트에 포함된 경우라면, 설정정보의 변경이 필요할 경우, 애플리케이션 전체를 다시 빌드하고 배포해야 하는 종속성 문제가 생긴다.
(설정정보를 프로파일로 구분할 수 있는 것도 하나의 해결책이긴 하지만, 이는 프로파일의 종류가 정해진 경우에는 유용하나, 동일한 프로파일내에서의 변경사항은 여전히 종속성 문제를 내포하고 있다)
* 설정정보를 배포 패키지에서 분리시키기
설정정보 파일을 애플리케이션 배포 패키지에서 분리시키기 위한 방법이 몇 가지 존재하긴 한다.
자바 기반 프로젝트로 보면,
1) 애플리케이션 실행 시, 설정정보 파일 위치를 옵션에 명시
2) @PropertySource 어노테이션으로 설정정보 파일 위치 명시
3) 시스템 자체의 환경설정 정보를 읽어오는 System.getProperties() 사용하기
이 중 1)번 방식을 선호하기는 하지만, 이 또한 설정정보 변경이 발생하면 애플리케이셔을 중지했다가 다시 구동해야 하는 종속성 문제가 발싱한다.
* 설정정보를 원격 서버에서 제공하기
Spring Cloud 프로젝트에서 제공하는 Spring Cloud Config 서버를 활용하면 원격지 서버에서 설정정보를 서비스 할 수 있게 해준다.
그리고 설정정보를 동적으로 변경하여 (애플리케이션의 재빌드나, 재구동 없이) 실시간으로 반영시킬 수 있도록 하는 메커니즘을 제공하며 git이나 svn 기반으로 설정정보의 버전 관리를 지원해 준다.
* Spring Cloud Config 서버-클라이언트 구현
1. git 저장소와 설정파일 생성
1) git 저장소 생성
여기서는 설정정보 저장소로 로컬 git을 사용하기로 한다. git 을 설치하고 git 저장소를 init 명령어로 지정한다.
> git init
2) 설정파일 생성
다음의 규칙으로 설정파일 이름을 지정하고 설정정보를 파일에 입력한다.
{applicationName}-{profile}.yml 또는 {applicationName}-{profile}.properties
3) 설정파일 커밋
생성한 설정파일을 git에 add하고 commit 한다.
> git status
> git add -A
> git commit -m "config file create"
4) Config 서버의 설정정보 확인
Config 서버에서 설정정보를 확인하기 위해서는 다음과 같은 규칙으로 접속한다.
http://{ConfigServer}/{applicationName}/{profile}
아래는 프로파일이 없는 경우와 dev 프로파일에 접속하는 예를 보여준다
> http://localhost:8888/my-service/default
> http://localhost:8888/my-service/dev
2. Spring Cloud Config 서버 구현
1) Config 서버 프로젝트 생성
스프링 부트 프로젝트로 Config Server와 Actuator을 의존성으로 선택하고 생성한다.
(스프링 부트 2.0은 Spring Cloud Finchley.M9 버전을 사용한다)
2) Config 서버 설정
bootstrap.yml을 resource 폴더에 생성하고 다음과 같이 설정한다.
설정파일이 관리될 로컬 git 저장소를 지정하고, Config 클라이언트를 구분할 목적으로 서브디렉터리를 만들고 searchPaths로 서브디렉터리를 지정해 준다.
spring:
application:
name: config-service
cloud:
config:
server:
git:
uri: file://{로컬 git 저장소 (루트) 경로}
searchPaths: subdirectory1, subdirectory2, ..., subdirectory{N}
server:
port: 8888 #Config Server Default Port
2. Spring Cloud Config 클라이언트 구현
1) Config 클라이언트 프로젝트 생성
스프링 부트 프로젝트로 Config Client를 의존성으로 선택하고 생성한다.
(스프링 부트 2.0은 Spring Cloud Finchley.M9 버전을 사용한다)
2) Config 클라이언트 설정
bootstrap.yml을 resource 폴더에 생성하고 다음과 같이 설정한다.
spring:
profiles:
active: dev
application:
name: sc-api-user-service
cloud:
config:
uri: http://localhost:8888 #Config 서버 uri
fail-fast: false #Config Server와 연결이 되지 않으면 예외를 발생시키고 종료하려면 true
3) 설정 정보 사용하기
@Value 어노테이션을 사용하거나 @ConfigurationProperties 어노테이션을 사용하여 Config 서버로 부터 설정정보를 매핑하여 사용할 수 있다.
* Config 서버의 가용성
Config 서버가 1대 뿐이라면 SPOF(single point of failure, 단일장애지점)가 된다.
SPOF는 라이브환경에서는 언제나 재앙의 대상이 되곤 한다.
하지만 Config 서버의 구동방식을 보면 일반적인 SPOF에 비해 그 부담이 다소 적다.
1) 운영중 가용성
Config 서버와 연동하는 클라이언트는 최초 구동시에만 서버에서 설정값을 읽어온다.
이후 부터는 클라이언트 측에 로컬 캐시되어 더 이상 Config 서버와 통신하지 않는다.
다만, 설정정보가 갱신될 경우 클라이언트의 /actuator/refresh 엔드포인트를 사용해서 서버에서 새로운 값을 다시 읽어 오도록 하는데, 이때도 서버가 응답이 없다면 기존 로컬 캐시값을 사용한다
즉 Config 서버가 다운되었다고 하더라도 클라이언트들은 다운되거나 하지 않는다는 말이다.
(단 서버의 변경 값을 동기화하지 못할 경우, 일관성 문제는 생길 수 있다)
2) 최초 구동시 가용성
그렇다면, 클라이언트가 최초 구동될 때 Config 서버가 다운된 상황이라면 어떨까?
이때의 가용성을 위해서 fail-fast 값을 false(기본값)로 준 것이다.
fail-fast 값이 true로 설정되면, Config 서버와 연결하지 못하면 클라이언트는 구동되지 않게 된다.
어떤 선택이 더 나은지는 환경에 따라 다를 것이다.
만일 Config 서버가 이중화되어 있지 않은 상태에서 최소한의 가용성을 확보하기 위해서는 이 값을 false로 하는 것을 생각해 볼 수 있다.
즉 Config 서버가 다운된 상황에서도 클라이언트 프로그램은 구동되도록 하는 것이다.
물론 이렇게 하면 Config 서버로 부터 설정정보를 받아오지 못하는 문제가 생긴다.
이를 위해서 클라이언트에서는 동일한 설정정보를 자신의 설정파일에도 가지고 있으면 된다
이렇게 되면 Config 서버와 연결이 실패한 클라이언트는 동일한 설정을 자신의 설정파일로 부터 읽어 들이게 된다.
이것은 그야말로 최소한의 가용성 확보를 위한 전략이 될 것이다.
3) 보다 나은 가용성
Config 서버자체를 이중화하는 것이다.
로드밸런서를 두고 Config 서버를 이중화하면 장비 차원에서 가용성 확보가 된다.
이때 생각해 봐야 할 문제는 git 저장소의 동기화와 가용성 문제이다.
Config 서버를 이중화 할 경우에는,
로컬 git 저장소를 사용하지 말고 원격의 중앙 git 저장소를 사용하도록 한다.
원격 git 저장소의 고가용성을 위해서는 다음 글을 참고 하자
https://about.gitlab.com/high-availability/
* 설정 정보의 실시간 동기화
Config 서버의 설정정보가 변경되면 이를 의존 하고 있는 클라이언트 프로그램들에게 알려줘야 하낟.
이때 사용되는 것이 @RefreshScope 어노테이션과 /actuator/refresh 엔드포인트이다.
이 두 작업은 클라이언트 측에서 수행되어야 한다.
실시간 동기화가 필요한 곳, 즉 설정정보를 엑세스 하는 클래스에 @RefreshScope 어노테이션을 붙여 준다.
그리고 클라이언트의 주소에 /actuator/refresh 엔트포인트로 POST (Body는 빈 값으로) 전송을 하면된다.
이렇게 하면 클라이언트 로컬에 캐시된 설정 정보를 Config 서버에서 가져온 값으로 즉시 갱신한다.
그러나 Config 서버에 의존하는 클라이언트가 상당히 많은 수일 경우, 모든 클라이언트의 /actuator/refresh 엔트포인트를 명시적으로 호출해 줘야 하기 때문에 유지보수가 까다로워 진다.
Spring Cloud Bus를 사용하면 메시지 브로커를 통해 변경 이벤트를 구독하여 자동으로 모든 클라이언트에 변경 정보를 자동으로 동기화 할 수 있다.
-------------------------------------------
오랜만에 글을 쓸려니... 느무~~ 귀찮으니무다... 아...
글 쓰기 싫어서 큰일이다. 쩝...
'SW개발' 카테고리의 다른 글
[웹보안] 브루트 포스(BRUTE FORCE) 공격 (6) | 2019.01.23 |
---|---|
[웹보안] 로컬 환경 셋팅과 툴 설치 (5) | 2019.01.17 |
SPA와 Social sharing(소셜 공유) (10) | 2017.06.26 |
좋은 소프트웨어를 위한 기본기 (6) | 2016.12.14 |
UI 디자인트렌드 (6) | 2016.12.13 |