1.1 마이크로 서비스 아키텍처로 진화
소프트웨어 아키텍처는 소프트웨어 시스템을 구성하는 여러 구성 요소들의 구조, 동작 및 상호작용 방식에 대한 설계 및 정의를 다루는 중요한 분야다. 이는 시스템의 전체적인 설계뿐만 아니라, 구성 요소들이 어떻게 조직되고, 서로 어떻게 통신하며, 요구사항을 충족하는 방식으로 동작하는지를 결정한다. 소프트웨어 아키텍처는 시스템의 성능, 확장성, 유지보수성, 보안성 등 중요한 품질 속성에 영향을 미친다.
1.1.1 다계층 (N-계층) 아키텍처
다계층(N-계층) 아키텍처는 일반적인 엔터프라이즈 아키텍처 유형 중 하나로, 애플리케이션을 여러 계층으로 분리하여 각 계층이 특정한 책임을 맡도록 설계하는 방식이다. 이 구조에서는 각 계층이 독립적으로 존재하면서도 서로 상호작용하여 전체 시스템을 구성한다. 예를 들어, 애플리케이션을 개발할 때 UI를 위한 프로젝트, 서비스 계층을 위한 프로젝트, 데이터 관리를 위한 프로젝트를 각각 따로 만들고, 이들을 결합하여 최종 애플리케이션을 형성한다.
각 계층은 고유한 책임을 가지며, 이 분리된 계층들은 전체 애플리케이션을 더 효율적으로 개발하고 관리할 수 있게 해줍니다.
다계층 아키텍처의 장점
- 다계층 애플리케이션에서는 관심사 분리가 잘 분리되어 있어 UI, 데이터, 비즈니스 로직 같은 영역을 따로 고려할 수 있다.
- 팀이 다계층 애플리케이션의 여로 컴포넌트에서 독립적으로 작업하기 쉽다.
- 널리 알려진 엔터프라이즈 아키텍처이므로 숙련된 다계층 프로젝트 개발자를 찾기가 상대적으로 수월하다.
다계층 아키텍처의 단점
- 변경을 적용하려면 전체 애플케이션을 중지하고 재시작해야 한다.
- 메시지가 상하 전체 계층에 통행하므로 비효율적일 수 있다.
- 대규모 다계층 애플리케이션이 배포되고 나면 리팩터링은 어려울 수 있다.
1.1.2 모놀리스 아키텍처
중소 규모의 웹 애플리케이션은 종종 모놀리스 아키텍처로 개발된다. 모놀리스 아키텍처에서는 애플리케이션이 하나의 독립된 배포 가능한 단위로 구성된다. UI, 비즈니스 로직, 데이터베이스 접근 등 모든 기능이 하나의 애플리케이션으로 결합되어, 하나의 애플리케이션 서버에 배포된다.
애플리케이션은 단일 작업 단위로 배포될 수 있지만, 하나의 애플리케이션에 여러 개발 팀이 동시에 참여하는 경우가 많다. 각 팀은 보통 애플리케이션의 특정 부분을 담당하며, 이를 통해 서로 다른 분야의 기능을 개발한다. 예를 들어, UI/UX, 고객 관리, 데이터 웨어하우스, 금융 관계자 등 다양한 팀들이 협력하는 상황에서 사내 맞춤형 고객 관계 관리(CRM) 애플리케이션을 개발하는 시나리오를 생각할 수 있다.
마이크로서비스 아키텍처 지지자들은 종종 모놀리스를 부정적으로 평가하지만, 모놀리스는 여전히 훌륭한 선택일 수 있다. 모놀리스는 다계층 아키텍처나 마이크로서비스 같은 복잡한 구조보다 구축하고 배포하기가 더 간단하다. 만약 사용 사례가 명확하게 정의되어 있고, 변경될 가능성이 적다면, 모놀리스를 기반으로 시작하는 것이 좋은 선택이 될 수 있다.
하지만 애플리케이션의 규모와 복잡성이 증가하면 모놀리스를 관리하는 것이 점점 어려워질 수 있다. 모놀리스에서 발생하는 변경 사항은 종종 애플리케이션의 다른 부분에까지 영향을 미치게 되어, 운영 환경에서 더 많은 시간과 비용이 소요될 수 있다. 반면, 세 번째 옵션인 마이크로서비스 아키텍처는 더 많은 유연성과 유지 보수 측면에서 장점을 제공할 수 있다.
1.1.3 마이크로서비스란?
마이크로서비스 개념은 대규모 모놀리스 애플리케이션을 기술적 또는 조직적으로 확장하는 데 직면한 다양한 문제를 해결하기 위해 소프트웨어 개발 커뮤니티에서 등장했다. 마이크로서비스는 작고 느슨하게 결합된 분산 서비스로, 이를 통해 대규모 애플리케이션을 명확한 책임을 가진 관리하기 쉬운 구성 요소로 나눌 수 있다. 또한, 잘 정의된 작은 단위로 분해함으로써 대규모 코드베이스에서 발생하는 전통적인 복잡성 문제를 해결하는 데 도움이 될 수 있다.
마이크로서비스를 구현할 때 중요한 핵심 개념은 분해 (decomposing)와 분리 (unbundling)이다. 애플리케이션의 각 기능은 서로 완전히 독립적이어야 하며, 이를 통해 각 서비스가 독립적으로 개발, 배포, 확장될 수 있다. 이러한 분리 덕분에 시스템의 복잡성을 줄이고, 각 서비스의 유지보수와 관리가 용이해진다.
- 애플리케이션 로직은 명확하고 대등한 책임 경게 있는 작은 컴포넌트로 분해된다.
- 각 마이크로서비스는 작은 책임 영역을 담당하며, 서로 독립적으로 배포됩니다. 각 서비스는 특정 비즈니스 도메인에 대한 책임을 지며, 다른 서비스와의 의존성을 최소화하여 독립적으로 운영될 수 있다.
- 마이크로서비스는 서비스 소비자와 공급자 간 데이터를 교환하고자 HTTP와 JSON 같은 경량 통신 프로토콜을 사용한다.
- 마이크로서비스 애플리케이션은 기술에 구애받지 않는 포맷 (예시 JSON)을 사용하여 서로 통신하므로, 각 서비스는 특정 기술이나 언어에 의존하지 않는다. 즉, 마이크로서비스 아키텍처로 구축된 애플리케이션은 다양한 언어와 기술 스택을 사용할 수 있어 유연하고 확장성이 뛰어난 구조를 제공한다.
- 마이크로서비스의 특성상, 작은 규모의 독립적인 팀들이 각자의 명확한 책임 영역을 맡게 된다. 이러한 팀들은 애플리케이션 출시와 같은 공통된 목표를 향해 협력하되, 각 팀은 자신이 담당하는 서비스에만 집중하고 책임을 진다. 이를 통해 조직은 더욱 효율적이고 전문화된 작업을 할 수 있다.
1.1.4 애플리케이션 구축 방법을 바꾸어야 하는 이유
국내 시장에서 활동해 온 기업들이 글로벌 시장으로 확장하려는 기회를 인식하면서, 동시에 글로벌 경쟁의 치열함도 증가하고 있다. 경쟁이 심화됨에 따라, 개발자들은 애플리케이션 구축에 대해 아래와 같은 방식으로 사고하게 되었다.
- 복잡성의 증가: 고객은 기업의 모든 영역에서 자신을 인식받기를 기대하며, 이제는 하나의 데이터베이스와만 통신하거나 다른 애플리케이션과 통합되지 않는 단절된 애플리케이션은 더 이상 표준이 아니다. 오늘날 애플리케이션은 사내 데이터 센터뿐만 아니라 외부의 다양한 서비스 제공자 및 데이터베이스와도 원활하게 통신해야 한다.
- 고객 더 빠른 전달을 요구한다.: 고객은 더 이상 소프트웨어 패키지의 연간 릴리스를 기다리지 않는다. 대신, 소프트웨어 제품의 기능이 분리되어 제공되고, 새로운 기능이 몇 주 또는 몇 일 내에 릴리스되기를 기대한다.
- 고객의 안정적인 성능과 확장성 요구한다: 글로벌 애플리케이션은 트랜잭션량의 변동을 예측하기 어려워 언제 얼마나 많은 트랜잭션을 처리해야 할지 알기 힘들다. 이 때문에 애플리케이션은 트랜잭션 양에 따라 서버를 유연하고 원활하게 확장하거나 축소할 수 있어야 한다.
- 고객은 애플리케이션을 언제나 사용할 수 있기는 기대한다: 고객은 경쟁사와의 차이를 단 한 클릭으로 판단하므로, 애플리케이션은 높은 회복력 (resiliency)을 가져야 한다. 즉, 애플리케이션의 일부가 고장 나거나 문제가 발생하더라도, 그 문제가 전체 시스템에 영향을 미치지 않도록 해야 한다.
이러한 요구를 충족하기 위해, 애플리케이션 개발자는 높은 확장성과 중복성을 가진 애플리케이션을 구축하기 위해, 애플리케이션을 독립적으로 빌드하고 배포할 수 있는 작은 서비스로 분리해야 한다. 애플리케이션을 더 작은 서비스들로 분할하고, 단일 모놀리식 산출물에서 서비스별 산출물로 전환하면, 우리는 아래와 같은 시스템을 구축할 수 있다.
- 유연성 (flexible): 분리된 서비스는 새로운 기능을 빠르게 제공할 수 있도록 구성되며, 재배치가 용이하다. 다른 서비스와 상호 작용하는 코드가 적을수록, 코드 변경에 따른 복잡성도 줄어들고, 코드의 테스트 및 배포 시간이 단축된다.
- 회복성 (resilient): 분리된 서비스는 애플리케이션이 더 이상 한 부분의 문제로 전체가 영향을 받는 복잡한 시스템이 아님을 의미한다. 고장은 특정 서비스에 국한되며, 전체 애플리케이션의 장애로 확대되기 전에 제어된다. 회복성은 시스템이 치명적인 오류가 발생했을 때도, 애플리케이션이 원활하게 작동을 지속할 수 있도록 보장한다.
- 확장성 (scalable): 분리된 서비스는 여러 서버에서 쉽게 수평 확장이 가능하여, 필요한 기능이나 서비스를 효과적으로 확장할 수 있게 해준다. 반면, 모놀리스 애플리케이션은 모든 로직이 서로 얽혀 있어, 한 부분에서 병목 현상이 발생하면 전체 애플리케이션을 축소해야 하는 문제를 겪는다. 작은 서비스들로 구성된 시스템은 국지적으로 확장할 수 있어, 확장성과 비용 효율성 측면에서 훨씬 유리하다.
따라서 목표로 해야하는 애플리케이션은 이와 같다. 작고 (small), 단순하고 (simple), 분리된 (decoupled) 서비스 = 확장 가능하고 (scalable), 회복적이며 (resilient) 유연한 (flexible) 애플리케이션
마이크로서비스 방식이 시스템과 조직에 이점을 제공한다고 이해하는 것이 중요한다. 이를 통해 조직은 효율적인 구조와 의사 소통 방식을 구축할 수 있다. 콘웨이의 법칙 (conway's law)을 역으로 적용하면, 조직의 구조가 시스템 설계에 어떤 영향을 미치는지에 대한 통찰을 얻을 수 있다. 이 법칙은 조직의 의사 소통 방식과 구조를 개선할 수 있는 몇 가지 중요한 방향을 제시한다. 콘웨이의 법칙은 팀이 팀 내부와 다른 팀과 의사소통하는 방식이 팀이 작성하는 코드에 직접적으로 반영된다 것을 의미한다.
콘웨이의 법칙을 역으로 적용 (inveres Conway's law)하고 마이크로서비스 구조를 기반으로 회사 구조를 설계하면, 마이크로서비스를 구현할 수 있는 자율적이고 느슨하게 결합된 팀들을 구성할 수 있다. 이러한 팀들은 각자의 책임을 명확히 하여 애플리케이션의 커뮤니케이션과 안정성을 향상시킬 수 있다. 또한, 팀 간의 협업과 자율성을 높여 조직의 전반적인 구조를 개선하고, 시스템의 확장성과 유지 보수성을 크게 향상시킬 수 있다.
1.2 스프링 마이크로서비스
스프링은 자바 애플리케이션을 구축할 때 사용되는 대표적인 프레임워크로, 의존성 주입(Dependency Injection, DI)을 핵심 개념으로 삼고 있다. 의존성 주입을 사용하면 객체 간의 관계를 명시적으로 코드에 하드코딩하는 대신, 외부에서 의존성을 주입하여 관리할 수 있다. 이를 통해 개발자는 객체 간의 결합도를 줄이고, 코드의 유연성과 테스트 가능성을 높일 수 있다.
스프링은 이러한 의존성 주입을 통해 애플리케이션 내의 객체들이 서로 의존하고 협력하는 방식을 관리한다. 개발자는 스프링을 사용하여 객체를 쉽게 생성하고 연결할 수 있으며, 이를 통해 대규모 자바 프로젝트도 보다 효율적으로 관리할 수 있다. 스프링은 마치 레고 블록을 조립하듯, 필요한 객체들을 조합하여 애플리케이션을 구축하는 방식으로, 개발자가 코드의 세부 사항에 신경 쓰지 않고 구조를 더 깔끔하고 유연하게 유지할 수 있도록 돕는다.
스프링 프레임워크와 스프링 개발 커뮤니티의 인상적인 점은 변화하는 기술 환경에 맞춰 지속적으로 관련성을 유지하고, 스스로를 재창조할 수 있는 능력이다. 특히, 많은 개발팀들이 전통적인 모놀리스 애플리케이션에서 벗어나, 점차 클라우드 기반의 작은 서비스들로 구성된 고도로 분산된 아키텍처로 이동하고 있다는 점을 스프링 개발자들은 신속하게 인식했다.
이러한 변화에 대응하기 위해, 스프링 커뮤니티는 스프링 부트와 스프링 클라우드라는 두 가지 프로젝트를 시작했다. 스프링 부트는 빠르고 간편하게 자바 애플리케이션을 배포할 수 있도록 도와주는 툴로, 개발자가 설정과 구성을 최소화하고 비즈니스 로직에 집중할 수 있게 해준다. 스프링 클라우드는 마이크로서비스 아키텍처를 지원하는 다양한 도구와 라이브러리를 제공하며, 클라우드 환경에서의 서비스 간 통신, 서비스 디스커버리, 로드 밸런싱 등을 보다 효율적으로 관리할 수 있도록 한다. 이를 통해 스프링은 클라우드와 마이크로서비스 시대에도 여전히 중요한 역할을 할 수 있는 강력한 도구로 자리잡았다.
스프링 부트는 스프링 프레임워크를 재구성하여, 기존 스프링의 핵심 기능을 유지하면서도 많은 엔터프라이즈 기능을 간소화한 프레임워크다. 스프링 부트는 특히 자바 기반의 REST 지향적인 마이크로서비스를 개발하는 데 최적화되어 있다. 이를 통해 개발자들은 몇 가지 간단한 어노테이션만으로 별도의 애플리케이션 컨테이너 없이 패키징하고 배포할 수 있는 REST 서비스를 신속하게 만들 수 있다. 이와 같은 간편한 설정 덕분에 복잡한 구성 작업 없이도 효율적으로 애플리케이션을 개발할 수 있게 되었으며, 마이크로서비스 아키텍처를 손쉽게 구현할 수 있도록 지원한다.
스프링의 핵심 제품 기능
- 애플리케이션을 배포하는데 복잡함을 줄여주는 내장형 웹 서버: 스프링 부트는 Tomcat(기본), Jetty 또는 Undertow와 같은 여러 웹 서버를 내장하고 있다. 이 웹 서버는 스프링 부트 애플리케이션을 실행하는 데 필요한 핵심 컴포넌트 중 하나로, 애플리케이션을 배포 가능한 JAR 파일로 패키징할 때 포함된다. 이로 인해 별도의 웹 서버를 설정하거나 배포할 필요 없이, 자바만 설치된 서버에서 바로 실행할 수 있다. 즉, 스프링 부트 애플리케이션을 배포하려면 서버에 자바가 설치되어 있으면 충분하며, 이를 통해 애플리케이션을 손쉽게 실행할 수 있다.
- 프로젝트 (스타터 라이브러리)로 빠르게 시작할 수 있는 기본 구성 (configuration)
- 가능하다면 스프링에 대한 기능적으로 자동화된 구성
- 운영 환경에 바로 사용 가능한 다양한 기능 (지표, 보안, 상태 확인, 코드와 분리된 구성 등)
스프링 부트가 마이크로서비스에 제공하는 이점
- 개발 시간 단축, 효율성과 생산성 향상
- 웹 애플리케이션 실행을 위한 내장형 HTTP 서버 제공
- 많은 사용구 (boilerplate) 코드 작성 회피
- 스프링 데이터, 스프링 시큐리티. 스프링 클라우드 같은 스프링 생태계와 통합 용이
- 다양한 개발 플러그인 제공
마이크로서비스는 클라우드 기반 애플리케이션을 구축하기 위한 일반적인 아키텍처 패턴 중 하나로 자리 잡았다. 이에 따라 스프링 개발 커뮤니티는 스프링 클라우드를 개발했다. 스프링 클라우드를 사용하면 사설(private) 또는 공용(public) 클라우드에서 마이크로서비스를 간편하게 운영하고 배포할 수 있다. 스프링 클라우드는 여러 인기 있는 클라우드 관리형 마이크로서비스 프레임워크들을 공통 프레임워크에 통합하여 제공하므로, 개발자는 코드에 어노테이션을 추가하는 것만으로 이러한 기술들을 쉽게 사용할 수 있고 배포할 수 있다.
1.3 우리가 구축할 것은 무엇인가?
생략
1.4 이 책의 내용
- 마이크로서비스의 정의, 모범 사례 및 마이크로서비스 기반 애플리케이션 구축을 위한 설계 고려 사항
- 마이크로서비스 기반 애플리케이션을 구축하면 안되는 경우
- 스프링 부트 프레임워크 사용하여 마이크로서비스를 구축하는 방법
- 마이크로서비스 애플리케이션, 특히 클라우드 기반 애플리케이션을 지원하는 핵심 운영 패턴
- 도커의 정의 및 마이크로서비스 기반 애플리케이션과 통합하는 방법
- 운영 패턴을 구현하는 스프링 클라우드 사용 방법
- 애플리케이션 지표를 만들고 모니터링 도구로 시각화하는 방법
- 집킨과 슬루스 (sleuth)로 분산 추적하는 방법
- ELK 스택으로 애플리케이션 로그를 관리하는 방법
- 서비스 배포 파이프라인 (내부에서 관리하는 사설 클라우드 또는 공용 클라우드 공급자에 배포 가능한)을 구착하는 방법
1.5 클라우드 및 마이크로서비스 기반 애플리케이션
1.5.1 스프링 부트로 마이크로서비스 구축하기
예제 및 예졔 설명이기 때문에 생략
1.5.2 클라우딩 컴퓨팅이란 무엇인가?
클라우드 컴퓨팅은 인터넷을 통해 다양한 가상화된 IT 서비스를 제공하는 기술로, 유연하고 안전하며 사용이 용이한 환경을 제공한다. 이 기술은 데이터베이스, 네트워킹, 소프트웨어, 서버, 분석 등 다양한 서비스에 접근할 수 있게 해준다. 클라우드 컴퓨팅은 낮은 초기 투자 비용, 사용 및 유지 보수의 용이성, 뛰어난 확장성 등의 장점을 통해 기업 내부 관리에서 큰 이점을 제공한다. 사용자는 클라우드 컴퓨팅 모델에 따라 제공되는 정보 및 서비스에 대한 통제 수준을 선택할 수 있으며, 이를 XaaS(Everything as a Service)와 같은 약어로 표현하기도 한다.
- IaaS (Infrastructure as a Service): 공급 업체는 서버, 스토리지, 네트워크와 같은 기본적인 컴퓨팅 자원에 접근할 수 있는 인프라스트럭처를 제공한다. 이 모델에서 사용자는 인프라스트럭처의 유지 관리와 애플리케이션의 확장성에 대한 모든 책임을 지게 된다. IaaS(Infrastructure as a Service) 플랫폼에는 AWS EC2, Azure, Google Compute Engine, Kubernetes 등이 포함됩니다. 이들 서비스는 사용자가 직접 서버를 관리하지 않고도 필요한 컴퓨팅 자원을 유연하게 사용할 수 있도록 해준다.
- CaaS (Container as a Service): CaaS(Containers as a Service)는 IaaS와 PaaS의 중간 모델로, 컨테이너 기반 가상화의 한 형태다. 개발자가 서비스가 배포되는 가상 머신을 관리해야 하는 IaaS 모델과 달리, CaaS를 사용하면 도커와 같은 이식성 높은 가상 컨테이너 내 마이크로서비스를 클라우드 공급자 환경에 쉽게 배포할 수 있다. 클라우드 공급자는 이러한 컨테이너가 실행될 수 있는 가상 서버를 제공하고, 컨테이너의 구축, 배포, 모니터링 및 확장에 필요한 종합적인 도구와 서비스를 제공한다. CaaS 플랫폼의 예로는 Google Kubernetes Engine(GKE)와 Amazon ECS가 있다.
- PaaS (Platform as a Service): 이 모델은 사용자가 애플리케이션의 개발, 실행, 유지 관리에 집중할 수 있는 플랫폼과 환경을 제공한다. 이 모델에서는 애플리케이션을 공급업체가 제공하는 도구들(운영 체제, DBMS, 기술 지원 저장소, 호스팅, 네트워크 등)을 사용하여 생성할 수 있다. 사용자는 물리적 인프라스트럭처에 투자할 필요가 없으며, 이들 도구를 관리하는 데 시간을 소모할 필요도 없다. 따라서 사용자는 애플리케이션 개발에 온전히 집중할 수 있다. 대표적인 PaaS 플랫폼에는 Google App Engine, Cloud Foundry, Heroku, AWS Elastic Beanstalk 등이 있다.
- FaaS (Function as a Service): 서버리스 아키텍처, 또는 FaaS(Function as a Service)는 서버가 없다는 의미에도 불구하고, 서버 관리를 개발자가 직접 하지 않고, 클라우드 공급자가 이를 처리하는 방식이다. 이 아키텍처에서는 확장성, 프로비저닝, 서버 관리에 대한 걱정 없이 코드 실행에만 집중할 수 있다. 즉, 사용자는 관리용 인프라스트럭처를 신경 쓸 필요 없이 필요한 함수나 기능을 클라우드 플랫폼에 업로드하면 된다. 서버리스 아키텍처는 필요에 따라 자동으로 확장되며, 사용한 만큼만 비용을 지불하는 모델로 효율적인 비용 관리가 가능하다. 대표적인 FaaS 플랫폼으로는 AWS Lambda, Google Cloud Functions, Azure Functions 등이 있다.
- SaaS (Software as a Service): 주문형 소프트웨어 모델로, 사용자가 애플리케이션을 배포하거나 유지 관리할 필요 없이 바로 사용할 수 있는 서비스다. SaaS는 웹 브라우저를 통해 접근할 수 있으며, 애플리케이션, 데이터, 운영 체제, 가상화, 서버, 저장소, 네트워크 등 모든 인프라와 소프트웨어 관리가 서비스 제공자에 의해 처리된다. 사용자는 소프트웨어의 설치나 유지 관리에 신경 쓸 필요 없이, 제공되는 서비스만 이용하면 된다. 대표적인 SaaS 플랫폼으로는 Salesforce, SAP, Google Workspace(구 Google Business) 등이 있다.
1.5.3 왜 클라우드와 마이크로서비스인가?
마이크로서비스 아키텍처의 핵심 개념 중 하나는 각 서비스가 독립적인 단위로 분리되어 패키징되고 배포된다는 것이다. 각 서비스는 독립적으로 실행되며 다른 서비스와 구별되어야 하며, 서비스 인스턴스는 빠르게 시작할 수 있어야 한다. 마이크로서비스를 설계할 때는 어떤 형태로 배포할지에 대한 결정이 중요하다.
- 물리 서버: 마이크로서비스를 물리적 서버에 배포하는 것도 가능하지만, 많은 조직은 이러한 방식을 택하지 않는다. 이는 물리 서버의 제한된 자원과 용량 확장의 어려움 때문이다. 또한, 여러 물리 서버에 걸쳐 마이크로서비스를 수평적으로 확장하려면 상당한 비용이 발생할 수 있다.
- 가상 머신 이미지: 마이크로서비스의 중요한 이점 중 하나는 인스턴스를 빠르게 시작하고 종료하여 확장성과 서비스 실패 상황에 유연하게 대응할 수 있다는 점이다. 이와 같은 특성을 지원하기 위해 가상 머신은 주요 클라우드 제공업체의 핵심 인프라 역할을 한다.
- 가성 컨테이너: 가상 컨테이너는 마이크로서비스를 VM 이미지 대신 배포하기 위한 자연스러운 진화다. 많은 개발자가 도커 같은 컨테이너 기술을 활용해 클라우드 환경에 서비스를 배포한다. 가상 컨테이너는 VM 내부에서 실행되며, 동일한 이미지를 기반으로 여러 독립적인 프로세스를 실행할 수 있게 해준다. 이를 통해 마이크로서비스를 패키징하고, IaaS 기반의 사설 또는 공용 클라우드에 다수의 서비스 인스턴스를 빠르게 배포하고 실행할 수 있다.
클라우드 기반 마이크로서비스의 장점은 탄력성 (elasticity)을 바탕으로 합니다. 클라우드 서비스 제공자는 몇 분 내로 새로운 VM과 컨테이너를 시작할 수 있게 지원하며, 용량 요구가 줄어들면 불필요한 비용을 피하기 위해 컨테이너를 줄일 수도 있습니다. 클라우드를 통해 마이크로서비스를 배포하면 서버와 서비스 인스턴스를 추가하여 애플리케이션의 수평적 확장성을 크게 향상시킬 수 있습니다.
서버 탄력성은 애플리케이션이 더 높은 복원력을 가질 수 있음을 의미한다. 특정 마이크로서비스에 문제가 발생해 실패하더라도 새로운 서비스 인스턴스를 신속히 기동하여, 개발 팀이 근본적인 문제를 해결하기 전에 애플리케이션이 정상적으로 작동할 수 있도록 유지할 수 있다.
CaaS 클라우드 공급자의 일반적인 특징
- 간소화된 인프라스트럭처 관리: CaaS 클라우드 공급자는 우리에게 서비스에 대한 더 많은 통제 기능을 제공한다. 간단한 API 호출로도 새로운 서비스를 시작하고 중지할 수 있다.
- 대규모 수평적 확장: CaaS 클라우드 공급자를 통해 빠르고 간결하고 하나 이상의 인스턴스를 시작할 수 있다. 이 특징은 서비스를 신속하게 할 수 있고, 오작동하거나 장애가 있는 서버를 우회할 수 있다는 것을 의미한다.
- 지리적 분산을 이용한 높은 중복성 (redundancy): 필요에 따라 CaaS 공급자는 여러 데이터센터를 보유한다. 이 공급자를 통해 마이크로서비스를 배포하면 데이터 센터에서 클러스터를 사용하는 것보다 더 높은 수준의 중복성을 얻을 수 있다.
1.6 마이크로서비스는 코드 작성 이상을 의미한다
개별 마이크로서비스 구축과 관련된 개념은 상대적으로 이해하기 쉬운 편이지만, 견고한 마이크로서비스 애플리케이션을 구축하고 실행하며 지원하는 것은 그 이상의 작업을 요구한다. 특히 클라우드 환경에서 마이크로서비스를 실행할 때는 단순히 서비스 코드를 작성하는 것 외에도 여러 가지 중요한 요소를 고려해야 한다. 여기에는 서비스의 배포, 모니터링, 자동화된 확장성 관리, 장애 처리, 보안 등 다양한 요소가 포함된다. 마이크로서비스 아키텍처는 분산 시스템을 기반으로 하기 때문에 이러한 복잡한 요구 사항을 충족하기 위한 추가적인 도전과제를 해결해야 한다.
- 적종 규모 (right-sized): 적절한 마이크로서비스 크기를 유지하는 것은 중요하다. 너무 크면 관리가 어려워지고, 너무 작으면 서비스 간의 상호작용이 과도해져 복잡성이 증가할 수 있다. 적당한 크기의 서비스를 사용하면 애플리케이션의 변경을 신속하게 처리할 수 있으며, 전체 애플리케이션에 대한 위험을 분산시킬 수 있다.
- 위치 투명성 (location transpancnt): 서비스 호출에 대한 물리적 상세 정보를 관리하는 방법이다. 마이크로서비스 애플리케이션에서 다수의 서비스 인스턴스가 빠르게 시작하고 종료될 수 있다.
- 회복성 (resilient): 실패한 서비스를 우회하고 "빠른 실패" 방식을 적용하여 마이크로서비스 소비자와 애플리케이션의 전반적인 무결성을 보호하는 방법이다.
- 반복성 (repeatable): 서비스의 모든 새 인스턴스가 시작할 때 운영 환경의 다른 서비스와 동일한 구성과 코드 베이스를 보장하는 방법이다.
- 확장성 (scalable): 서비스간 직접적인 종속 관계를 최소화하고 마이크로서비스를 적절히 확장할 수 있도록 통신 방식을 구축하는 방법이다.
1.7 핵심 마이크로서비스 개발 패턴
핵심 마이크로서비스 개발 패턴은 마이크로서비스 구축에 대한 기본 사항을 다룬다.
- 서비스 세분성 (service granularity): 서비스를 서로 다른 비즈니스 문제 도메인의 책임과 중첩된 절도로 지나치게 크게 나누면 시간이 지나 유지 관리하고 변경하기 어렵다 반면 서비스를 너무 세분화하면 애플리케이션의 복잡성이 전반적으로 높아지고 데이터 저장소에 액세스하는 것 외에 다른 로직이 없는 멍청한 서비스가 된다, 자세한 내용은 3장의 세버스 세분성에서 다룬다.
- 통신 프로토콜 (communication protocols): 동기 프로토콜의 경우 XML, JSON, 쓰리포트 (Thrift) 같은 바이너리 프로토콜을 사용하는 HTTP 기반 REST가 일반적인 통신이다. 비동기 프로토콜의 경우 RabbitMQ, 아파치 카프카, 아마존 SQS (Simple Queue Service) 같은 메시지 브로커를 통해 일대일 혹은 일대다 통신을 하는 AMQP (Advanced Message Queuing Protocol)가 가장 일반적인 프로토콜이다.
- 인터페이스 설계 (interface design): 자세한 내용은 2장의 모범 사례와 인터페이스 설계에서 다룬다.
- 서비스 구성 관리 (configuration mangement of service): 자세한 내용은 5장의 외부화된 구상과 프로파일로 구성과 프로파일로 관리에서 다룬다.
- 서비스간 이벤트 처리 (event processing between services): 자세한 내용은 10장의 스프링 클라우드 시스템 이벤트 기반 아키텍처에서 다룬다.
1.8 마이크로서비스 라우팅 패턴
마이크로서비스 라우팅 패턴은 클라이언트 애플리케이션이 마이크로서비스를 발견하고 요청을 해당 서비스로 라우팅하는 방법에 관한 것이다. 클라우드 환경에서는 수백 개의 마이크로서비스가 동시에 실행될 수 있으므로, 각 서비스의 물리적 IP를 추상화하고 보안 및 콘텐츠 정책을 적용하기 위해 모든 서비스에 대한 단일 진입점이 필요하다. 이를 통해 클라이언트는 서비스의 위치를 신경 쓸 필요 없이 효율적으로 요청을 처리할 수 있다.
- 서비스 디스커버리 (service discovery): 서비스 디스커버리는 마이크로서비스 아키텍처에서 중요한 역할을 하며, 핵심 기능인 서비스 레지스트리를 통해 마이크로서비스가 동적으로 탐색 가능하다. 이를 통해 클라이언트 애플리케이션은 서비스의 위치를 하드코딩할 필요 없이, 서비스 디스커버리 메커니즘을 통해 필요한 서비스를 자동으로 찾을 수 있다. 서비스 디스커버리는 클라이언트 애플리케이션뿐만 아니라, 서비스 간의 통신을 위한 내부적 서비스 탐색에도 활용된다.
- 서비스 라우팅 (service rounting): API 게이트웨이를 사용하면 마이크로서비스 애플리케이션에 대해 모든 서비스에 대한 단일 진입점을 제공할 수 있다. 이를 통해 마이크로서비스 간의 라우팅을 간소화하고, 여러 서비스에 대해 일관된 보안 정책과 라우팅 규칙을 적용할 수 있다.
1.9 마이크로서비스 클라이언트 회복성
마이크로서비스 아키텍처는 고도로 분산된 특성으로 인해, 하나의 서비스나 서비스 인스턴스에서 발생한 문제가 다른 서비스 소비자에게 연쇄적으로 전파되지 않도록 세심한 주의가 필요하다. 각 서비스가 독립적으로 운영되지만, 상호 연결된 환경에서는 개별 문제도 전체 시스템에 영향을 미칠 가능성이 있으므로 이를 방지하는 설계가 중요하다.
- 클라이언트 부하 분산 (client-side load balancing): 마이크로서비스의 여러 인스턴스에 대한 호출이 정상 인스턴스에 분산되도록 서비스 인스턴스 위치를 캐싱하는 방법이다.
- 회로 차단기 패턴 (circuit breaker pattern): 문제가 있거나 성능 저하를 겪는 서비스를 반복적으로 호출하지 않도록 설계한다. 서비스가 느려지면 호출한 클라이언트는 불필요하게 리소스를 오래 사용하게 된다. 이를 방지하기 위해 마이크로서비스 호출이 신속하게 실패하도록 설정하여 클라이언트가 더 빠르게 반응하고 적절한 조치를 취할 수 있도록 한다.
- 폴백 패턴 (fallback pattern): 마이크로서비스 호출이 실패할 때 호출되는 마이크로서비스가 아닌 다른 수단으로 서비스 클라이언트가 작업을 수행하도록 플로그인 메커니즘을 제공하는 방법이다.
- 벌크헤드 / 격벽 패턴 (bulkhead pattern): 마이크로서비스 애플리케이션은 작업을 수행하기 위해 다양한 분산 지원을 활용한다. 이 방식은 한 서비스에서 발생한 오작동 호출이 애플리케이션의 다른 부분에 부정적인 영향을 미치지 않도록 호출을 분리하고 격리하는 방법을 제공한다.
1.10 마이크로서비스 보안 패턴
대중에게 마이크로서비스 노출을 피하려면 적잘한 자격 증명을 가진 승인된 요청만 서비스를 호출할 수 있도록 다음 보안 패턴을 아키텍처에 적용하는 것이 중요하다.
- 인증 (authentication): 서비스를 호출하는 서비스 클라이언트가 누구인지 확인하는 방법
- 인가 / 권한부여 (authorization): 마이크로서비스를 호출하는 서비스 클라이언트가 수행하려는 행동에 대한 수행 자격 여부를 확인하는 방법
- 자격 증명 관리와 전파 (credential mangement and propagation): 서비스 클라이언트가 한 트랜잭션에서 여러 서비스를 호출할 때 계속해서 자격 증명을 제시하지 않는 방법이다. 사용자를 인증 및 인가하고자 서비스 호출 간 전달되는 토큰을 발급받을 수 있는QAuth2와 JWT과 같은 토큰 기반의 보안 표준 방법이 있다.
1.11 마이크로서비스 로깅과 추적 패턴
마이크로서비스 아키텍처의 단점 중 하나는 단순한 작업을 수행하기 위해 여러 마이크로서비스 호출이 발생하므로 문제를 디버깅하거나 추적하고 모니터링하는 과정이 훨씬 복잡해진다는 단점이 있다.
스프링 클라우드 슬루스 (Sleuth), 집킨 (Zipkin), ELK 스택을 활용한 분산 추적 패턴을 구현하기 위해선 아래의 세 가지 핵심 로깅 및 추적 패턴을 사용해야 한다.
- 로그 상관관계 (log correlatiopn): 사용자의 단일 트랜잭션에서 여러 서비스가 생성한 로그를 연결하려면 상관관계(correlation) ID를 구현하는 방법을 사용할 수 있다. 이 ID는 특정 트랜잭션을 고유하게 식별하며, 모든 서비스 호출에 전달되어 각 서비스에서 생성된 로그 항목을 한데 묶는 데 활용된다.
- 로그 수집 (log aggregation): 이 패턴을 사용하여 마이크로서비스 (모든 인스턴스)가 출력한 모든 로그를 질의 (query) 가능한 단일 데이터베이스로 수집하고 트랜잭션 내 서비스 성능 특성을 이해할 수 있다.
- 마이크로서비스 추적 (microservices tracing): 트랜잭션과 관련된 모든 서비스 간 클라이언트 트랜잭션 흐름을 시작화하고 성능 특성을 살펴볼 것이다.
1.12 애플리케이션 지표 패턴
애플리케이션 지표(metrics) 패턴은 애플리케이션이 지표를 모니터링하고 가능한 실패 원인을 경고하는 방법을 설명한다. 이 패턴은 애플리케이션의 성능을 추적하고 문제를 사전에 감지하여 서비스의 잠재적인 성능 문제를 예방하려는 목적을 가지고 있다. 지표 서비스는 비즈니스와 관련된 데이터를 수집(스크래핑), 저장, 질의하는 역할을 하며, 이러한 데이터를 기반으로 시스템의 상태를 실시간으로 모니터링할 수 있다. 이를 통해 문제가 발생하기 전에 경고를 보내고, 시스템의 장애를 예방하거나 빠르게 대응할 수 있게 도와준다.
- 지표: 애플리케이션 상태에 대해 중요한 정보를 생성하고 이 정보의 지표를 노출하는 방법이다.
- 지표 서비스: 애플리케이션 지표를 저장하고 질의하는 곳이다.
- 지표 시각하 제품군 (metrics visualization suite): 애플리케이션과 인프라스트럭처에 대해 비즈니스와 연관된 시계열 데이터를 시작화한다.
지표 서비스는 풀 (pull) 또는 푸시 (push) 방식을 사용하여 지표를 가져온다.
- 푸시 방식은 애플리케이션 데이터를 전송하려고 서비스 인스턴스가 지표 서비스가 제공한 서비스 API를 호출하는 것이다.
- 풀 방식은 지표 서비스가 애플리케이션 데이터를 가져오는 함수에 요청하거나 질의하는 것이다.
지표 모니터링은 마이크로서비스 아키텍처에 팔수적이며, 마이크로서비스는 높은 분산성으로 인해 이러한 아키텍체에 대한 요구 사항은 모놀리식 구조보다 더 높은 경향이 있음을 이해하는 것이 중요하다.
1.13 마이크로서비스 빌드 / 배포 패턴
마이크로서비스 아키텍처의 핵심 중 하나는 각 마이크로서비스 인스턴스가 동일해야 한다는 점이다. 서버가 배포된 후 발생할 수 있는 구성 불일치 문제는 애플리케이션의 안정성을 위협할 수 있기 때문에, 이를 방지하는 것이 매우 중요하다. 마이크로서비스의 일관된 실행 환경을 유지하기 위해서는 인프라스트럭처 및 애플리케이션의 구성을 자동화하고 관리하는 방식이 필요합니다. 이를 통해 서버 변경으로 인한 문제를 최소화할 수 있다.
이 패턴의 목적은 인프라 구성을 빌드 및 배포 절차에 포함시켜, 자바 WAR나 EAR 파일 같은 소프트웨어 산출물을 기존의 운영 중인 인프라에 더 이상 추가로 배포하지 않는 것이다. 대신, 빌드 과정에서 마이크로서비스와 가상 서버 이미지를 생성하고 컴파일한다. 그런 다음, 마이크로서비스가 배포되면 전체 서버의 머신 이미지가 함께 배포되어 실행된다.
- 빌드 및 배포 파이프라인 (build and deployment pipelines): 조직의 모든 환경에서 원 버튼 클릭 빌드와 배포를 중시하는 반복적인 빌드 및 배포 프로세스를 구축하는 방법이다.
- 코드형 인프라스트럭처 (infranstructure as code): 소스 제어로 실행되고 관리되는 서비스 프로비저닝 처리 방법이다.
- 불변 서버 (immutable servers): 마이크로서비스 이미지가 생성되고 배포된 후 절대 변경되지 않도록 하는 방법이다.
- 피닉스 서버 (phoenix servers): 각 컨테이너를 실행하는 서버가 주기적으로 분해되어 불변 이미지를 기반으로 재생성되는 방식이다. 서버가 오랜 시간 실행될수록 구성 불일치가 발생할 가능성이 커진다. 구성 불일치는 시스템 구성 정보에 대한 임의적인 변경이 기록되지 않을 때 발생한다.
이들 패턴과 주제의 목표는 구성 불일치가 스테이지나 운영 환경 등 상위 환경에서 발생하지 않도록 사전에 과감히 노출하고 근절하는 것이다.
'스프링 마이크로서비스' 카테고리의 다른 글
6장. 서비스 디스커버리 (2) | 2024.12.12 |
---|---|
5장. 스프링 클라우드 컨피그 서버로 구성 관리 (0) | 2024.12.12 |
4장. 도커 (0) | 2024.12.11 |
3장. 스프링 부트로 마이크로서비스 구축하기 (1) | 2024.12.10 |
2장. 스프링 클라우드와 함께 마이크로서비스 세계 탐험 (4) | 2024.12.10 |