[MSA] 마이크로서비스 코딩 공작소 - 5장

서정범·2024년 1월 22일
0

MSA

목록 보기
7/10

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

많은 개발자가 프로퍼티 파일(YAML, JSON, XML)을 사용해서 구성 정보를 저장합니다. 이러한 파일 안에 애플리케이션 구성을 설정하는 것은 간단한 작업이므로 대부분의 개발자는 구성 파일을 소스 제어(있다면)에 추가하고 애플리케이션의 한 부분으로 배포하는 것 이상을 하지 않습니다.

하지만, 만약 여러 서비스가 존재하고 여러 서버에서 동작하는 상황이라면 어떻게 될까요? 수백 개의 마이크로서비스가 많은 인스턴스를 실행하고 있는 클라우드 기반의 애플리케이션을 처리하고 있다면 어떻게 될까요?

세 가지 환경(개발, 테스트, 배포)에 대해 서로 다른 환경 구성이 포함되어 있고 이것이 외부에서 관리되지 않는다면 변경 사항이 있을 때마다 코드 저장소에서 파일을 검색하고, 코드 저장소에서 검색해서 통합 과정(있다면)을 따라 애플리케이션을 재시작해야 합니다.

클라우드 기반 마이크로서비스 개발을 위한 모범 사례로 다음 사항들을 고려해야 합니다.

  • 배포되는 실제 코드와 구성 정보를 완전히 분리한다.
  • 여러 환경에서도 절대 변경되지 않는 불변(immutable) 애플리케이션 이미지를 빌드한다.
  • 서버가 시작할 때 마이크로서비스가 읽어 오는 환경 변수 또는 중앙 저장소를 통해 모든 애플리케이션 구성 정보를 주입한다.

5.1 구성(그리고 복잡성) 관리

네 가지 원칙을 확인해 봅시다.

  • 분리(segregate): 서비스의 물리적 배포에서 서비스 구성 정보를 완전히 분리해야 한다. 중앙 저장소를 이용하자.
  • 추상화(abstract): 서비스 인터페이스 뒷단에 있는 구성 데이터의 접근 방식을 추상화해야 한다. REST 기반 JSON 서비스를 사용하자.
  • 중앙 집중화(centralize): 가능한 적은 수의 저장소로 애플리케이션 구성 정보를 모아야 합니다.
  • 견고화(harden): 분리와 중앙 집중화를 통해 구현할 솔루션은 가용성이 높고 이중화가 필요합니다.

이러한 작업을 적용할 때 명심해야 할 점은 구성 정보를 실제 코드 외부로 분리하기 때문에 외부 의존성이 생깁니다. 이를 관리하고 버전 제어를 해야합니다.

다음은 마이크로서비스의 수명 주기를 보여줍니다.

아래 그림은 CI/CD를 통해 구성 정보들이 어떻게 업데이트 되는지 보여줍니다.

방식을 정리하자면 다음과 같습니다.

  1. 마이크로서비스 인스턴스가 시작되면, 서비스 엔드포인트를 호출하여 동작 중인 환경별 구성 정보를 읽어 옵니다. 구성 관리 서비스에 대한 접속 정보(접속 자격 증명, 서비스 엔드포인트 등)는 마이크로서비스가 시작될 때 전달됩니다.
  2. 실제 구성 정보는 저장소에 보관됩니다. 구성 저장소 구현체에 따라 구성 데이터를 보관하는 다양한 방법을 선택할 수 있습니다. 예를 들어 소스 제어되는 파일, 관계형 데이터베이스, 키-값 데이터 저장소 같은 방법이 있습니다.
  3. 애플리케이션 구성 데이터의 실제 관리는 응용 프로그램이 배포되는 방식과는 독립적으로 합니다. 구성 관리에 대한 변경 사항은 일반적으로 빌드 및 배포 파이프라인으로 처리되며, 여기서 수정 사항에 대한 버전 정보는 태그를 달아 여러 환경(개발, 스테이징, 운영 환경 등)에 배포할 수 있습니다.
  4. 관리하는 구성 정보가 변경되면 애플리케이션 구성 데이터를 사용하는 서비스는 변경 사항을 통지받고 애플리케이션 데이터 복제본을 갱신해야 합니다.

책에 있는 내용을 그대로 끌어다가 적었는데, 중요한 내용이라 적어놨습니다.

간단하게 정리하자면,

  1. 인스턴스가 처음 동작할 때, 중앙 저장소로부터 구성 정보를 받아오면서 서비스가 시작됩니다.
  2. 구성 정보는 다양하게 저장되어 있으며 본인(마이크로서비스)에게 필요한 구성 정보들을 가져옵니다.
  3. 이후 변경할 사항이 있다면, 중앙 저장소에 반영하도록 파이프라인을 구축해놓습니다.
  4. 변경 사항이 반영 되었다면, 해당 변경 사항에 적용되는 서비스들에게 메시지가 전달되며, 메시지를 받은 서비스들은 중앙 저장소로부터 다시 읽어옵니다.

해당 방식은 전형적인 CI/CD 배포의 일부분을 보여줍니다.

다양한 구현 솔루션을 제시하고 있는데 다음과 같은 솔루션들이 있다고 합니다.

  • etcd
  • 유레카(Eureka)
  • 콘술(Consul)
  • 주키퍼(Zookeeper)
  • 스프링 클라우드 구성 서버(Spring Cloud Configuration Server)

여기서는 스프링 클라우드 구성 서버를 선택하고 그에 대한 장점들을 나열하고 있습니다.

  • 설치하기 쉽고, 사용하기 쉽다.
  • 스프링 부트와 밀접하게 통합되어 있다.
  • 구성 데이터를 저장하는 많은 백엔드를 지원한다.
  • 깃 소스 제어 플랫폼이나 하시코프 볼트와 바로 통합할 수 있다.

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

스프링 클라우드 컨피그(Spring Cloud Config) 서버는 스프링 부트로 만든 REST 기반의 애플리케이션입니다.

해당 책에서는 코드와 함께 설명들을 제시하고 있는데, 해당 부분은 생략하고 필요한 개념들만 정리를 하도록 하겠습니다.

우선, 스프링 클라우드 컨피그 서버를 설정하기 위해서 xml 파일을 작성할텐데 중요한 것들이 몇가지 있습니다.

  1. 스프링 부트 버전
  2. 스프링 클라우드 버전
  3. 서비스에 사용되는 의존성
  4. 스프링 클라우드 컨피그의 상위 BOM(Bill Of Materials)

1, 2, 3번의 경우 사실 어느 백엔드 서버에서든 중요한 것이고 4번의 무엇인지 살펴봅시다.

BOM은 프로젝트에서 사용되는 의존성들의 버전을 관리하는데 도움을 줍니다.

  1. BOM의 역할: BOM은 여러 라이브러리들과 그들의 호환되는 버전을 정의합니다. 의존성들 간의 버전 충돌을 줄이고, 호환성을 유지하는데 도움을 줍니다.
  2. 의존성 관리: 프로젝트에 BOM을 포함시키면, 개별 라이브러리들의 버전을 일일이 지정할 필요가 없이, BOM에서 정의된 버전에 따라 의존성이 관리됩니다. 이는 프로젝트의 의존성 관리를 간소화하고, 일관성을 유지하는데 유용합니다.
  3. 스프링 클라우드 컨피그 서버에서 사용: 스프링 클라우드 컨피그 서버에서 BOM을 사용할 경우, 이는 스프링 클라우드와 관련된 여러 의존성들의 호환되는 버전을 정의합니다. 이를 통해 컨피그 서버와 클라이언트 애플리케이션 간의 호환성 문제를 최소화할 수 있습니다.

추가적으로 부트스트랩(bootstrap) 파일에 대한 설명이 나오는데, 보통 부트라는 단어가 붙은 파일이나 장치들은 맨 처음 실행되는 것으로 해당 파일에서도 그에 맞게 필요한 정보들을 담고 있습니다.

  • 스프링 애플리케이션 이름
  • 스프링 클라우드 구성 서버의 위치
  • 암호화/복호화 정보
  • 등등

bootstrap.yml 파일을 사용하여 컨피그 서버와 마이크로서비스의 구성 정보를 정의합니다.

해당 파일에 저장되는 중요한 정보들은 다음과 같습니다.

  • 수신 대기(listen)할 포트: 서비스 서버로부터 요청이 들어올 때 사용하는 포트입니다.
  • 애플리케이션 이름: 서비스 디스커버리(Discovery)를 위해 필요
  • 애플리케이션 프로파일과 구성 데이터를 저장할 위치 지정

컨피그 서버는 스프링 부트 애플리케이션이고, 해당 애플리케이션은 가동되기 위해서는 진입점이 필요합니다. 이것을 부트클래스로 보면 되고, 다음과 같은 것들이 들어가는 클래스들을 의미합니다.

  • main()
  • @SpringBootApplication
  • @EnableConfigServer

여기서는 복잡한 외부 저장소를 사용하지 않고, 로컬 파일 시스템을 이용해서 설명을 하고 있습니다. 로컬 파일 시스템을 이용하여 파일에 접근할 때는 classpath:file:를 이용하여 접근하는 native 프로파일을 사용합니다.

이제 구성 파일 설정에 대해서 설명을 해보자.

앞서 언급했듯이 컨피그 서버는 여러 환경의 구성을 가지고 있을 수 있습니다.

여기에 추가로 스프링 클라우드 컨피그는 모든 것이 계층 구조로 동작합니다.

여기서 말하는 계층 구조는 엄청 거창한 계층 구조라기 보다는 여러 환경에서 공통적으로 적용되는 것들은 모두 적용되도록 하나의 파일에서 정의를 내리면서 추가적으로 변경사항이 생길 때 나눠지는 형태로 일종의 트리 형태로 쪼개지는 계층 구조를 생각하면 됩니다.

이러한 계층 구조는 프로퍼티 파일을 이용하여 정의를 할 수 있습니다.

추가적으로, 마이크로서비스의 경우 도커 컨테이너 위에서 동작합니다. 따라서, 도커 컴포즈에서 정의한 환경 변수를 이용해서 해당 작업을 수행할 수 있다는 점을 기억하면 됩니다.

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

해당 단원의 앞 절에서는 초기 코드 구축에 대한 설명이 나옵니다. JPA를 사용해서 Repository를 구축하고 이것을 활용하는 코드에 대해서 제시하고 있습니다.

해당 부분은 넘어가겠습니다.

여기서 봐야되는 것은 스프링 부트 애플리케이션에서 지속적으로 구성을 관리하는 방법입니다.

일반적으로 스프링 부트 애플리케이션은 시작할 때만 프로퍼티를 읽기 때문에 컨피그 서버에서 변경된 프로퍼티가 자동으로 애플리케이션에 적용되지는 않습니다.

이것을 위해서 스프링 부트 액추에이터(Spring Boot Actuator)@refreshScope 어노테이션을 사용하여 스프링 애플리케이션이 구성 정보를 다시 읽게 만드는 /refresh 엔드포인트에 접근할 수 있게 해줍니다.

스프링 부트 액추에이터는 상태 확인을 위해 마이크로서비스 아키텍처에서 자주 사용되는 것으로 Health Check도 이 라이브러리를 통해서 수행됩니다.

❗️마이크로서비스 갱신

컨비그 서비스를 사용하여 동적으로 프로퍼티를 변경할 때 주의할 점은 동일한 서비스에 대한 여러 인스턴스가 실행 중일 수 있다는 것입니다.
스프링 클라우드 컨피그 서비스는 변경이 발생할 때 이 서비스를 사용하는 모든 클라이언트에 게시(publish)할 수 있는 스프링 클라우드 버스(Spring Cloud Bus)라는 푸시(push)기반의 매커니즘을 제공합니다.
이것은 별도 미들웨어인 RabbitMQ가 필요합니다.
이 방법은 변경을 감지하는 매우 유용한 수단이지만 모든 스프링 클라우드 컨피그 백엔드가 이 푸시 매커니즘을 지원하는 것은 아닙니다.
따라서, 이 문제를 해결하기 위해서 스프링 클라우드 서비스 디스커버리와 유레카(Eureka)를 사용하여 서비스의 모든 인스턴스를 등록하여 문제를 해결합니다.

마지막으로, 프로퍼티를 외부 저장소인 깃 혹은 볼트에 저장하여 사용하는 방법들이 제시되고 있습니다.

여러 방식을 혼합해서 사용 가능하고 외부 저장소를 이용할 경우에는 프로퍼티에 저장되는 중요 정보들이 노출되지 않도록 암호화/복호화 작업을 수행해야 한다는 점입니다.

외부 저장소에 저장을 할 때는 암호화 작업을 수행하고, 컨피그 서버는 이를 복호화하여 안전하게 클라이언트에게 전달할 수 있습니다.

참고한 자료

  • 스프링 마이크로서비스 코딩 공작소 개정 2판
profile
개발정리블로그

0개의 댓글