loading...

스프링 클라우드(Spring Cloud)를 활용한 클라우드 네이티브 애플리케이션 개발

스프링 클라우드(Spring Cloud)를 활용한 클라우드 네이티브 애플리케이션 개발

스프링 프레임워크(Spring Framework)는 오늘날 Java 기반 웹 애플리케이션 개발의 표준으로 인식되고 있습니다. 스프링 프레임워크를 기반으로 한 다수의 상용 프레임워크가 출시되었고, 스프링과 함께 많은 발전을 이뤄냈습니다. 이같은 상황에서 스프링 프로젝트는 더욱 향상된 개발 환경을 제공하기 위해 스프링 부트(Spring Boot), 보안 및 인증 모듈 등을 추가하였고, 새로운 기술을 지원하고자 다양한 서브 프로젝트를 개발해오고 있습니다.

이제 클라우드가 시대적 대세라는 사실에 이견을 제시하는 사람은 없을 것입니다. 스프링 역시 이런 변화에 맞춰 스프링 클라우드(Spring Cloud)라는 서브 프로젝트를 만들어 성장시키고 있습니다. 스프링은 영리하게도 모든 것을 자신이 직접 개발하기보다는 오픈소스의 개념에 맞게 이미 발전하고 있는 다른 모듈과 스프링을 연계하는 것에 많은 관심을 기울이고 있습니다.

본 아티클에서는 스프링 클라우드가 어떻게 클라우드 네이티브(Cloud Native) 프로그램 개발을 지원하는지 알아보겠습니다.

클라우드 네이티브 애플리케이션

먼저 클라우드 네이티브 애플리케이션에 대해서 간략하게 살펴보겠습니다.

The Twelve-Factor App

클라우드 네이티브를 이해하기 위해서는 먼저 클라우드 환경에서 실행하는 SaaS(Software as a Service) 애플리케이션의 특징을 알아야 합니다. 잘 만들어진 SaaS 애플리케이션은 아래의 특징을 가지고 있습니다.

 • 설정 자동화 절차를 체계화하여(Declarative) 새로운 개발자가 프로젝트에 참여하는 데 드는 시간과 비용을 최소화합니다.
 • 운영체제에 따라 달라지는 부분을 명확히 하고 실행 환경 사이의 이식성을 극대화합니다.
 • 최근 등장한 클라우드 플랫폼에 적합하고 서버와 시스템의 관리가 필요 없습니다.
 • 개발 환경과 운영 환경의 차이를 최소화하고 민첩성을 극대화하면서 지속적인 배포가 가능합니다.
 • 툴, 아키텍처 및 개발 방식을 크게 바꾸지 않고 확장할 수 있습니다(Scale Up).

    잘 만들어진 SaaS 애플리케이션은 손쉬운 확장이 가능해야 합니다.

이와 같은 애플리케이션을 만들기 위해 웹 애플리케이션 개발 PaaS 사업자인 Heroku의 엔지니어들은 Twelve-Factor App이라는 SaaS 앱 개발 방법론을 정의했습니다. 이를 기반으로 개발하면 시간이 지나면서 앱이 유기적으로 성장할 수 있습니다. Twelve-Factor App의 주요 내용은 다음과 같습니다.

1. 코드베이스: 버전 관리되는 하나의 코드베이스와 다양한 배포
애플리케이션당 하나의 코드베이스만 존재하고 이를 CI/CD(Continuous Integration/Continuous Delivery)를 통해서 다양한 환경(개발, 테스트, 운영)에 배포합니다.

2. 종속성: 명시적으로 선언되고 분리된 종속성
Maven과 같은 종속성 관리 도구를 사용합니다.

3. 설정: 환경(environment)에 저장된 설정
설정과 코드를 명시적으로 분리하여 실행 환경에 맞는 설정으로 유연하게 변경이 가능하게 합니다.

4. 백엔드 서비스: 백엔드 서비스를 연결된 리소스로 취급
네트워크를 통해서 사용하는 외부의 서비스를 리소스로 간주합니다. 설정 시스템에 저장된 값을 읽어 해당 리소스에 접근합니다.

5. 빌드, 릴리즈, 실행: 철저하게 분리한 빌드와 실행 단계
CI/CD를 통해서 빌드와 릴리즈 단계를 분리합니다.

6. 프로세스: 애플리케이션을 하나 또는 여러 개의 무상태(stateless) 프로세스로 실행
클라우드에서 연산은 상태(State)가 없어야 합니다.

7. 포트 바인딩: 포트 바인딩을 사용해서 서비스 공개
앱은 독립적이어야 합니다. 실행 환경에 대한 런타임 인젝션에 의존하지 않습니다. 각 서비스는 HTTP 환경으로 제공되며 API를 통해서만 접근합니다. 데이터는 공유하지 않습니다.

8. 동시성: 프로세스 모델을 사용한 확장
확장과 자원 사용량 개선을 위해서 각 서비스와 함수는 독립적이고 수평적으로 동작합니다.

9. 폐기 가능: 빠른 시작과 그레이스풀 셧다운(Graceful shutdown)을 통한 안정성 극대화
개별 프로세스들은 쉽게 종료할 수 있고 빠르게 시작할 수 있습니다.

10. 개발/프로덕션환경 일치: 개발, 스테이징, 프로덕션 환경을 최대한 비슷하게 유지
각각의 환경 차이를 줄임으로써 지속적인 배포가 가능하도록 지원합니다.

11. 로그: 로그를 이벤트 스트림으로 취급
분산 시스템에서 로그를 관리하는 것은 중요합니다. 개별 앱은 이를 관리하지 않고 외부 시스템으로 전달하기만 합니다.

12. Admin 프로세스: admin/maintenance 작업을 일회성 프로세스로 실행
관리를 위한 코드와 작업을 분리하지 않고 동일한 환경에서 배포, 실행합니다.

물론 이런 규칙이 클라우드 네이티브 애플리케이션과 완전히 일치하지는 않습니다. 이후로도 계속해서 개발, 발전되었기 때문입니다. 그럼에도 불구하고 이는 클라우드 네이티브의 기반이 되는 원칙으로 여전히 고려해야 할 요소인 것은 분명합니다.

CNCF(Cloud Native Computing Foundation)

소프트웨어 개발 방식은 IaaS(Infrastructure as a Service)와 PaaS(Platform as a Service) 환경으로 넘어오면서 여러 방식으로 변화하였습니다. 위의 Twelve-Factor App에서 설명했듯이 수평적으로 확장 가능하고, 프로세스 단위로 분리하여 독립성을 유지하며, 쉽게 종료하고 다시 생성할 수 있어야 합니다. 이런 변화의 핵심은 클라우드입니다. 도커(Docker), 컨테이너(Container), 컨테이너 오케스트레이션(Container Orchestration), 이런 용어들이 어느새 우리에게 익숙해졌습니다. 그만큼 빠르게 발전하는 분야에서 표준화를 위해 노력하는 단체들이 있고 그중 가장 유명한 곳이 CNCF입니다.

CNCF는 다양한 클라우드 네이티브 애플리케이션 개발에 도움을 주기 위해 아키텍처를 제안하고 관련된 영역을 정의하며 개별 영역에서 유효한 제품과 기술을 소개합니다(CNCF Cloud Native Interactive Landscape).

스프링 클라우드

앞서 클라우드 네이티브 애플리케이션에 필요한 기술을 살펴봤습니다. 이어서 스프링 클라우드에서 위의 요소들을 어떻게 지원하는지, 또한 어떤 외부 제품을 연계하여 개발할 수 있는지 설명하겠습니다.

분산∙버전 설정(Spring Cloud Config)

애플리케이션이 동작할 때 설정 파일을 읽어서 처리하는 것은 매우 일반적입니다. 만약 이런 설정이 없다면 ‘개발∙운영 환경 일치’라는 요소를 만족하기 어려울 것입니다. 대부분의 경우 설정 파일은 바이너리와 함께 배포되며 환경 혹은 실행 옵션으로 실제 값이 선택됩니다. 그러나 클라우드 환경에서 마이크로서비스를 지원하는 경우 환경 자체의 값들이 자유롭게 변경∙처리될 수 있어야 합니다. 즉 중앙저장소에서 모든 설정값을 관리하고 개별 서비스는 설정값을 해당 저장소에 문의하여 가져와야 합니다. 스프링 클라우드 Config는 ‘외부화된 구성(Cofnig)’에 대해 서버 및 클라이언트측 지원을 제공합니다. 기본적으로 설정은 키-값(Key-Value)의 쌍으로 되어 있고 필요시 암호화할 수 있습니다.

서비스 등록 및 조회

기존 환경에서는 특정 서비스가 다른 서비스를 호출해야 하는 경우 ‘어디에’, ‘어떻게’ 호출하는지 명확히 알고 있습니다. 하지만 클라우드 환경에서는 ‘어디에’가 실시간으로 변경되고 ‘어떻게’ 역시 자유롭게 바뀔 수 있습니다. 여기서 ‘어디에’에 대한 부분이 바로 서비스 등록 및 조회입니다. 각각의 서비스는 중앙저장소에 자신의 정보와 제공하는 ‘서비스의 이름’을 등록합니다. 서비스 내에서 다른 서비스를 호출할 때는 이 서비스 이름으로 ‘어디에’ 문의해야 할지 정보를 가져와서 호출합니다. 바로 위에서 설명한 ‘분산∙버전 설정’과 매우 유사합니다. 서비스 이름이 키가 되고 호출 위치가 값이 되는 것입니다. 따라서 설정에 대한 정보 처리와 서비스 정보 처리를 모두 제공하는 제품도 많이 있습니다.

이런 서비스 등록 및 조회 기능은 다양한 솔루션에서 지원합니다. 스프링 클라우드는 Spring Cloud Netflix(Eureka), Spring Cloud Consul(Hashicorp’s Consul), Spring Cloud Zookeeper(Apache Zookeeper)의 프로젝트에서 개별 솔루션과의 연동을 지원합니다. 물론 이런 솔루션이 단순히 키-값의 정보만을 제공하는 건 아니고 복제, 고가용성 등의 다른 기능도 가지고 있습니다. 각자의 운영 환경에 적합한 프로젝트를 선택하여 활용하면 됩니다.

라우팅

라우팅은 특정 네트워크 내부에서 목적지를 찾아가는 과정을 의미합니다. 이때 사용하는 것이 API 게이트웨이입니다. API 게이트웨이는 원래 다양한 네트워크를 사용하는 모듈 사이에서 중계와 필요한 변환 및 추가 처리 작업을 하는 모듈을 의미합니다. 즉 API 게이트웨이를 사용해서 라우팅하는 것입니다. API 게이트웨이는 외부에 노출하는 포인트를 하나로 만들어 관리가 편해지고 인증 혹은 SSL 처리를 담당하여 게이트웨이 내부의 모듈을 더 간단하게 만들 수 있도록 도와줍니다. 아울러 필요한 경우 필터를 추가할 수 있습니다. 스프링 클라우드는 Spring Cloud Netflix(Zuul)를 지원하고 내부 프로젝트인 Spring Cloud Gateway 또한 제공하고 있습니다.

서비스 간 호출

마이크로서비스 환경에서 서비스 간 호출은 매우 빈번하게 발생합니다. 위의 API 게이트웨이를 활용해 작업을 수행할 수 있지만 신뢰할 수 있는 내부망에서 SSL, 필터링 처리 등 불필요한 부하가 발생합니다. 또한 이러한 호출은 일대일 호출과 일대다 호출로 구분할 수 있습니다. 일대일 호출의 경우 직접 서비스 주소를 찾아서 호출하거나 클라이언트사이드 부하분산 솔루션인 리본(Ribbon)을 활용하는 것이 일반적입니다. 일대다 호출의 경우 다시 설명할 분산 메시징을 활용할 수 있습니다. 스프링 클라우드는 Spring Cloud Netflix(Ribbon)를 지원합니다.

부하 분산

클라우드 환경에서는 동일한 서비스를 처리하는 인스턴스가 많을 수 있습니다. 이 중에서 적합한 인스턴스로 호출하는 것, 그리고 부하를 분산시키는 것이 중요합니다. API 게이트웨이나 클라이언트사이드 부하분산 솔루션은 다양한 알고리즘으로 이를 지원합니다. 리본을 예로 들면 기본적인 라운드 로빈(Round Robin) 이외에 평균 응답시간을 기록해서 활용하는 방법, 현재 연결된 서비스 수를 받아서 활용하는 방법 등이 있습니다. 리본 외에도 스프링은 클라이언트의 부하분산을 위한 모듈을 제공합니다. 스프링 클라우드 로드밸런서(Spring Cloud Loadbalancer)인데 리본에서 발생했던 단점과 사용성이 개선되었습니다.

    스프링 클라우드 클라이언트의 부하분산을 위한 로드밸런서 모듈을 제공합니다.

서킷 브레이커(Circuit Breakers)

지금까지 서비스 간 커뮤니케이션을 쉽고 빠르게 할 수 있는 방법에 대해서 알아보았습니다. 중요한 요소 중 하나는 장애 발생 시 이를 회피하는 방법입니다. 동일한 서비스를 제공하는 여러 인스턴스 중 하나에서 장애가 발생할 경우 해당 인스턴스에 계속 요청을 보내 타임아웃(Timeout)까지 기다리는 것은 전체 시스템에 부하를 주게 됩니다. 따라서 장애가 발생한 인스턴스로 가는 요청을 ‘중단’시킬 필요가 있습니다. 서킷 브레이커가 그런 역할을 합니다. 리본이나 주울(Zuul) 모두 이런 기능을 갖추고 있습니다.

Global Locks, Leadership Election and Cluster State

클라우드 네이티브 환경에서 특정 리소스에 하나의 모듈만 접근해야 하는 경우가 종종 발생할 수 있습니다. 이때 필요한 것이 글로벌 락(Global Lock)입니다. 해당 리소스에 접근한 모듈이 잠금(Lock)을 생성하고, 이후 접근한 모듈이 잠금 상태라면 대기, 잠금이 없다면 자신이 잠금을 잡는 방식입니다. 각각의 인스턴스가 개별로 동작하므로 이런 잠금은 하나의 시스템이 아닌 전체를 대상으로 관리되어야 합니다. 그리고 특정 서비스 사이에서 리더(Leader)를 선출하고 관리하는 것도 일반적인 요구조건입니다. 이런 기능은 자세히 보면 키-값 관리 기능의 확장으로 생각할 수 있습니다. 전체 시스템에서 하나의 키를 이용해 잠금을 관리할 수 있고 리더 역시 특정 키를 조회해 값이 없으면 조회한 모듈이 자신으로 값을 설정하고, 값이 있으면 해당 모듈을 리더로 판단하면 됩니다. 리더 대체나 잠금 자동 해제 등 실제 서비스는 키-값 관리 기능 외에 추가 기능을 지원합니다. 콘술(Consul)이나 주키퍼(Zookeeper) 모두 이런 기능을 제공하고 있습니다.

분산 메시징

앞서 서비스 호출 단계에서 목표한 서비스를 찾아 호출하는 방식에 대해 설명했습니다. 그런데 클라우드 네이티브 애플리케이션에서는 메시지를 이용하는 방식도 자주 사용됩니다. 예를 들어 배달 서비스를 개발하는 경우 고객의 주문이 완료되면 고객에게 문자로 결과를 보내고, 배달자에게도 같은 내용을 전송하며, 서비스 메인 모듈에도 전달합니다. 그리고 필요한 경우 프로모션 담당 모듈에도 메시지를 전달할 수 있습니다. 이렇게 메시지 큐(Message Queue)를 이용하거나 게시∙구독 모델을 사용해 분산 메시징을 지원할 수 있습니다. 세부적으로 들어가면 컨슈머 그룹(Consumer Group)을 활용해 중복 처리를 방지하거나 파티셔닝(Partitioning) 기능을 활용할 수 있습니다. Spring Cloud Stream은 래빗MQ(RabbitMQ), 아파치 카프카(Apache Kafka), 구글 펍섭(Google PubSub) 등의 제품과 연계하여 분산 메시징을 지원합니다.

마치며

지금까지 클라우드 네이티브 애플리케이션을 개발하기 위해 어떤 요소를 고려해야 하는지, 그리고 스프링 클라우드가 어떤 기능을 제공하는지에 대해 알아보았습니다. 개별 기능은 스프링에서 직접 개발했거나 다른 유용한 제품들과 연계함으로써 개발자가 애플리케이션을 쉽게 개발할 수 있도록 도와줍니다. ‘스프링 클라우드에서 지원한다’고 하는 것은 Starter를 이용해 쉽게 구성하고, 간단한 설정과 코드 내 Annotation만으로 기능을 활용하며, 개발 도중에 다양한 샘플을 이용할 수 있다는 의미입니다.

스프링 클라우드가 개발자의 모든 문제를 해결해 줄 수는 없습니다. 그러나 스프링 프레임워크가 그러했듯이 스프링 클라우드 역시 꼭 필요한 요소들을 쉽고 빠르게 개발할 수 있도록 도움을 줄 것입니다. 그리고 지금까지와 같이 다가올 미래에도 계속해서 발전해 나갈 것입니다.

# References
[1] https://12factor.net/
[2] https://landscape.cncf.io/
[3] https://github.com/cncf/trailmap
[4] https://spring.io/projects/spring-cloud
[5] https://spring.io/blog/2020/03/25/spring-tips-spring-cloud-loadbalancer
[6] 클라우드 네이티브, 보리스 숄 외 저/정원천 역, 한빛미디어, 2020년 6월
[7] 마스터링 스프링 클라우드, 피요트르 민코프스키 저/김민석 역, 위키북스, 2018년 11월



▶  해당 콘텐츠는 저작권법에 의하여 보호받는 저작물로 기고자에게 저작권이 있습니다.
▶  해당 콘텐츠는 사전 동의 없이 2차 가공 및 영리적인 이용을 금하고 있습니다.


이 글이 좋으셨다면 구독&좋아요

여러분의 “구독”과 “좋아요”는
저자에게 큰 힘이 됩니다.

subscribe

구독하기

subscribe

하태준
하태준 클라우드 전문가

에스코어㈜ 소프트웨어사업부 개발플랫폼그룹

클라우드와 Spring Cloud에 관심이 많은 개발자입니다. 현재 Anyframe 개발 및 유지보수를 담당하고 있습니다.

공유하기