스프링 프레임워크(Spring Framework)는 오늘날 Java 기반 웹 애플리케이션 개발의 표준으로 인식되고 있습니다. 스프링 프레임워크를 기반으로 한 다수의 상용 프레임워크가 출시되었고, 스프링과 함께 많은 발전을 이뤄냈습니다. 이같은 상황에서 스프링 프로젝트는 더욱 향상된 개발 환경을 제공하기 위해 스프링 부트(Spring Boot), 보안 및 인증 모듈 등을 추가하였고, 새로운 기술을 지원하고자 다양한 서브 프로젝트를 개발해오고 있습니다.
이제 클라우드가 시대적 대세라는 사실에 이견을 제시하는 사람은 없을 것입니다. 스프링 역시 이런 변화에 맞춰 스프링 클라우드(Spring Cloud)라는 서브 프로젝트를 만들어 성장시키고 있습니다. 스프링은 영리하게도 모든 것을 자신이 직접 개발하기보다는 오픈소스의 개념에 맞게 이미 발전하고 있는 다른 모듈과 스프링을 연계하는 것에 많은 관심을 기울이고 있습니다.
본 아티클에서는 스프링 클라우드가 어떻게 클라우드 네이티브(Cloud Native) 프로그램 개발을 지원하는지 알아보겠습니다.
먼저 클라우드 네이티브 애플리케이션에 대해서 간략하게 살펴보겠습니다.
클라우드 네이티브를 이해하기 위해서는 먼저 클라우드 환경에서 실행하는 SaaS(Software as a Service) 애플리케이션의 특징을 알아야 합니다. 잘 만들어진 SaaS 애플리케이션은 아래의 특징을 가지고 있습니다.
• 설정 자동화 절차를 체계화하여(Declarative) 새로운 개발자가 프로젝트에 참여하는 데 드는 시간과 비용을 최소화합니다.
• 운영체제에 따라 달라지는 부분을 명확히 하고 실행 환경 사이의 이식성을 극대화합니다.
• 최근 등장한 클라우드 플랫폼에 적합하고 서버와 시스템의 관리가 필요 없습니다.
• 개발 환경과 운영 환경의 차이를 최소화하고 민첩성을 극대화하면서 지속적인 배포가 가능합니다.
• 툴, 아키텍처 및 개발 방식을 크게 바꾸지 않고 확장할 수 있습니다(Scale Up).
이와 같은 애플리케이션을 만들기 위해 웹 애플리케이션 개발 PaaS 사업자인 Heroku의 엔지니어들은 Twelve-Factor App이라는 SaaS 앱 개발 방법론을 정의했습니다. 이를 기반으로 개발하면 시간이 지나면서 앱이 유기적으로 성장할 수 있습니다. Twelve-Factor App의 주요 내용은 다음과 같습니다.
물론 이런 규칙이 클라우드 네이티브 애플리케이션과 완전히 일치하지는 않습니다. 이후로도 계속해서 개발, 발전되었기 때문입니다. 그럼에도 불구하고 이는 클라우드 네이티브의 기반이 되는 원칙으로 여전히 고려해야 할 요소인 것은 분명합니다.
소프트웨어 개발 방식은 IaaS(Infrastructure as a Service)와 PaaS(Platform as a Service) 환경으로 넘어오면서 여러 방식으로 변화하였습니다. 위의 Twelve-Factor App에서 설명했듯이 수평적으로 확장 가능하고, 프로세스 단위로 분리하여 독립성을 유지하며, 쉽게 종료하고 다시 생성할 수 있어야 합니다. 이런 변화의 핵심은 클라우드입니다. 도커(Docker), 컨테이너(Container), 컨테이너 오케스트레이션(Container Orchestration), 이런 용어들이 어느새 우리에게 익숙해졌습니다. 그만큼 빠르게 발전하는 분야에서 표준화를 위해 노력하는 단체들이 있고 그중 가장 유명한 곳이 CNCF입니다.
CNCF는 다양한 클라우드 네이티브 애플리케이션 개발에 도움을 주기 위해 아키텍처를 제안하고 관련된 영역을 정의하며 개별 영역에서 유효한 제품과 기술을 소개합니다(CNCF Cloud Native Interactive Landscape).
앞서 클라우드 네이티브 애플리케이션에 필요한 기술을 살펴봤습니다. 이어서 스프링 클라우드에서 위의 요소들을 어떻게 지원하는지, 또한 어떤 외부 제품을 연계하여 개발할 수 있는지 설명하겠습니다.
애플리케이션이 동작할 때 설정 파일을 읽어서 처리하는 것은 매우 일반적입니다. 만약 이런 설정이 없다면 ‘개발∙운영 환경 일치’라는 요소를 만족하기 어려울 것입니다. 대부분의 경우 설정 파일은 바이너리와 함께 배포되며 환경 혹은 실행 옵션으로 실제 값이 선택됩니다. 그러나 클라우드 환경에서 마이크로서비스를 지원하는 경우 환경 자체의 값들이 자유롭게 변경∙처리될 수 있어야 합니다. 즉 중앙저장소에서 모든 설정값을 관리하고 개별 서비스는 설정값을 해당 저장소에 문의하여 가져와야 합니다. 스프링 클라우드 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)인데 리본에서 발생했던 단점과 사용성이 개선되었습니다.
지금까지 서비스 간 커뮤니케이션을 쉽고 빠르게 할 수 있는 방법에 대해서 알아보았습니다. 중요한 요소 중 하나는 장애 발생 시 이를 회피하는 방법입니다. 동일한 서비스를 제공하는 여러 인스턴스 중 하나에서 장애가 발생할 경우 해당 인스턴스에 계속 요청을 보내 타임아웃(Timeout)까지 기다리는 것은 전체 시스템에 부하를 주게 됩니다. 따라서 장애가 발생한 인스턴스로 가는 요청을 ‘중단’시킬 필요가 있습니다. 서킷 브레이커가 그런 역할을 합니다. 리본이나 주울(Zuul) 모두 이런 기능을 갖추고 있습니다.
클라우드 네이티브 환경에서 특정 리소스에 하나의 모듈만 접근해야 하는 경우가 종종 발생할 수 있습니다. 이때 필요한 것이 글로벌 락(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차 가공 및 영리적인 이용을 금하고 있습니다.
에스코어㈜ 소프트웨어사업부 개발플랫폼그룹
클라우드와 Spring Cloud에 관심이 많은 개발자입니다. 현재 Anyframe 개발 및 유지보수를 담당하고 있습니다.