Spring Cloud + MSA 애플리케이션 개발 7(Configuration Service)

지원·2024년 2월 23일
0

MSA개발

목록 보기
7/15

지금까지 E-commerce 에 필요한 서비스들을 만들어봤다.

지금부터 각각의 서비스의 application.yml 파일에 내용이 변경될 때 애플리케이션 자체를 빌드 , 배포를 해야하는 단점을 해결해보자.

  • 이러한 문제를 해결하기 위해서는 애플리케이션 내에서 구성 파일을 가지고 있는게 아니라 외부의 시스템으로 구성파일을 관리해야한다.
  • Local Git Repository , Remote Git Repository , Native File Repository
  • 추가로 Spring Boot Actuator , Profiles 도 적용해볼 예정

Spring Cloud Config

  • 분산 시스템에서 서버 , 클라이언트 구성에 필요한 설정 정보(application.yml)를 외부 시스템에서 관리
  • 하나의 중앙화 된 저장소에서 구성요소 관리 가능
  • 각 서비스를 다시 빌드 하지 않고, 바로 적용 가능
  • 애플리케이션 배포 파이프라인을 통해 DEV(개발)-UAT(테스트)-PROD 환경에 맞는 구성 정보 사용

private Git Repository 에서 Spring Cloud Config Server 를 거쳐서 각 서비스에 구성 정보를 전달하는 방식으로 변경해보자.

Local Git Repository

현재 token 에 관련된 정보가 user-service , api-gateway-service 2군데에서 관리가 되고 있다.

이것을 ecommerce.yml 파일 한 군데에서 관리할 수 있도록 단일화를 해보자.

  • local file 을 만들고 git init -> git add -> git commit 을 하면 된다.

그런후에 Spring Cloud Config Server 의 역할을 하는 프로젝트를 하나 더 생성한다.

  • 서버의 Main 함수가 있는 클래스 레벨에 @EnableConfigServer 애노테이션을 붙혀줘야 한다.
  • application.yml 파일에 port 번호와 위에서 만든 local file 경로들을 넣어주면 된다.

우선순위

  1. application.yml
  2. application-name.yml
  • ex) user-service.yml , order-service.yml
  1. application-name-< profile>.yml
  • ex) user-service-dev.yml , user-service-test.yml , user-service-prod.yml

Spring Cloud Config 생성

git-local-repo 파일을 만들고 Visual Studio Code 를 이용해 ecommerce.yml 를 만들고 여기에 token 에 해당하는 정보들을 넣었다.

  • 그런후에 git init -> git add -> git commit 명령어로 git 관리 대상으로 만든다.
  • Spring Cloud Config Server 의존성만 추가하고 config-server 라는 프로젝트를 만든다.
  • Main 함수가 있는 Class 레벨에 @EnableConfigServer 를 붙혀준다.
# config-server applicatino.yml
# config-server의 default port 번호가 8888
server:
  port: 8888
spring:
  application:
    name: config-service
  cloud:
    config:
      server:
        git:
          uri: file:///Users/supportkim/Desktop/git-local-repo
  • 아래처럼 설정해주고 실행하고 /ecomerce/default URI 로 접근하면 ecommerce.yml 에 넣은 정보들이 JSON 형태로 출력되는 것을 확인할 수 있다.

Users MicroService 에서 Spring Cloud Config 연동하기

Dependencies 추가

  • 연동하기 위해서는 2가지의 Dependency를 추가해야한다.
  • spring-cloud-starter-config
  • spring-cloud-starter-bootstrap
  • bootstrap.yml 추가

bootstrap.yml

  • bootstrap.yml 은 applicationl.yml 보다 우선순위가 높다.
  • 지금 현재 하려는 작업의 핵심은 applicationl.yml 파일의 일부분을 공용 서버 역할을 하는 config-server 를 통하도록 하는 것이다.
  • 그렇다면 그 일부분을 config-server 에 저장을 시켰는데 해당 정보는 applicationl.yml 파일 정보 보다 더 먼저 작업을 해야지만 전체적으로 우리가 필요한 리소스가 맞을 수 있다.
  • 그래서 config-server 정보를 먼저 등록할 수 있는 정보가 필요한데 그게 bootstrap.yml 에다가 그 정보를 등록하면 되는 것이다.
  • 해당 작업이 모두 끝난 후에 application.yml 에서 정보를 읽어온다.
  • bootstrap.yml 파일은 아래에 작성하겠지만 이러한 이유로 8888 port 를 가지는 URI 와 ecommerce 의 정보를 넣는 것이다.

Users Microservice 에 있는 /health_check 에 해당하는 메서드에서 yml 파일에 있는 각 정보들을 출력하는 로직를 만들어서 테스트도 해본다.

먼저 user-service 에서 application.yml 파일에 token 정보를 주석처리한다.

  • 그런후 위에서 말했듯이 의존성 2개를 pom.xml 에 추가한다.
# user-service bootstrap.yml
spring:
  cloud:
    config:
      uri: http://127.0.0.1:8888
      # name 에는 파일명 넣기(ecommerce.yml)
      name: ecommerce
  • user-service 에서 bootstrap.yml 을 만들고 위와 같이 코드를 작성한다.
    @GetMapping("/health_check")
    public String status() {
        return String.format("It's Working in User Service"
                + ", port(local.server.port)=" + env.getProperty("local.server.port")
                + ", port(server.port)=" + env.getProperty("server.port")
                + ", token secret=" + env.getProperty("token.secret")
                + ", token expiration time=" + env.getProperty("token.expiration_time"));
    }
  • Controller 코드를 수정하고 서버를 시작해보면 정상적으로 출력 되는 것을 확인할 수 있다.

token 정보는 yml 파일에서 주석처리를 했지만 config-server 는 git-local-repo 에 들어있는 ecommerce.yml 파일을 가지고 있고, user-service 는 bootstrap.yml 에 config-server 의 URI 와 yml 파일 이름을 넣어줬기 때문에 token 정보를 가져올 수 있다.

여기서 문제는 ecommerce.yml 파일의 구성 정보를 변경한 것을 반영하기 위해서는 3가지의 선택지가 주어진다.
1. 서버 재기동

  • 매번 서버를 재기동 하는것은 좋은 방법은 아니다.
  1. Actuator refresh
  2. Spring Cloud Bus 사용

먼저 2번의 방법을 사용하고 3번 방법은 다음 시간에 사용해본다.

Spring Boot Actuator

  • Application 상태 , 모니터링
  • Metric 수집을 위한 Http End point 제공
  • Spring Boot Actuator 의존성 추가
  • /actuator/** 접근할 수 있도록 permitAll() 하기 (WebSecurity.class)
# user-service application.yml
management:
  endpoints:
    web:
      exposure:
        include: refresh , health , beans
  • yml 파일을 위와 같이 작성하면 refresh , health , beans 의 기능을 사용할 수 있다.
  • ecommerce.yml 을 수정하고 /actuator/refresh 를 POST 로 호출하면 변경이 된다.
  • 즉 서버를 재기동하지 않아도 구성 정보의 변경을 반영할 수 있다.

Spring Cloud Gateway 에서 Spring Cloud Config 연동하기

  • Users MicroService 에서 한 것 처럼 config , bootstrap , actuator 3개의 dependency 를 추가한다.
  • 또한 bootstrap.yml 파일을 만들고 spring.cloud.config.uri , name 을 작성한다.
  • Users MicroService 에서 연동한것과 유사하기 때문에 어렵지 않다.
  • 이번에는 application.yml 에서 actuator 의 노출하려는 정보에서 httptrace 까지 추가한다.
    -> 말 그대로 http 의 trace 를 보여주는 기능이다.
    -> HttpTraceRepository 를 Bean 으로 등록해야한다.
  • application.yml 에서 USER-SERVICE 의 Actuator 정보를 추가해야한다.

먼저 apigateway-service 에서 3가지 의존성을 추가하고 bootstrap.yml 파일을 만들고 user-serivce 와 같이 작성한다.

  • spring.cloud.config.uri 는 8888 port 로 지정한다.
    -> config-server 의 port 를 8888 으로 했기 때문
# bootstrap.yml
spring:
  cloud:
    config:
      uri: http://127.0.0.1:8888
      name: ecommerce
      
# application.yml      
        - id: user-service
          uri: lb://USER-SERVICE
          predicates:
            - Path=/user-service/actuator/**
            - Method=GET,POST
          filters:
            - RemoveRequestHeader=Cookie
            - RewritePath=/user-service/(?<segment>.*), /$\{segment}
            
management:
  endpoints:
    web:
      exposure:
        include: refresh , health , beans , httptrace
  • /actuator/** 에 접근하는 정보도 추가
  • refresh 등 다양한 actuator 정보 노출
    @Bean
    public HttpExchangeRepository httpExchangeRepository() {
        return new InMemoryHttpExchangeRepository();
    }
  • httptrace 를 확안히 위해서는 HttpExchangeRepository 를 Bean 으로 등록해야한다.

해당 정보를 추가하고 실행하면 8888 정보를 가져오고, 만약 구성 정보의 변경을 반영하기 위해서는 /actuator/refresh 를 POST 로 요청하고 나면 반영이 된다.

  • 하지만 해당 방법도 비효율적이기 떄문에 다음 섹션에서 배우는 Spring Cloud Bus 를 사용하도록 하자.

Profiles 을 사용한 Configuration 적용

위에 우선순위를 보면 현재 2번에 있는 방법을 사용한 것이다.

  • ecommerce.yml 파일을 만들어서 해당 파일의 정보를 읽도록 했다.

이번에는 3번의 방법을 사용해보자.

  • ecommerce-dev , ecommerce-uat , ecommerce-prod
  • user-service 는 ecommerce-dev 파일을 읽고 apigateway-service 는 ecommerce-prod 파일을 읽도록 해보자.
  • 먼저 git-local-repo 파일을 만들고 ecommerce.yml 파일을 복사하여 ecommerce-dev.yml , ecommerce-prdo.yml 파일을 만든다.
  • dev 파일에는 token.secret 에 dev 를 넣어주고 prod 파일에는 prod 를 넣어준다.
# apigateway-service bootstrap.yml
spring:
  cloud:
    config:
      uri: http://127.0.0.1:8888
      name: ecommerce
  profiles:
    active: prod
    
# user-service bootstrap.yml
spring:
  cloud:
    config:
      uri: http://127.0.0.1:8888
      name: ecommerce
  profiles:
    active: dev
  • 위에처럼 user-service 에는 spring.profiles.active 에 dev 를 넣고 apigateway-service 에는 prod 를 넣으면 ecommerce-dev , ecommerce-prod 부터 찾고 해당 정보를 가져온다.
  • 하지만 이렇게 다르게 했을 경우 오류가 발생하는데 그 이유는 apigateway-service 에서 token의 유효성을 검사하는 로직이 있는데 user-service 와 token.secret 의 값이 다르기 때문에 오류가 발생한다.
  • 해당 경우에는 active 를 똑같이 해야 정상적으로 돌아가지만, 이런식으로 다양한 configuration 파일을 읽어올 수 있다.

Remote Git Repository

만약 git-local-repo 라는 파일을 Git 에 올렸을때 해당 정보를 가지고 오는 방법을 알아보자.

# config-server application.yml
server:
  port: 8888
spring:
  application:
    name: config-service
  cloud:
    config:
      server:
        git:
          uri: git repo 주소
          username:
          password:
  • 위와 같이 URI 만 해당 git repository 주소를 넣어주면 된다.
  • 만약 private repository 인 경우에는 username , password 정보도 넣어줘야한다.
  • public 인 경우에는 URI 만 넣어주면 된다.

Native File Repository

git 에 저장하는 것이 아닌 파일 시스템에 저장한 정보를 가져오는 방법을 알아보자.

native-file-repo 라는 파일을 만든다.

  • 해당 파일에 yml 파일들을 생성한다.
  • application.yml , ecommerce.yml , user-service.yml
# config-server application.yml
server:
  port: 8888
spring:
  application:
    name: config-service
  profiles:
    active: native
  cloud:
    config:
      server:
        native:
          search-locations: file://${user.home}/Desktop/native-file-repo
  • 8888/user-service/natvie , 8888/ecommerce/native 로 접근해보면 각 yml 파일들의 정보를 가져오는 것을 확인할 수 있다.

WebSecurity.class

        http.authorizeHttpRequests((authz) -> authz
                .requestMatchers(new AntPathRequestMatcher("/actuator/**")).permitAll()
                .requestMatchers(new AntPathRequestMatcher("/**")).permitAll()
                .requestMatchers(new AntPathRequestMatcher("/h2-console/**")).permitAll()
                .requestMatchers("/**").access(
                        new WebExpressionAuthorizationManager("hasIpAddress('127.0.0.1') or hasIpAddress('172.30.56.196')"))
        ).authenticationManager(authenticationManager);
  • requestMatchers.access() 에 hasIpAddress 를 사용해서 requestMatchers 를 만들 수도 있다.
  • /login 와 같은 경우는 permitAll() 을 시켜야하지만 내 IP 로 접근하는 경우는 언제든지 접근할 수 있도록 할 수 있다.

참고자료

profile
덕업일치

0개의 댓글