
스프링 클라우드를 사용하여, MSA를 구축하는 강좌를 듣고있다. 그래서 SpringCloud가 정확히 어떤 것인지 파악해보는 시간을 가지려고 한다. 조금 사용해본 결과 분산 시스템을 위해 사용되는 것이며, 다양한 서브 API들이 있다. 이번엔 SpringCloud의 사용 목적과 방향성을 파악해보고자 한다. 공식문서를 먼저 보면
Spring Cloud provides tools for developers to quickly build some of the common patterns in distributed systems.(e.g. configuration management, service discovery, circuit breakers, intelligent routing, micro-proxy, control bus, short lived microservices and contract testing).
Coordination of distributed systems leads to boiler plate patterns, and using Spring Cloud developers can quickly stand up services and applications that implement those patterns. They will work well in any distributed environment, including the developer’s own laptop, bare metal data centres, and managed platforms such as Cloud Foundry.
스프링 클라우드는 개발자에게 흔히 사용하는 분산 시스템 패턴들을 개발할 수 있도록 해준다. 그 예가 설정 관리, discovery, circuitebreakers, routing, micro-proxy 등등이 있다. 그리고 분산 시스템을 만들다보면 boiler plate patterns를 만들게 되는데 스프링 클라우드는 이를 더 빠르게 할 수 있도록 도와준다. 롬복의 어노테이션 Bean, Controller, Data 등등을 활용해서 개발 속도를 빠르게 하는것 처럼 말이다.
버전은 위와 같다. Release Train은 모르는 단어라 검색해봤는데, 주기적인 업데이트를 할 때 쓰는 용어로, 일반적으로 주요한 버그 수정, 보안 업데이트, 새로운 기능 추가 등을 포함한다. 아래를 보면 스프링 클라우드의 버전 설정 방식을 볼 수 있는데 properties로 springCloud의 버전을 정해둔다. 위 표에 써있는 연도를 스프링 클라우드 버전으로 쓸 수 있다.
<properties>
<spring-cloud.version>2023.0.0</spring-cloud.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
참고로 이야기 하는데 위처럼 Maven의 Property로 버전을 명시하고, dependency 설정에서는 해당 프로퍼티를 가져다 쓰는 방식이 BOM 매커니즘 이라고 한다. 좋은게 가장 중요한건 SpringCloud의 버전을 설정하고, 하위 API에 대해 의존성을 등록할 때 자동적으로 상위에 선언해둔 spring-cloud버전으로 설정된다는 것이다. 그래서 SpringCloud dependencyManagement를 등록하고, 다른 여러 api를 dependency로 등록하면 알아서 그 버전이 정해진다. 그래서 아래처럼 버전 없이 사용할 의존성 groupId, artifactId만 쓰면 해당 의존을 쓸 수 있다.
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
...
</dependencies>
그닥 큰 내용은 아니지만 위처럼 사용할 수 있는 원리를 조금 더 정확히 파악할 수 있어서 SpringCloud의 내부 의존성을 확인해봤다. 내부에 아래 MainProject에 있는 모든 프로젝트의 버젼이 정해져있다. 정말 너무 편하다. 다른 프레임워크를 사용해보진 않았지만, 갈수록 설계를 잘 하는게 중요해질 것만 같은 느낌이 든다. 단순한 많은 작업들이 편해진다면 더 중요한 것들에 대한 향상이 따르는 법이기 때문이다. 어쨋든 SpringCloud의 내부 의존성은 아래와 같다.
<properties>
<spring-cloud-circuitbreaker.version>3.1.1</spring-cloud-circuitbreaker.version>
<spring-cloud-bus.version>4.1.1</spring-cloud-bus.version>
<spring-cloud-consul.version>4.1.1</spring-cloud-consul.version>
<spring-cloud-stream.version>4.1.1</spring-cloud-stream.version>
<spring-cloud-contract.version>4.1.2</spring-cloud-contract.version>
<spring-cloud-openfeign.version>4.1.1</spring-cloud-openfeign.version>
<spring-cloud-config.version>4.1.1</spring-cloud-config.version>
<spring-cloud-gateway.version>4.1.2</spring-cloud-gateway.version>
<spring-cloud-task.version>3.1.1</spring-cloud-task.version>
<spring-cloud-vault.version>4.1.1</spring-cloud-vault.version>
<main.basedir>${basedir}/../..</main.basedir>
<spring-cloud-netflix.version>4.1.1</spring-cloud-netflix.version>
<spring-cloud-function.version>4.1.1</spring-cloud-function.version>
<spring-cloud-build.version>4.1.1</spring-cloud-build.version>
<spring-cloud-commons.version>4.1.2</spring-cloud-commons.version>
<spring-cloud-kubernetes.version>3.1.1</spring-cloud-kubernetes.version>
<spring-cloud-zookeeper.version>4.1.1</spring-cloud-zookeeper.version>
</properties>
================================== ==================================
================================== ==================================
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons-dependencies</artifactId>
<version>${spring-cloud-commons.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-dependencies</artifactId>
<version>${spring-cloud-netflix.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-dependencies</artifactId>
<version>${spring-cloud-stream.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-task-dependencies</artifactId>
<version>${spring-cloud-task.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-circuitbreaker-dependencies</artifactId>
<version>${spring-cloud-circuitbreaker.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-dependencies</artifactId>
<version>${spring-cloud-config.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-function-dependencies</artifactId>
<version>${spring-cloud-function.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-gateway-dependencies</artifactId>
<version>${spring-cloud-gateway.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-consul-dependencies</artifactId>
<version>${spring-cloud-consul.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-vault-dependencies</artifactId>
<version>${spring-cloud-vault.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-zookeeper-dependencies</artifactId>
<version>${spring-cloud-zookeeper.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-bus-dependencies</artifactId>
<version>${spring-cloud-bus.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-dependencies</artifactId>
<version>${spring-cloud-contract.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-openfeign-dependencies</artifactId>
<version>${spring-cloud-openfeign.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-dependencies</artifactId>
<version>${spring-cloud-kubernetes.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
MSA를 만들다보면 설정파일이 굉장히 중요하고 양이 많다. 서비스별로 다 떨어져 있기 때문에 Gateway에서는 해당 서비스들을 등록하는 설정이 필요하고, 각 서비스들은 자신들을 관리할 discoveryService를 등록해야하는 등의 설정들이 필요하다. 더해서 Filter, Constructor등 부가적인 설정을 더 하게될 수도 있다. 그래서 필요한게 별도의 Config 서버이다. 그런 환경을 제공해주는 프로젝트다
MSA의 진입점이다. 여기서 유저의 요청을 받아 여러 각 개별의 서비스로 전달을 해준다. 그리고 로드밸런서 역할도 설정할 수 있다. 아주 유용하다.
서버가 여러개 띄워져 있을 때 이를 관리할 수 있는 그런 서비스이다. Gateway도 여기서 관리할 수 있다. 의존성으로 SpringCloudNetflix discovery 서비스를 등록해서 해당 서버의 uri를 다른 서비스의 설정에 등록해주면, SpringCloudNetflix discovery 서비스의 uri로 들어갔을 때 아래와 같은 화면을 볼 수 있다.

사용해보지 못 했는데 HashiCorp의 Consul을 기반으로 한다. 넷플릭스 유레카 처럼 discovery service도 해주는데 이보다 더 다양한 기능을 제공한다. 로드밸런서 역할이나, 특징적으로 모니터링, 분산 락 등의 기능이 있다. 즉, 넷플릭스 유레카보다 조금 더 어렵지만 더 상세하고 유용한 기능들이 많다.
A cloud-native orchestration service for composable microservice applications on modern runtimes. Easy-to-use DSL, drag-and-drop GUI, and REST-APIs together simplifies the overall orchestration of microservice based data pipelines.
위 내용은 일단 공식문서 그대로의 내용이다. 일단 오케스트레이션 도구인데 데이터 파이프라인을 기본으로 하는 오케스트레이션 도구이다. 굳이 비교하자면 K8S는 애플리케이션 컨테이너 레벨을 관리한다면, SpringCloudDataFlow는 데이터 파이프라인 레벨의 오케스트레이션을 다룬다.
Spring Cloud Function promotes the implementation of business logic via functions. It supports a uniform programming model across serverless providers, as well as the ability to run standalone (locally or in a PaaS).
AWS 람다같은 기능으로 비즈니스 로직을 단일 함수로 구현할 수 있도록 지원한다.
A lightweight event-driven microservices framework to quickly build applications that can connect to external systems. Simple declarative model to send and receive messages using Apache Kafka or RabbitMQ between Spring Boot apps.
이건 나중에 쓰게될텐데 Kafka나 RabbitMQ와 같이 메시지 브로커랑 애플리케이션을 연결해주는 도구라고 한다.
Spring Cloud Stream Applications are out of the box Spring Boot applications providing integration with external middleware systems such as Apache Kafka, RabbitMQ etc. using the binder abstraction in Spring Cloud Stream.
이건 SpringCloudStream의 업그레이드 버젼이라고 볼 수도 있을 것 같다. 정확히는 어렵지만 SpringCloudStream의 구현체라고도 볼 수 있다.
A short-lived microservices framework to quickly build applications that perform finite amounts of data processing. Simple declarative for adding both functional and non-functional features to Spring Boot apps.
간단하고 짧은 실행 시간을 가지는 데이터 처리 작업을 위한 프레임워크이다. 예를 들어, 특정 이벤트 발생 시 데이터를 처리하거나, 주기적인 데이터 정리 작업 등을 수행할 때 Spring Cloud Task가 유용하게 쓴인다고 한다.
An event bus for linking services and service instances together with distributed messaging. Useful for propagating state changes across a cluster (e.g. config change events).
메시지 브로커와 역할이 비슷하다고 한다. 정확히는 모르겠으나 변경사항을 서버간 전파하는 것이 가능하다.
SpringCloud는 많은 하위 프로젝트가 있다. 이는 분산시스템 구축을 지원하는 구성들로 이루어져 있으며, 메시지 브로커, 게이트웨이 등등이 있다. 여러 메인 프로젝트들을 전부 사용해본 것도 아니고 사용해본 것들도 깊이 있게 써보질 않아서 자세히 쓸 수는 없었지만, 대략적으로 큰 그림은 그릴 수 있는 것 같다. 앞으로 좀 더 자세한 내용들을 다뤄보겠다.