분산 시스템의 골칫거리, 못다한 이야기
'분산 시스템의 골칫거리' 주제로 강의 할 때 gc, tcp, timeout 등 키워드 중심으로만 설명했다. 그러다보니 전체적인 숲에 대한 이야기는 하지 못했다. 왜 그렇게 진행 했을까를 생각해보면 나는 fact 전달에만 집중했던거 같다. gc 나 tcp 같이 기술적인 deep 한 접근은 내가 주는 정보에 이견이 생기기 어렵다(오랜 세월 공고히 발전해온 기술들이니까).
하지만 청자들은 분산 애플리케이션을 경험하면서 겪은 어려움이 구체적으로 뭐였는지 더 궁금했을거 같다. 그 이야기를 하면서, 내가 잘못된 정보를 제공하더라도 일단 설명하고 지적 받으면 정정하면서 나도 같이 성장하는게 맞는 방향이라 생각한다.
분산 시스템이 왜 필요할까?
먼저 왜 필요한지 근본적인 질문에서부터 이야기를 시작해보겠다. 사실 답은 간단하다. 애플리케이션이 제공하는 서비스가 성공해서, 많은 트래픽을 감당하기엔 기존 computing power 로는 한계가 있기 때문이다.
서버 스펙을 증가시키는 scale up 도 있지만, 요즘은 scale out 도 쉽게 할 수 있다(k8s 환경에서 auto scaling 은 쉽다).
DB 도 아래와 같이 write only db 와 read only db 를 나눠서 운영할 수 있다.
그런데 이것만으로 분산 시스템이라고 할 수 있을까? 애매하다. 완전히 아니라고 할 수 없지만 이 정도로는 부족하다고 생각한다.
cf) 분산 애플리케이션 기준으로 설명하고 있다. 하둡이나 분산 파일시스템을 말하는건 아니다.
분산 시스템인지 아닌지 에 대한 명확한 기준이 있는건 아니지만 2가지 키워드(conway's law, heterogeneity)를 중심으로 생각해보면 좋을거 같다.
1. conway's law
conway's law 는 쉽게말해 '개발조직 구조와 시스템 구조가 일치하게 된다' 는 뜻이다. 그런데 수많은 비지니스 도메인마다 각자 처한 상황이 다른데 법칙이라니. 분명 많은 반발과 함께 깨려는 시도들이 있었을 것이다. 하지만 나는 아직까지 conway's law 를 깨고 성공적으로 잘 운영중이라는 후기를 접해 본적 없다.
서비스가 성공해서 시스템이 고도화 될수록, 팀은 쪼개지고 각각의 전문 영역이 생긴다. 예를 들어 고객이 스마트스토어 쇼핑을 하고 주문/결제 후 상품을 받기까지는, 내부적으로 수많은 팀이 협업하는 구조로 되어있고 팀마다 담당하는 시스템이 있다. 그리고 팀 내에서도 도메인에 따라 분리시키기도 한다. ex) 쇼핑팀 / 페이팀(주문, 결제, 정산, 쿠폰, 배송 등). 여기선 생략했지만 쇼핑팀도 엄청 세분화되어 있다. 결국 경계를 어떻게 나누냐가 제일 중요하다. 분리시키는게 유리하다고 판단되는 만큼만 MSA 구조로 간다.
2. heterogeneity(challenge of distributed system)
컴퓨터 공학에서 heterogeneity 는 서로 다른 종류로 구성된 network, computer h/w, program language 그리고 implementation by different developers 상황에서 어떻게 분산 시스템을 구축해 common goal 을 달성하냐에 대한 이야기다.
나는 이 두가지 모두 고려된 아키텍처가 진짜 분산 시스템이라고 생각한다.
분산 시스템 개발이 어려운 이유
워낙 다양하지만 두가지 에피소드를 소개하려고 한다. 아래 그림처럼 application server 들을 scale out 하면 더 많은 사용자 트래픽을 받을 수 있지만 db 는 scale out 이 쉽지 않으니 결국 몰리게 된다(centralized data). 그로 인해 실제로 db connection storming 문제가 있었다. 또한 db 가 아니더라도 연관된 시스템에 과도한 api 호출로 장애를 전파 시킬 수 있다.
cf) db scale up 도 쉬운 선택은 아니다. 작업 시간만큼 서비스가 중단되니까.
결국 application scale out 만으론 이 문제를 해결할 수 없고 아래 두가지 기술을 활용했다.
1. MessageQueue
트래픽이 몰릴 때 asynchronous interaction 효과는 더 커지고 그만큼 reliable 하게 서비스를 제공할 수 있다. 우리는 이렇게 새로운 시스템을 만들었지만 경우에 따라 기존 시스템보다 사용자 응답은 더 느리다. 안정성이 더 중요한 가치로 목표가 세워졌기 때문이다. 하지만 이 구조도 모든 문제를 해결해주진 못한다. 새로운 문제들이 발생했고 대응중이다(개인 블로그라서 자세한 상황은 공유할 수 없다).
2. 분산 DB
분산 DB 를 운영하는 것은 난이도가 높다. 샤딩키를 뭐로 할거냐(샤딩 전략)도 중요하고, DB 전환이 목표인 우리는 multi master 인 상황에서도 서비스를 안정적으로 운영해야 했다.
저런 기술적 complexity 를 생각해보면 분산 시스템의 장점인 economic 에 대해서 다시 생각해보게 된다. 컴퓨터 비용이 저렴해지면서 scale up 보다는 scale out 이 economic 한건 알지만, 고려 대상은 그게 전부가 아니다.
k8s 환경에서 health check 이슈
분산 시스템의 어려운 점 중 하나로, k8s liveness/readiness 설정 때문에 장애가 전파되는 문제를 소개한다. k8s 에서 liveness check 가 실패하면 container 를 재시작시킨다(pod 가 재시작 된다고도 하는데 엄밀히 말하면 container 재시작이다). readiness check 가 실패하면 container 의 새로운 트래픽을 차단시킨다(LoadBalancer 에서 빼버린다).
k8s 는 liveness/readiness 모두 외부(db call, api call) 에 의존하지 않길 원한다. 만약 liveness check 에서 외부 db call 을 하는데, 실패하면 container 가 재시작된다. 하지만 재시작 되도 다시 livenessProbe 를 실행시키므로 무한 재시작 하는 상황에 빠진다.
그런데 readiness 로 외부 db call 하는건 무조건 안좋을까? 특정 pod 의 container 만 db 접속이 실패하는 상황이라면 LB 에서 빼버리는게 더 나을 수 있다. 하지만 db 가 아예 먹통인 상황이라면? 모든 application container 가 LB 에서 빠지게 되니 문제가 된다. 물론 이렇게 생각할 수 있다. db 가 아예 동작 안하면 application 이 할 수 있는게 뭐가 있냐고. 결국 해당 application 에서 db 를 이용하는 로직이 많냐 적냐로 의견이 갈릴거 같다.
cf) 우리는 spring boot actuator 를 이용해 livenessProbe 와 readinessProbe 를 연동했다.