Seller Insight 개편안

최호승·2022년 3월 27일
0
post-thumbnail
post-custom-banner

Monolithic Architecture 란?

  • 온라인 서점을 만든다고 가정을 해보겠습니다. 초기 단계이니 작게 유저, 책, 주문 3가지의 서비스로 시작하고, MVC로 계층을 나누겠습니다.

  • 위의 구조로 해당 사이트는 아무 문제 없이 잘 동작할 것입니다. 프로젝트, 데이터베이스 서버 총 2개의 서버만 관리하면 되기 때문에 편하게 운영을 할 수 있습니다.
    그러나 이 온라인 서점 서비스가 점차 성장하기 시작했고, 많은 고객을 얻음과 동시에 그에 상응하는 갖가지 요구 사항으로 인해 서비스는 점차 복잡해져갑니다. 새로운 기능들도 생기고, 기존에 있던 기능들도 커져가죠.

1. 빌드 시간, 테스트 시간이 길어진다.

처음에는 전혀 문제가 되지 않던 빌드와 테스트가 서비스가 커짐에 따라 점점 오래 걸립니다.

CI / CD (지속적 통합 / 지속적 배포)가 강조되고 있고, 페이스북 같은 경우는 하루에도 수십 번을 프로덕션에 배포를 한다고 합니다. (참고)

CI / CD란

CI/CD는 애플리케이션 개발 단계를 자동화하여 애플리케이션을 보다 짧은 주기로 고객에게 제공하는 방법입니다. CI/CD의 기본 개념은 지속적인 통합, 지속적인 서비스 제공, 지속적인 배포입니다. CI/CD는 새로운 코드 통합으로 인해 개발 및 운영팀에 발생하는 문제(일명 "통합 지옥(integration hell)")를 해결하기 위한 솔루션입니다.

특히, CI/CD는 애플리케이션의 통합 및 테스트 단계에서부터 제공 및 배포에 이르는 애플리케이션의 라이프사이클 전체에 걸쳐 지속적인 자동화와 지속적인 모니터링을 제공합니다. 이러한 구축 사례는 “CI/CD 파이프라인”이라 부르며 개발 및 운영팀의 애자일 방식 협력을 통해 지원

CI / CD

  • CI/CD는 애플리케이션 개발 단계를 자동화하여 애플리케이션을 보다 짧은 주기로 고객에게 제공하는 방법입니다. CI/CD의 기본 개념은 지속적인 통합, 지속적인 서비스 제공, 지속적인 배포입니다. CI/CD는 새로운 코드 통합으로 인해 개발 및 운영팀에 발생하는 문제(일명 "통합 지옥(integration hell)")를 해결하기 위한 솔루션입니다.
  • 특히, CI/CD는 애플리케이션의 통합 및 테스트 단계에서부터 제공 및 배포에 이르는 애플리케이션의 라이프사이클 전체에 걸쳐 지속적인 자동화와 지속적인 모니터링을 제공합니다. 이러한 구축 사례는 “CI/CD 파이프라인”이라 부르며 개발 및 운영팀의 애자일 방식 협력을 통해 지원

2. 개발 언어에 종속적이다.

상황에 맞게 기술을 유연하게 적용하지 못합니다. Java + Spring으로 구현이 되어있다면, 선택의 여지없이 Java + Spring으로 구현을 해야 합니다.

3. 선택적으로 확장할 수 없다.

이벤트 서비스와 주문 서비스의 사용률 90 : 10이더라도, 원하는 서비스만 확장할 수 없고, 프로젝트 하나를 통째로 확장해야 합니다.

4. 하나의 서비스가 모든 서비스에 영향을 준다.

하나의 서비스에 문제가 생기면 Monolithic은 구조상 모든 서비스에 영향을 줄 수밖에 없습니다. 이벤트 서비스에 트래픽이 몰려 해당 서버가 죽게 된다고 가정을 해보겠습니다.

Monolithic에서는 하나의 서버에 모든 서비스가 있기 때문에 하나의 서비스의 트래픽 폭주로 인해 다른 서비스도 마비되는 상황이 옵니다.

Monolithic의 문제점

100명 정도가 모여 하나의 앱을 서비스한다고 가정해보자. 하나의 앱이므로 monolithic하다는 것은 하나의 프로젝트에 모든 기능을 담고 서빙하는 것으로 생각하면 된다. 하나의 레포지터리에 100 명의 개발자가 붙어서 여기저기 수정하고 배포한다면 어떨까?

  • 부분의 장애가 전체 서비스의 장애로 확대될 수 있다.
  • 배포가 잦다. 너도 나도 그대도 배포를 한다면 배포가 잦겠다. 한 번의 배포(file changes)가 전체 프로젝트의 몇 %의 코드/기능을 수정하는 것일까? 프로젝트의 규모가 크기에, 굳이 배포가 되지 않아도 되는 부분이 더 많을 경우가 대부분일 것이다. 불필요한 부분에 대해 배포를 할 필요는 없어보이는데 말이다.
  • 배포 속도가 느리다. 프로젝트의 크기가 커짐에 따라 배포 대기 시간이 비약적으로 증가하게 된다.
  • 프로젝트의 크기가 크다. 디렉토리 구조가 엄청나게 복잡해진다. 읽어야 할 라인 수나 파일 수가 부담되는 순간 가독성과 유지보수성은 떨어지기 마련이다.
  • 기능 분리가 명확하지 않다. 여러 팀에서 공통으로 사용하는 모듈이 있는데, 이 부분을 변경한다면 어떻게 될까? 한 부분의 코드를 변경하면 monolithic의 다른 부분에 영향을 미칠 수 있다. 점점 legacy가 된다. 테스트 코드를 촘촘히 짜는 것도 한계가 있다. 테스트가 어려워진다.
  • 데이터베이스의 규모가 거대하다. Monolithic 서비스에는 하나의 데이터베이스가 붙어있고, 프로젝트의 규모가 커질수록 데이터베이스의 크기도 같이 커지는데, 스케일 업/다운 문제가 발생하기 쉽다.
  • 좋은 최신 기술을 적용하기 어렵다. 기술을 도입하든 버전업을 하든 한 번 건들이기 시작하면 대규모 작업이 되기 때문이다. 새로운 기술을 적용하는데 있어서 개발자들이 보수적이게 된다.

문제점 참고링크

Microservice Architecture 란?

마이크로 서비스 아키텍처 스타일은 단일 응용 프로그램을 나누어 작은 서비스의 조합으로 구축하는 방법이다.
이전에 본 Monolithic으로 구현된 프로젝트입니다. MSA로 변경하게 되면

  • 가로 방향으로 구분되어 있던 경계가 MSA는 세로 방향으로 구분됩니다. 이는 하나의 프로젝트가 프레젠테이션, 비즈니스, 데이터베이스 계층으로 구분되던 것을 하나의 서비스가 하나의 프로젝트로서 프레젠테이션, 비즈니스, 데이터베이스 계층을 가지게 됨을 의미합니다.
  • 즉 각각의 서비스 별로 프로젝트가 생기게 되며, 하나의 서비스가 문제가 생긴다고 해서 다른 서비스에 영향을 주지 않습니다.
  • Method로 호출하면 되는 걸 왜 굳이 REST 방식으로 호출하고, 잘 돌아가는 프로젝트를 굳이 시간을 들여 나누어서 얻을 수 있는 것은 어떤 이점이 있을까요?

1. 빌드 및 테스트 시간을 단축시킬 수 있다.

30개의 서비스를 가진 Monolithic 의 빌드 시간이 30분이었다면, MSA는 각각의 서비스를 1분 만에 빌드 할 수 있습니다. 이는 CI / CD를 추구하는 기업에서는 좀 더 적합한 모델이 됩니다. 왜냐하면 하루에도 몇 번을 빌드 및 배포를 해야 하는데 그때마다 많은 시간을 소모하게 된다면 낭비이기 때문입니다.

2. 폴리글랏 아키텍처 구성이 가능하다.

상황에 맞게 기술을 유연하게 적용할 수 있습니다. 예를 들어 TPS(시간당 트랜잭션)가 높고, 읽기 작업이 많은 서비스에는 Node + Redis로 구현을 하고, 트랜잭션 및 안정성이 중요한 서비스에는 Spring + RDB를 적용할 수 있습니다.

플리글랏 아키텍처

미국 개발자들 사이에서 ‘폴리글랏 프로그래밍’은 하나의 대세가 되었다. 폴리글랏(polyglot)은 여러 언어를 구사하는 것을 말한다. 즉, 폴리글랏 프로그래밍은 ‘패러다임을 달리 하는 여러 개발 언어를 자유롭게 구사하는 것’이라고 할 수 있다.

일반적으로 프로그래머는 하나의 언어를 배우게 된다. 하나의 언어를 배우면서 문법이나 API만 배우는 것이 아니라, 패러다임이나 아이디어까지 함께 배운다. 여러 개의 프로그래밍 언어를 배운다는 것은 그만큼 다양한 패러다임을 접하고, 많은 아이디어를 만들 수 있는 기반을 다진다는 뜻이다. 다양한 패러다임과 많은 아이디어는 개발자가 직면한 다양한 문제를 해결하는데 도움이 된다.

TPS

Transaction Per Second(TPS)는 초당 트랜잭션의 개수입니다. 실제 계산하는 방식은 일정 기간 동안 실행된 트랜잭션의 개수를 구하고 다시 1초 구간에 대한 값으로 변경합니다

3. 탄력적이고 선택적인 확장이 가능하다.

MSA는 작은 단위의 작업이라 필요한 서비스만을 선택적으로 확장할 수 있습니다. 만약, 주문 서비스와 이벤트 서비스의 사용률이 90 : 10 이라면 이벤트 서비스만을 선택적으로 확장(scale out) 할 수 있습니다.

4. 하나의 서비스가 다른 서비스에 영향을 주지 않습니다.

이전 글처럼 책을 무료로 주는 이벤트를 한다고 가정을 해보겠습니다. 이벤트 서비스에 트래픽이 몰려 해당 서버가 죽더라도 다른 서비스에는 영향이 가지 않습니다. 각 서버마다 서비스를 놓기 때문이죠.

물론, 죽은 서비스를 타 서비스가 호출하게 되면 문제는 생깁니다. 이는 Circuit Breaker에서 좀 더 알아보도록 하겠습니다.

  • 그러면 서비스를 분리함으로써 생기는 문제점
  1. 성능 이슈가 있다.

Monolithic은 다른 서비스 간의 상호작용이 필요할 시에는 Method 호출을 이용합니다. 즉 이러한 행위는 메모리 안에서 일어나죠. 그러나 MSA와 같은 경우에는 주로 HTTP를 사용합니다. 이는 Network IO를 통하여 다른 서버까지 갔다 다시 와야 됨을 의미합니다. '첫 번째 문제점이 성능이라니...'라고 생각이 들 법도 하지만 요즘은 하드웨어나 기술이 많이 발전했고 성능보다는 유지 보수에 좀 더 무게중심을 둔다는 점을 미뤄볼 때, 개인적으로는 Client가 크게 체감하지 못할 정도면 MSA의 장점에 좀 더 손을 들어줘도 될 거 같습니다.

2. 트랜잭션이 불편하다.

Spring에서는 @Transactional이라는 어노테이션하나만 달아주면 자동적으로 트랜잭션 처리를 해줍니다. 그러나 서비스가 각각으로 나뉘게 되면 불편함이 따르긴 합니다. 그래서 MSA에서는 서비스 간에 Global 트랜잭션이 일어나는 상황보다는 Local 트랜잭션이 주로 이루어지게 경계를 나누고, 불가피하게 서비스 간에 트랜잭션이 필요하면 트랜잭션 로직을 추가합니다.

3. 관리 포인트가 늘어난다.

Monolithic은 간단한 경우 어플리케이션서버와 DB 서버 두 개를 관리하면 되지만, MSA에서는 기본적으로 서비스 수 * 인스턴스만큼의 서버 (경우에 따라 DB)가 존재하고 서버가 늘어난 만큼 로깅, 모니터링, 배포, 테스트, 클라우드 환경에서의 관리들은 부담스럽습니다. 이러한 문제는 관리에 들어가는 비용을 줄이기 위해 자동화와 간단하게 모니터링할 수 있는 환경을 권장합니다.

spring boot admin - 모니터링

jenkins - 테스트, 빌드, 배포 자동화 (CI)

MSA의 경계의 기준

  • 자율적인 기능
  • SRP
  • 배포 단위의 크기

부연설명

  • 관리할 수 있는 수준 이내로 유지할 수 있어야 함
  • 서브도메인
  • 폴리글랏 아키텍처
  • 선택적 확장

부연설명

  • 모든 기능 모듈이 모두 동일한 수준의 확장성을 필요로 하지는 않는다
  • 확장에 관한 요구사항을 기준으로 마이크로서비스의 경계를 결정
  • 자원소모량, 소유비용, 비즈니스 효용성, 유연성 등이 분석 기준
  • 작고 애자일한 팀

부연설명

  • 전체 중에서 서로 다른 일부를 개발하는 데 집중할 수 있는 작고 집중력 있는 팀 구성을 통해 애자일 방식의 개발을 가능하게 해준다
  • 트랜잭션

서비스간의 상호작용은 어떻게 이뤄져야 할까?

주문이 일어날 때는 간략하게 유저와 책 정보를 주문 테이블에 넣고, 배송 서비스에 유저와 책에 대한 정보까지 전달을 합니다. 유저, 책은 동기적으로 일어나야겠지만 배송은 비동기적으로 일어날 수 있습니다.

동기 : REST/JSON

비동기 : MQ 프로토콜

MSA가 다른 Architecture과 마찬가지로 모든 상황에 적합한 것은 아닙니다. 각 기업마다 갖가지 상황이 존재할 것이고, 이를 기반으로 뭐를 내주고 뭘 얻을 것인지 이해타산을 잘 따져야 합니다. 특히 상황에 맞는 설계가 가장 중요할 것 같습니다

가령. Monolithic을 메인으로 삼고 유연성이 필요한 서비스들만을 분리하는 것도 하나의 방법이 됩니다.

MSA의 특징

  • 마이크로서비스의 각 모듈은 명확한 경계를 가지고 있다.
  • 각 서비스는 독립적으로 배포할 수 있다.
  • 한 서비스는 한 데이터베이스에 연결되어 있다.
  • Stateless. 서비스 간 요청 간에 정보를 저장하지 않는다.
  • 각 서비스를 이어주는 Gateway 서버가 별도로 존재한다. Gateway 서버에서는 라우팅, 캐싱, 프록싱을 한다.
  • 보통 한 팀에서 하나의 서비스를 관리하게 된다.

장점

  • 기술 부채를 해소하기 더 쉬워진다. 서비스 단위가 작게 유지되므로, 다른 서비스를 방해를 하지 않고 다양한 기술을 적용해 볼 수 있다.
  • 서비스 별로 다른 트래픽을 관리하기 쉬워진다. 호출 횟수가 엄청나게 많은 API가 하나 있다고 하자. Monolithic으로 구현하게 되면, 이 부하를 소화하기 위해 큰 덩어리의 코드를 여러 대의 서버에 다 올려야 한다. 하지만 MSA 구조라면, 호출 횟수가 많은 서비스에 대해서만 서버를 증설하면 된다.
  • 서비스 별 모니터링이 용이해진다.
  • 한 서비스에서 장애가 나도 전체 서비스에 영향을 주지 않게 된다.

단점

  • 느리다!
    • 개발 속도가 느리다. API를 서빙하기 위해서는 Gateway를 통해야 하기 때문에 서비스 쪽만 개발해서 되는 것이 아니라 gateway에 대한 개발도 필요하게 되므로 개발자들 간의 커뮤니케이션 코스트가 증가하게 된다. 작업량도 늘어나게 된다.
    • 응답 속도도 느리다. 최소 한 번 이상의 request가 더 필요해지므로 monolithic에 비해 API 응답 속도가 느려지게 된다.
  • 서비스의 경계를 정의하는 것이 쉽지 않을 때도 있다. 결제 서비스 내에서 복잡한 쿠폰 기능을 처리 vs 결제 서비스 + 쿠폰 서비스
  • 서비스 호출이 다른 서비스를 연속적으로 호출한다면 디버깅이 어렵다.

정리

대규모의 복잡한 기능을 서빙하는 수십, 수백명의 개발자가 달려들어야 하는 서비스를 만드는 것이 아니라면 MSA는 오히려 개발을 더 복잡하게 만들 수 있다. Monolithic architecture와 MSA가 내게 필요한 순간인지를 알아야 한다.

참고사항

profile
백엔드 개발자
post-custom-banner

0개의 댓글