마이크로서비스를 구축하는 방법
마이크로서비스 아키텍처로 전환하는 모범 사례
Sten Pittet
제품 매니저
하나의 코드로 구축되었으며 상당히 크고 모놀리식인 애플리케이션이 있다고 가정해 보겠습니다. 처음에는 잘 작동했지만 나중에는 문제가 생겼습니다. 애플리케이션이 진화하여 더 복원력이 있고 확장 가능하며 독립적으로 배포 가능하기를 바랍니다. 그러기 위해서는 세분화된 마이크로서비스 수준에서 애플리케이션 구조를 재고해야 합니다.
애플리케이션이 더욱 분산되고 복잡해지면서 마이크로서비스의 인기가 높아지고 있습니다. 마이크로서비스의 기본 원칙은 애플리케이션의 비즈니스 구성 요소를 서로 독립적으로 배포 및 운영할 수 있는 소규모의 서비스로 분할하여 애플리케이션을 구축하는 것입니다. 서비스 간 우려 사항의 분리를 “서비스 경계”라고 정의합니다.
서비스 경계는 비즈니스 요구 사항 및 조직의 계층 구조 경계와 밀접한 관련이 있습니다. 개별 서비스를 별도의 팀, 예산 및 로드맵에 연결할 수 있습니다. 서비스 경계의 예로는 “결제 처리” 및 “사용자 인증” 서비스를 들 수 있습니다. 마이크로서비스는 모든 구성 요소를 함께 번들로 제공하는 레거시 소프트웨어 개발 관행과 다릅니다.
이 문서에서는 “Pizzup”이라는 가상의 피자 스타트업을 참조하여 최신 소프트웨어 비즈니스에 마이크로서비스를 적용하는 방법을 설명합니다.
무료로 Compass 사용해 보기
개발자 경험을 개선하고 모든 서비스를 카탈로그화하고 소프트웨어 상태를 향상하세요.
마이크로서비스를 구축하는 방법
1단계: 모놀리스로 시작
마이크로서비스의 첫 번째 모범 사례는 마이크로서비스가 필요하지 않을 수도 있다는 것입니다. 애플리케이션의 사용자가 없는 경우 MVP를 구축하는 동안 비즈니스 요구 사항이 빠르게 변할 가능성이 있습니다. 이것은 단순히 소프트웨어 개발의 특성을 비롯해 시스템이 제공해야 하는 주요 비즈니스 기능을 식별하는 동안 발생해야 하는 피드백 주기 때문입니다. 마이크로서비스는 기하급수적인 오버헤드와 관리 복잡성을 가중시킵니다. 따라서 새로운 프로젝트에서 모든 코드와 로직을 단일 코드베이스 내에 보관하면 애플리케이션의 서로 다른 모듈의 경계를 더 쉽게 이동할 수 있으므로 훨씬 적은 오버헤드가 발생합니다.
예를 들어, Pizzup의 경우 고객이 온라인으로 피자를 주문할 수 있도록 하려는 간단한 문제부터 시작해 보겠습니다.
관련 자료
마이크로서비스와 모놀리식 아키텍처 비교
솔루션 보기
Compass로 DevEx 개선
피자 주문 문제에 대해 생각하면서 이러한 요구 사항을 충족하기 위해 애플리케이션에 필요한 다양한 기능을 파악합니다. 만들 수 있는 다양한 피자의 목록을 관리하고, 고객이 하나 이상의 피자를 고르도록 하고, 결제를 처리하고, 배달을 예약하는 등의 작업을 수행해야 합니다. 고객이 계정을 만들 수 있도록 하면 다음에 Pizzup을 이용할 때 다시 주문하기 쉬울 것으로 생각할 수 있습니다. 첫 번째 사용자와 이야기를 나눈 후, 배달에 대한 실시간 추적과 모바일 지원을 제공하면 경쟁 업체보다 우위를 점할 수 있다는 것도 알게 될 수 있습니다.
처음에는 단순한 요구 사항에 불과했던 것이 순식간에 새로운 기능의 목록이 되었습니다.
마이크로서비스는 시스템에 필요한 다양한 서비스를 확실히 파악하고 있을 때 효과적입니다. 그러나 애플리케이션의 핵심 요구 사항이 제대로 정의되지 않은 경우 마이크로서비스를 활용하기가 훨씬 더 어렵습니다. 조정이 필요한 변동 사항이 일반적으로 더 많기 때문에, 마이크로서비스에서 서비스 상호 작용, API 및 데이터 구조를 다시 정의하는 데는 상당한 비용이 듭니다. 저희의 조언은, 고객의 기본적인 요구 사항을 이해하고 그에 따라 계획한다는 확신을 줄 수 있도록 충분한 사용자 피드백을 수집할 때까지 작업을 단순하게 유지하는 것이 좋습니다.
주의 사항은 모놀리스를 구축하면 코드가 빠르게 복잡해져 더 작은 조각으로 나누기 어려워질 수 있습니다. 나중에 모놀리스에서 추출할 수 있도록 명확한 모듈을 식별하는 것이 가장 좋습니다. 웹 UI에서 로직을 분리하고 HTTP를 통한 RESTful API를 통해 백엔드와 상호 작용하는지 확인하는 것부터 시작할 수도 있습니다. 이렇게 하면 API 리소스를 다른 서비스로 마이그레이션할 때 마이크로서비스로 보다 쉽게 전환할 수 있습니다.
2단계: 올바른 방식으로 팀 구성
지금까지는 마이크로서비스를 구축하는 것이 대부분 기술적인 일인 것처럼 보일 수 있습니다. 코드베이스를 여러 서비스로 나누고, 적절한 패턴을 구현하여 네트워크 문제에서 안정적으로 종료 및 복구하고, 데이터 일관성을 다루며, 서비스 부하를 모니터링하는 등의 작업이 필요합니다. 파악해야 할 새로운 개념이 많을 것입니다. 하지만 간과해서는 안 되는 가장 중요한 점은 팀의 구성 방식을 다시 구성해야 한다는 것입니다.
콘웨이의 법칙은 실제로 작용되며 모든 유형의 팀에서 볼 수 있습니다. 소프트웨어 팀이 백엔드 팀, 프런트엔드 팀 및 독립적으로 작업하는 운영 팀으로 구성된 경우, 프로덕션에 제공하기 위해 부서 외부로 전달하는 별도의 프런트엔드 및 백엔드 모놀리스를 제공할 것입니다. 각 서비스는 다른 서비스와 독립적으로 제공하는 자체 제품처럼 다뤄야 하기 때문에, 이러한 유형의 팀 구조는 마이크로서비스에 적합하지 않습니다.
대신에 담당하는 서비스를 개발하고 유지 관리하는 데 필요한 모든 역량을 갖춘 소규모 DevOps 팀을 만들어야 합니다. 이렇게 팀을 구성하면 큰 장점이 있습니다. 우선 개발자는 코드가 프로덕션에 미치는 영향을 더 잘 이해할 수 있으므로 더 나은 릴리스를 만들고 고객에게 문제가 릴리스되는 위험을 줄일 수 있습니다. 두 번째로, 배포 파이프라인의 자동화뿐만 아니라 코드 개선을 위해서도 함께 작업하기 때문에 각 팀에서 배포가 손쉽게 이루어집니다.
3단계: 모놀리스를 분할하여 마이크로서비스 아키텍처 구축
서비스의 경계를 파악하고 팀을 다시 구성하는 방법을 파악했다면 모놀리스를 분할하여 마이크로서비스를 구축할 수 있습니다. 이 시점에 고려해야 할 핵심 사항은 다음과 같습니다.
RESTful API를 사용하여 서비스 간 통신을 단순하게 유지
아직 RESTful API를 사용하고 있지 않은 경우 지금이 채택하기에 좋은 시점입니다. Martin Fowler의 설명과 같이, "스마트 엔드포인트와 덤 파이프"가 있어야 합니다. 즉, 서비스 간의 통신 프로토콜은 가능한 한 단순해야 하며 데이터를 변환하지 않고 데이터 전송만 담당해야 합니다. 실제 작업은 엔드포인트 자체에서 일어납니다. 즉, 요청을 수신하고 처리한 다음 그에 대한 응답을 내보냅니다.
마이크로서비스 아키텍처에서는 구성 요소의 긴밀한 결합을 피하기 위해 최대한 간단하게 유지하려고 노력합니다. 경우에 따라 비동기식 메시지 기반 통신과 함께 이벤트 중심의 아키텍처를 사용할 수 있습니다. 하지만 다시 한 번 말하지만 RabbitMQ와 같은 기본 메시지 큐 서비스를 살펴보고 네트워크를 통해 전송되는 메시지를 더 복잡하게 만들지 않아야 합니다.
데이터를 제한된 컨텍스트 또는 데이터 도메인으로 분할
모놀리스 애플리케이션은 애플리케이션의 모든 비즈니스 기능에 대해 단일 데이터베이스를 사용합니다. 모놀리스가 마이크로서비스로 분할됨에 따라 이 단일 데이터베이스는 더 이상 효과적이지 않을 수 있습니다. 중앙 집중식 데이터베이스는 트래픽 확장에서 병목 현상을 유발할 수 있습니다. 특정 서비스가 높은 부하로 데이터베이스에 액세스하면 다른 서비스의 데이터베이스 액세스가 중단될 수 있습니다. 또한 단일 데이터베이스는 스키마를 동시에 수정하려는 여러 팀에 공동 작업 병목 현상을 일으킬 수 있습니다. 마이크로서비스 데이터 요구 사항을 지원하기 위해 데이터베이스를 분할하거나 더 많은 데이터 스토리지 도구를 추가해야 할 수 있습니다.
모놀리식 데이터베이스 스키마를 리팩터링하는 것은 정교한 작업이 될 수 있습니다. 각 서비스에 어떤 데이터 세트가 필요한지, 그리고 중복되는 데이터 세트가 있는지 명확하게 식별하는 것이 중요합니다. 이 스키마 계획은 도메인 중심 설계의 한 패턴인 제한된 컨텍스트를 사용하여 할 수 있습니다. 제한된 컨텍스트는 해당 시스템에 들어가고 나갈 수 있는 항목을 포함하여 자립적인 시스템을 정의합니다.
이 시스템에서는 사용자가 주문에 액세스할 때 테이블에서 고객 정보를 볼 수 있으며, 이것을 사용하여 청구 시스템에서 관리하는 인보이스를 채울 수도 있습니다. 논리적이고 단순해 보일 수 있지만 마이크로서비스에서는 주문 시스템이 중지되더라도 인보이스에 액세스할 수 있도록 서비스를 분리해야 합니다. 또한 이 방법을 사용하면 다른 테이블과 독립적으로 인보이스 테이블을 최적화하거나 발전시킬 수 있습니다. 결과적으로 각 서비스에는 필요한 데이터에 액세스할 수 있는 자체 데이터 스토리지가 생길 수 있습니다.
그러면 일부 데이터가 다른 데이터베이스에서 복제되므로 새로운 문제가 발생합니다. 제한된 컨텍스트는 공유 또는 중복 데이터를 처리하기 위한 최상의 전략을 식별할 수 있습니다. 여러 서비스 간에 데이터를 동기화하는 데 도움이 되는 이벤트 중심의 아키텍처를 채택할 수 있습니다. 예를 들어, 청구 및 배달 추적 서비스는 고객이 개인 정보를 업데이트할 때 계정 서비스에서 송신하는 이벤트를 수신할 수 있습니다. 이벤트를 수신하면 서비스는 그에 따라 데이터 저장소를 업데이트합니다. 이벤트 기반 아키텍처를 사용하면 다른 종속 서비스를 알 필요가 없기 때문에 계정 서비스 로직을 단순하게 유지할 수 있습니다. 단순히 시스템에 수행한 작업을 알려주고 다른 서비스는 이것에 따라 수신 및 작동합니다.
또한 모든 고객 정보를 계정 서비스에 보관하고 청구 및 배달 서비스에 외래 키 참조만 유지하도록 선택할 수도 있습니다. 그러면 서비스는 기존 레코드를 복제하는 대신 계정 서비스와 상호 작용하여 관련 고객 데이터를 가져옵니다. 이러한 문제에 대한 보편적인 해결책은 없으므로 각 특정 사례를 살펴보고 가장 좋은 접근 방식을 결정해야 합니다.
장애에 대비하여 마이크로서비스 아키텍처 구축
마이크로서비스가 모놀리식 아키텍처에 비해 얼마나 큰 이점을 제공할 수 있는지 살펴봤습니다. 마이크로서비스는 크기가 작고 특화되어 있으므로 이해하기 쉽습니다. 분리되어 있으므로 시스템의 다른 구성 요소를 손상시키거나 다른 팀의 개발 속도를 늦출 걱정 없이 서비스를 리팩토링할 수 있습니다. 또한 다른 서비스의 요구 사항에 제약을 받지 않고 필요 시 다른 기술을 선택할 수 있으므로 개발자에게 더 높은 유연성을 제공합니다.
즉, 마이크로서비스 아키텍처를 사용하면 각 비즈니스 기능을 더 쉽게 개발하고 유지 관리할 수 있습니다. 그러나 모든 서비스 및 작업을 완료하기 위해 상호 작용해야 하는 방식을 살펴보면 상황이 더 복잡해집니다. 이제 시스템이 여러 장애 지점과 함께 분산되었으므로 이에 대처해야 합니다. 서비스가 응답하지 않는 경우를 고려해야 할 뿐 아니라 느린 네트워크 응답도 처리해야 합니다. 서비스가 다시 온라인 상태가 될 때 보류 중인 메시지가 밀려들어오지 않도록 해야 하므로, 장애로부터 복구하는 것도 경우에 따라 까다로울 수 있습니다.
모놀리식 시스템에서 기능을 추출하기 시작할 때는 처음부터 장애에 대비하여 설계해야 합니다.
마이크로서비스 테스트가 간편하도록 모니터링을 강조
모놀리식 시스템과 비교하여 마이크로서비스의 또 다른 단점은 바로 테스트입니다. 단일 코드베이스로 구축한 애플리케이션은 테스트 환경을 가동하고 실행하는 데 많은 것이 필요하지 않습니다. 대부분의 경우 테스트 모음을 실행하려면 데이터베이스와 결합된 백엔드 서버를 시작해야 합니다.
마이크로서비스 환경에서는 일이 그렇게 쉽지 않습니다. 단위 테스트의 경우 아직 모놀리스와 매우 비슷하므로 이 수준에서는 크게 어렵지 않을 것입니다. 하지만 통합 및 시스템 테스트의 경우 훨씬 어렵습니다. 여러 서비스를 함께 시작하고 서로 다른 데이터 스토리지를 가동 및 실행하고 모놀리스에서는 필요하지 않았던 메시지 큐를 설정에 포함해야 할 수 있습니다. 이 상황에서는 기능 테스트를 실행하는 데 훨씬 더 많은 비용이 들며 변동 사항의 수가 많아지면서 발생 가능한 다양한 유형의 장애를 예측하기가 매우 어렵습니다.
모니터링을 통해 문제를 조기에 식별하고 대응할 수 있습니다. 다양한 서비스의 기준치를 이해하고 서비스가 중단될 때뿐만 아니라 예기치 않게 작동할 때도 대응해야 합니다. 마이크로서비스 아키텍처를 채택하는 것의 장점은 시스템이 부분적인 장애에 대해 복원력이 있다는 것입니다. 따라서 Pizzup 애플리케이션의 배달 추적 서비스에서 이상이 발생하기 시작하면 모놀리식 시스템의 경우보다 상황이 나을 것입니다. 실시간 추적을 복원하는 동안 다른 모든 서비스가 올바르게 응답하고 고객이 피자를 주문할 수 있도록 애플리케이션을 설계해야 합니다.
지속적 배포를 도입하여 배포 마찰 줄이기
모놀리식 시스템을 프로덕션에 수동으로 릴리스하는 것은 지루하고 위험하지만 가능한 작업입니다. 물론 추천하는 접근 방식은 아니며 모든 소프트웨어 팀이 모든 유형의 개발에 지속적 배포를 도입하도록 권장하지만, 프로젝트를 시작할 때 명령줄을 통해 첫 번째 배포를 수행할 수도 있습니다.
하루에 여러 번 배포해야 하는 서비스의 개수가 늘어나는 경우 이 접근 방식은 지속 가능하지 않습니다. 따라서 마이크로서비스로의 전환의 일부로, 릴리스 장애의 위험을 줄이기 위해 지속적 배포를 도입하고, 팀이 애플리케이션 배포에 얽매이지 않고 구축 및 실행에 집중할 수 있도록 하는 것이 중요합니다. 지속적 배포를 실천한다는 것은 서비스가 프로덕션 단계로 이동하기 전에 승인 테스트를 통과했음을 의미합니다. 물론 버그가 발생하지만 시간이 지남에 따라 릴리스 품질에 대한 팀의 자신감을 높여주는 강력한 테스트 모음을 만들 것입니다.
마이크로서비스 실행은 긴 여정
마이크로서비스는 널리 사용 및 채택되는 업계 모범 사례입니다. 복잡한 프로젝트의 경우 소프트웨어 구축 및 배포에 더 높은 유연성을 제공합니다. 또한 시스템의 비즈니스 컴포넌트를 식별하고 공식화하므로 여러 팀이 동일한 애플리케이션에서 작업할 때 유용합니다. 그러나 분산 시스템을 관리하는 데에는 몇 가지 분명한 단점이 있으며 모놀리식 아키텍처 분할은 서비스 경계를 명확하게 이해할 수 있는 경우에만 수행해야 합니다.
마이크로서비스 구축은 팀의 즉각적인 목표가 아니라 여정이라고 생각해야 합니다. 소규모로 시작하여 분산 시스템의 기술적 요구 사항을 이해하고 시스템을 안정적으로 종료하고 개별 구성 요소를 확장합니다. 그리고 경험과 지식을 쌓으면서 점차 더 많은 서비스를 추출할 수 있습니다.
마이크로서비스 아키텍처로의 마이그레이션을 한 번의 전체적인 노력을 통해 완료할 필요는 없습니다. 더 작은 구성 요소를 마이크로서비스로 순차적으로 마이그레이션하는 반복 전략이 더 안전한 방법입니다. 확립된 모놀리스 애플리케이션 내에서 가장 잘 정의된 서비스 경계를 식별하고 이것을 자체 마이크로서비스로 분리하기 위해 반복적으로 작업합니다.
결론...
요약하면, 마이크로서비스는 원시 기술 코드 개발 프로세스와 전반적인 비즈니스 조직 전략 모두에 도움이 되는 전략입니다. 마이크로서비스는 팀을 특정 비즈니스 기능을 개발하고 소유하는 데 중점을 둔 단위로 구성하는 데 도움이 됩니다. 이렇게 세분화된 부분에 초점을 맞추면 전반적인 비즈니스 커뮤니케이션과 효율성이 향상됩니다. 마이크로서비스의 장점에는 단점도 따릅니다. 마이크로서비스 아키텍처로 마이그레이션하기 전에 서비스 경계를 명확하게 정의하는 것이 중요합니다.
마이크로서비스 아키텍처에는 수많은 장점이 있지만 복잡성도 높아집니다. Atlassian은 회사가 확장할 때 분산 아키텍처의 복잡성을 관리할 수 있도록 Compass를 개발했습니다. Compass는 확장 가능한 개발자 경험 플랫폼으로, 모든 엔지니어링 결과물에 대한 단절된 정보와 팀 차원의 공동 작업을 검색 가능한 중앙 집중식 위치로 모아줍니다.
이 문서 공유
다음 토픽
여러분께 도움을 드릴 자료를 추천합니다.
이러한 리소스에 책갈피를 지정하여 DevOps 팀의 유형에 대해 알아보거나 Atlassian에서 DevOps에 대한 지속적인 업데이트를 확인하세요.