2019년 10월 3일 목요일

서버 개발자로 살아가기 #2. MSA 이야기

요즘 서비스되고 있는 수많은 서비스들 중 MSA가 아닌 것을 찾아보기 힘들 정도로 이제는 보편화된 설계 구조로 채택되고 있다.


MSA를 왜 해야 하는가?

MSA를 'WHY' 해야 하는가를 논하는 주제는 기술적인 주제가 아니라 Business로 접근해야 한다. 그 다음 HOW 나 WHAT(테크 스텍)이 기술적으로 논해볼 이야기들이다.


요즘 고갱님들은 게임이든 웹이든 서비스가 빠르게 개선되길 원한다.
원하는 기능이 빠르게 자주 개선되길 원하고 버그 때문에 서비스 이용을 거부하기도 한다.

옛날보다 고객한테 배송(배포)해야 할 계란(기능)이 많아졌는데 또 자주 빨리 하란다.
서비스 퀄리티는 고객 경험으로 직결되고 이건 비즈니스에 매우 크리티컬 하다.


한바구니(Monolithic)에 담아서 배송을 하면 빠르고 잦은 배송이 힘들기 때문이다
그리고 가끔은 너무 위함하다


MSA는 서비스를 한바구니에 담지 않기 때문에 자주 그리고 빠르게 나눠서 개선이 가능하다. 동시에 기존에 서비스 하고 있는 제품의 퀄리티도 보장하면서


나는 최근에 라이브 서비스를 하는 도중에 점검 없이 게임 서비스에 영향을 미치지 않는 로거 시스템의 Rolling update를 수행하기도 했다. (내부 로그 데이터를 분석하는 데이터 분석 팀의 빠른 문제 개선 요청을 했기 때문이다)


최근 서비스 되고 있는 수많은 웹 서비스들은 하루에도 수많은 무중단 서비스 배포가 많이 이루어 지고 있다. MSA 구조로 되어 있기에 잦은 수정 및 배포가 서비스 중단 없이 낮은 비용과 낮은 리스크로 가능하게 되었다.



MSA를 어떻게(HOW) 하나요?

이제 이걸 기술적으로 어떻게 풀어야 할지 이야기를 해보자 (그다지 깊이 있는 내용은 없으니 기대는 금물)

Why use micro service architecture 라고 구글링을 해보면 MSA가 뭔지 잘 정리된 글들이 많이 있다. 공통적으로 이야기 하는 MSA의 정의는

A bunch of small and simple services that are loosely coupled


좀더 풀어 설명하면,

- small and simple services : 하나의 서비스의 크기가 작아져서 개발 및 유지보수가 좋아진다는 뜻이고

- loosely coupled : 서비스간 의존도가 적어서 사이드 이펙트가 적고 하나의 서비스 fail 이 전체 서비스에 영향을 미치지 않는다는 뜻이다.


이 세상에 MSA를 어떻게 설계하라고 하는 마땅한 가이드 라인은 없다.  아니 그런게 있을리가 없다.
MSA는 새로 개발된 가이드라인이나 방법론이 아니라 그저 필요에 의해 자연스럽게 나눠 설계를 하다 보니 나오게 된 개념일 뿐이다.



시스템 설계를 할 때 어디까지 쪼갤지 여부는 각자 사정에 맞게 잘 나눠 설계를 하면 될 뿐이다. 사실 수많은 서비스들이 모놀리딕 수준의 규모로 복잡해질 수 밖에 없다. (그럴 운명을 타고 날 수 밖에 없는 상황도 발생한다)

하지만 괜찮다.

MSA의 목표는 모든 서비스의 micro화가 아니라 원하는 만큼 리스크를 분산하고 개발 및 서비스 운영에 있어서 최고의 생산성을 낼 수 있는 방법을 찾기 위한 것이다.

MSA는 엔지니어들이 최적의 제품 개발 및 운영을 위해 자연스럽게 채택하게 된 결과물일뿐, 목표가 아니다.




MSA 테크 스택 패러다임의 변화

micro service 들은 작은 서비스들의 느슨한 결합이라고 하였다.
micro service 라는 말이 나오기 전에 조금 옛날 용어로 정의를 하면 'Distributed system (분산 시스템)' 이다.

물론 분산 시스템 이라고 하는 개념은 너무나도 광범위 해서 microservice === distributed system 이라고 할수는 없다. 분산시스템은 SOA(Service-oriented Architecture) 개념만을 담기에는 너무 큰 개념이다. 


서버 기술 세상에는 일찌감치 Load balancing 이 보편화 되었다.
(엔지니어를 갈아서 18 개월 마다 프로세서 성능을 2배로 올리라는) 망할 놈의 무어의 명령에 한계가 오는 순간, 단일 서버 자원이 처리할 수 있는 작업량에 한계를 극복하기 위해  여러 서버 자원에 들어오는 요청을 분산시켜 처리하는 LB은 더이상 생소한 개념이 아니다.


"서버 처리량이 많아져서 서비스 퀄리티가 떨어지고 있다고요? 서버를 늘리도록 요청해 볼게요.."

scale out 이라는 개념을 비 개발자도 이해하는 세상이다.

묻고(ask) 더블로 가



주의해야 할 점은 MSA === Scale out 가능한 구조라는 것은 아니다.
MSA 구조로 설계를 하면서 자연스럽게 서비스 별로 Scale out이 쉬워 졌을뿐.


서비스해야 할 서버의 종류도 많고 갯수도 많아지다 보니 이를 관리하기 위한 효율적인 방법들이 필요하게 되었고, VM, Container, Orchestration 등의 기술들이 발전하게 되었다. (이 테크 스택들에 대한 자세한 설명은 생략한다)


서버 자원을 정의하는 단위는 하드웨어가 아니라 추상적인 '자원' 그 자체 단위가 되었다.
AWS, GCP, Azure 같은 클라우드 서비스들은 고도로 추상화된 하드웨어 자원을 빌려주고 유통하는 자원 마켓이다.



사실 정말 이야기 해보고 싶은 주제는, MSA 에서 서버 프로그램을 개발할 때 명심해야 하는 내용들을 정리해 보고 싶었다.
컨테이너 기술과 오케스트레이션 기술을 활용하기 때문에 서버 프로그래밍을 할 때 알아두어야 할 내용들을 다음 편에 정리해 보기로 하겠다.




MSA의 단점을 극복하기 위한 기업 문화

큰 덩어리를 잘게 쪼개면 서비스 간 의존성 및 관리 비용 때문에 효율성은 떨어지고 그로 인한 비용 증가는 당연한 결과이다 

서비스를 나누다 보니 동일한 기능을 제공하는데 사용되는 네트워크, 하드웨어 자원은 증가하였고, 그것을 관리하기 위한 운영 비용, 개발 비용, 테스트 비용이 매우 증가 한다.


아래 위키 피디아에 언급된 MSA의 단점 몇가지를 한번 보자.


It may involve communication between different teams, rewriting the functionality in another language or fitting it into a different infrastructure.

팀 간 커뮤니케이션 비용 증가는 작게 나눠지고 많아질수록 당연히 지수함수를 그리며 증가한다.
일단 팀 별로 구현하는 기능이 다르고, 누가 무엇을 개발 하고 있는지도 모른다.
설계, 개발, 테스트 및 배포 까지 수많은 팀, 수많은 사람들을 거치게 된다.


MSA는 Architecture다. 소프트웨어 아키텍쳐는 기술적인 디자인을 넘어서 비즈니스와 고객, 기업 문화, 개발 생산성, 비용 등을 모두 고려하는 것을 의미 한다.
MSA를 적용할때 커뮤니케이션 비용을 산정하고 줄이는 노력은 매우 중요하다.


Services form information barriers

서비스 간 접근(공유)할 수 있는 정보가 제한적.
서비스를 modularization 하고 encapsulation한다는 이야기는 결국 서비스를 개발하는 팀 간에 공유되는 정보 역시 제한적이 될 수 밖에 없다는 뜻이다.

특히 내가 속해 있는 TechLive 같은 라이브 운영팀에서는 개발 요구사항을 성공적으로 라이브 서비스로 운영해야 하는 입장에서는 MSA 구조로 구현된 수십개의 서비스들의 전반적인 이해를 해야 하고 있어야 한다. 매번 발생하는 설계 및 기능 변경 요구사항을 파악하기 위한 적지않은 시간을 할애하고 있다.

그래서 개개인의 역량이 뛰어난 것은 안 비밀~ (내가 제일 못함)


Testing and deployment are more complicated.
테스트 및 배포가 복잡해 짐에 따라서 개발 팀과 QE(Quality Engineering)팀의 커뮤니케이션 비용 및 검증 비용은 매우 크다.
기본 기능 테스트 뿐만 아니라 비 기능 요구사항을 만족하기 위한 로드 테스트나 코드 품질 검증 등의 프로세스들을 최대한 자동화 하려고 노력하고 있지만 수시로 변하고 발전하는 수십개의 서비스들의 요구사항을 매번 follow up 하기가 여간 힘든일이 아니다.


Development and support of many services is more challenging if they are built with different tools and technologies - this is especially a problem if engineers move between projects frequently

각 서비스들 마다 다른 테크 스택, 툴들을 사용하게 되면 개발 유지 보수 비용이 올라감. 특히 엔지니어가 여러 프로젝트들을 동시에 개발을 할 때.

그래서 많은 회사들이 자연스럽게 빌드 자동화를 비롯한 개발 프로세스에 적지 않은 리소스를 투자하게 된다. CI/CD 툴, 코드 리뷰 시스템을 개발 팀 필수 도구 및 문화로 받아들이게 된지는 꽤 되었다.


Moving responsibilities between services is more difficult.

모든 개발자들이 전체 구조에 대한 이해를 100% 이해 하기가 쉽지 않기 때문에
특정 기능을 다른 서비스로 이전하기 어려워 지도록 설계하고 코딩하는 경우들이 발생한다.

특정 기능을 조금 아쉬운 곳에 구현을 했다거나.
기능이 중복 개발 되었다거나 하는 일이 발생한다.




이거 왜 여기다 '개'발했어


에이 ㅅㅂ
('개'발자가 삐뚤어지는 불상사가 생긴다)



이런 현상을 '개'인의 탓으로 돌리지 않도록 끊임 없이 정보를 공유하고 기업문화를 잘 정리하는 것이 중요하다.



MSA 구조로 서비스를 나눠 개발할 수 있다는 아주 좋은 장점을 취하기로 결정 했다면,
동시에 그로 인해 발생하는 cost & debt 들을 받아들이고 개선해 나가도록 노력 해야 한다.




Viewing the size of services as the primary structuring mechanism can lead to too many services when the alternative of internal modularization may lead to a simpler design

(내부 모듈화로 만으로도 충분히 나눌수 있는 기능을) 작은 서비스 크기에 집착해서 서비스를 너무 잘게 나누다 보면 복잡도가 증가할 수 있음

MSA 세상에서는 구조갖고 싸우는 토론하는 경우가 많다. (문화를 이야기 한다며...)

MSA 설계 자체에 집중하다가 정작 목표 달성을 잊고 적당한 선에서 설계를 마무리 짓지 못하는 경우를 만들어서는 안될 것이다.

물론 이 기능은 더 쪼개는게 좋을거 같고 저건 저렇게까지 쪼갤 필요가 있나? 하는 의구심이 들법도 하다.
마음에 안들수도 있고 구조적인 문제 때문에 내가 맡은 서비스의 복잡도가 증가하는 경우도 많다.
심지어 MSA 구조 자체에 회의를 느끼는 경우도 있다.

그럼에도 우리는 MSA 구조가 가져다 주는 장점을 이해하고 이것을 위한 비용을 지불하고 있다는 사실을 받아들여야 한다.





P.S. 그 밖에 기술적인 단점들.


Inter-service calls over a network have a higher cost in terms of network latency and message processing time than in-process calls within a monolithic service process.
네트워크를 통한 통신 비용으로 인해 전체 처리 비용 증가


Two-phased commits are regarded as an anti-pattern in microservices-based architectures as this results in a tighter coupling of all the participants within the transaction. However, lack of this technology causes awkward dances which have to be implemented by all the transaction participants in order to maintain data consistency.
두 군데서의 데이터 처리 의존성은 microservice 기반 아키텍쳐에 위배되는 것. 하나의 트렌젝션 처리에 모든 서비스들이 의존하게 되어 강한 결합을 야기함.
데이터 일관성을 유지하기 위해 트랜젝션에 관여하는 모든 서비스들에 대한 구현(또는 수정)이 필요함.
즉, 비효율적인 설계가 되어 버리는 경우가 발생할 수 있음