만쥬의 개발일기

챕터 4: 외부화 설정 관리

애플리케이션을 여러 환경에 배포한다면 톰캣 설정을 어떻게 해주어야할까?

SaaS의 개발 방법론인 12-factors의 config에 관한 내용은 다음과 같습니다.

3. Config

모든 설정 정보는 코드로부터 분리된 공간에 저장되어야 하고, 런타임에서 코드에 의해 읽혀야 한다. SaaS는 동일한 코드를 여러 환경(운영/개발)에 배포한다. 이를 위해 환경마다 달리 사용되어야 하는 정보를 분리한다.

분리되어야 할 정보

  • 데이터베이스나 다른 백업 서비스를 처리하는 리소스
  • 외부 리소스(S3, Twitter 등)의 인증 정보
  • 각 배포마다 달라지는 값(cononical hostname..)
  • dev,test,stage,prod의 배포 단계마다 다를 수 있는 어떤 값들

설정정보를 저장하면 안되는 곳

  • code
  • properties file
  • build : one build, many deploy니까
  • app server(jndi database 같은 정보)

Config 항목 준수 방법

  • 배포 환경(개발/운영)용 설정파일을 작성
  • Spring Cloud Config 사용

그리고 클라우드 네이티브 애플리케이션의 핵심은 환경이 달라져도, 애플리케이션 빌드가 변경되지 않는 것입니다.

이 말을 다시 말하면 위 그림처럼 배포할 때 변경되는 것은 설정이 가능해야 합니다.

스프링 설정: 속성과 프로파일

앞으로 사용할 설정이라는 용어는 배포 사이 변경될 가능성이 있는 모든 것을 의미합니다.

스프링에서는 Environment라는 추상화 기능을 통해 설정 소스에 상관없이 모든 설정 데이터에 액세스할 수 있습니다.

그리고 스프링 애플리케이션 환경의 두 가지 주요 측면은 property와 profile 입니다.

속성은 특별한 제약 없이 사용하며, 컴파일된 자바 코드 외부에 설정 매개변수를 저장할 때 속성은 필수적인 역할을 합니다.

동일한 속성이 여러 소스에 정의될 경우, 속성간 우선순위가 존재합니다.
예를 들어 속성파일과 커맨드라인 인수 모두 server.port 속성에 대한 값을 지정한다면, 커맨드라인이 우선됩니다.

다음은 그 우선순위 입니다.

  1. 테스트 클래스의 @TestPropertySource 애너테이션
  2. 커맨드라인 인수
  3. System.getProperties() 메서드를 사용한 JVM 시스템 속성
  4. System.getenv() 메서드를 사용한 OS 환경 변수
  5. 설정 데이터 파일
  6. @Configuration 클래스의 @PropertySource 애너테이션
  7. SpringApplication.setDefaultProperties으로 기본 설정된 속성

자바 클래스에서 속성에 액세스하는 방법은 다음과 같이 여러 가지 방법이 존재합니다.

*세 번째 방법인 @ConfigurationProperties로 config 클래스를 정의했다면, application.yml파일에서 기본값을 정의할 수 있다.

또한 해당 클래스 혹은 레코드는 표준 스프링 빈이기 때문에 생성자 오토와이어링을 통해 어디든 주입해 사용 가능하다.

프로파일: 기능 플래그와 설정 그룹

dev 또는 prod 같은 프로파일을 사용해 빈을 조건부로 로드하는 것은 클라우드 네이티브 애플리케이션에서는 바람직하지 않습니다.
애플리케이션을 개발, 테스트, 프로덕션에 배포하고 조건부로 특정 빈을 로드하기 위해 세 가지 프로파일을 정의하는 경우를 고려해보겠습니다.

스프링 프레임워크의 프로파일 기능을 사용하면 특정 프로파일이 활성화된 경우에만 해당 빈을 로드할 수도 있습니다.
application.yml 대신 application-dev.yml 이라는 파일을 만들고, config 클래스에서 속성갑을 정의하면 dev 프로파일이 활성화될 때만 스프링 부트가 이 파일의 값을 사용하고, 프로파일별 속성 파일은 단순 속성 파일에 우선하므로 application.yml의 값보다 우선됩니다.

외부화된 구성: 하나의 빌드, 여러 설정

스프링 부트는 설정을 실행 환경에 저장하기 위해서 몇 가지 방법을 제공하는데, 애플리케이션이 어디 배포되는지에 따라 우선순위가 높은 속성 소스 중 하나를 사용해 기본 설정 값을 재지정할 수 있습니다.

그리고 우선순위 규칙은 다음과 같습니다.

예를 들어 CLI 인수값이 주어지면 키-값 쌍으로 변환하여 Environment 객체에 저장하고, 어떤 속성 파일보다 우선됩니다.

경험 상 위 내용 중 실제로 주로 사용하는 것은 CLI 인수와 속성 파일인 것 같습니다.
혹은 쿠버네티스를 함께 사용한다면 환경 변수도 빈번히 사용하게 됩니다.

스프링 클라우드 컨피그 서버로 중앙식 설정 관리하기

환경 변수를 사용하면 애플리케이션의 설정을 외부화할 수 있지만, 몇 가지 처리할 수 없는 문제가 있습니다.

  • 설정 데이터는 애플리케이션 코드만큼 중요하므로 지속성부터 시작해 애플리케이션 코드와 동
    일한 관리와 주의를 가지고 처리해야 한다. 설정 데이터를 어디에 저장해야 할까?
  • 환경 변수에 대해서는 세부적인 액세스 제어를 할 수 없다 설정 데이터에 대한 액세스를 어떻
    게 제어할 수 있을까?
  • 설정 데이터는 애플리케이션 코드와 마찬가지로 진화하고 변경한다 어떻게 설정 데이터의 수
    정을 추적하고 릴리스에 사용된 설정을 감사해야 할까?
  • 설정 데이터를 변경한 후, 어떻게 애플리케이션을 다시 시작하지 않고도 런타임에 읽을 수 있
    도록 할수 있을까?
  • 애플리케이션 인스턴스 수가 증가하면 각 인스턴스에 대해 분산된 방식으로 설정을 처리하는
    것이 어려워질 수 있다. 이 상황을 어떻게 극복할 수 있을까?
  • 스프링 부트의 속성이나 환경 변수는 설정 암호화를 지원하지 않기 때문에 암호를 안전하게
    저장할 수 없다 시크릿을 어떻게 관리해야 할까?

위 문제들을 해결하기 위해서 다음 방법들을 사용할 수 있습니다.
선택은 인프라 요구사항에 따라 해줄 수 있습니다.

  • 설정 서비스: 자신만의 설정 서비스를 실행하고 설정 모듈을 스프링 클라우드 프로젝트가 제공
    • 스프링 클라우드 컨피그: 깃 저장소, 데이터 저장소와 같이 데이터 소스에 의해 지원되는 설정 서비스
    • 스프링 클라우드 주키퍼: 아파치 주키퍼를 저장소로 사용하는 설정 서비스
    • 스프링 클라우드 볼트 : 하시코프 볼트를 데이터 저장소로 사용하는 설정 서비스
  • 클라우드 공급업체 서비스
    • 스프링 클라우드 AWS,애저,GCP 등
  • 클라우드 플랫폼 서비스
    • 쿠버네티스 플랫폼에서 컨피그맵 또는 시크릿 사용

중앙 집중식 설정에 대한 아키텍처는 다음과 같습니다.


위 아키텍처를 보면 설정 서버는 중앙의 한 장소에서 모든 설정 데이터를 관리하지만, 설정 데이터는 다른 방식으로도 저장 가능합니다.

ex) 민감하지 않은 데이터: 깃 저장소 , 민감한 데이터: 하시코프 볼트

위 아키텍처의 고 가용성 문제는 설정 서버를 클러스터링해서 해결할 수 있다.

깃을 통한 설정 데이터 저장

혹은 깃 저장소를 통해 설정 데이터를 저장할 수 있습니다.
config-repo라는 이름으로 저장소를 만들고, 해당 저장소 내에 .properties 또는 .yml 파일에 스프링 형식으로 속성을 직접 저장 가능합니다.

설정 서버 구성

스프링 클라우드 컨피그 서버 프로젝트를 사용해 설정 서버 구성이 가능합니다.


아까 본 아키텍처를 조금 더 자세하게 나타낸 그림입니다.
서비스 애플리케이션은 REST API로 컨피그 서버로 부터 설정을 요청하고, 컨피그 서버는 컨피그 저장소에서 REST API로 데이터를 읽습니다.

다음과 같이 config server 디펜던시를 추가하여 서버를 생성합니다.

이제 메인 클래스에 @EnableConfigServer 애너테이션만 추가해도, 설정 서버로 사용이 가능한 상태가 됩니다.

그리고 저장소의 위치는 application.yml에서 설정해줍니다.

server:
  port: 8888 ・
  tomcat: 컨피그 서비스 애플리케이션이 듣는 포트
    connection-timeout: 2s
    keep-alive-timeout: 15s
    threads:
      max: 50
      min-spare: 5

spring:
  application: 현재 애플리케이션의 이름
    name: config-service <
  cloud:
    config:
      server:
        git:
          uri: <your-config-repo-github-url> 
          default-label: main  # 브랜치

위와 같이 설정하고, 데이터 저장 백엔드로 사용할 깃 저장소 url을 cloud.config.server.git.url 에 저장합니다.

동작 방식

스프링 클라우드 컨피그는 원격 저장소에서 설정 데이터를 최초로 가져온 후, 로컬에 복제합니다.
로컬 저장소와 원격 저장소의 내용이 달라질 수 있는 부분도 설정을 통해 항상 동일한 데이터를 가지도록 할 수 있씁니다.

spring:
  application:
    name: config-service
  cloud:
    config:
      server:
        git:
          uri: <your-config-repo-github-url>
          default-label: main
          timeout: 5 <- 원격 저장소와의 연결시 시간 한도
          clone-on-start: true <- 서비스 시작시 리모트 저장소의 복제본을 로컬에 저장
          force-pull: true <- 항상 동일한 상태 유지

스프링 클라우드 컨피그 클라이언트로 설정 서버 사용

애플리케이션이 컨피그 서버와 견고하게 통신하기 위해선 스프링 클라우드 컨피그 클라이언트가 제공됩니다.

우선 스프링 클라우드 컨피그 클라이언트에 대한 의존성을 추가해줍니다.
그리고 application.yml에서 config service 서버 관련 설정을 추가해줍니다.

스프링 클라우드 컨피그는 실행 시간에도 클라이언트 애플리케이션에서 설정 업데이트가 가능합니다.
따라서 애플리케이션을 재시작하지 않고도 속성을 변경 및 반영이 가능합니다.


위 아키텍처와 같이 관리 프로세스가 동작합니다.

결론

  • 스프링의 Environment 추상화는 속성 및 프로파일에 액세스하기 위한 통합 인터페이스를 제공한다
  • 속성은 설정을 저장하기 위해 사용되는 키/값 쌍이다.
  • 스프링 부트는 우선순위 규칙에 따라 여러 소스에서 속성을 수집한다. 커맨드라인 인수, JVM 시스템 변수, OS 환경 변수, 프로파일별 속성 파일 및 일반 속성 파일을 통해 속성을 정의할 수 있다
  • 스프링 빈은 @Value 애너테이션으로 값을 주입하거나 @ConfigurationProperties 애너테이션으로 속성 세트에 매핑된 빈을 통해 Environment 객체의 속성에 액세스할 수 있다
  • @Profile 애너테이션은 지정된 프로파일이 활성화된 경우에만 빈 또는 설정 클래스가 고려되도록 표시한다.
  • 설정 서버를 통해 애플리케이션을 다시 시작하지 않고도 런타임에 비밀 암호화, 설정 추적성, 버전 관리 및 콘텍스트 새로고침과 같은 측면을 처리한다
  • 스프링 부트 애플리케이션은 스프링 클라우드 컨피그 클라이언트 라이브러리를 사용해 설정 서버에서 설정 데이터를 받아 설정할 수 있다
profile

만쥬의 개발일기

@KangManJoo

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!