SLASH 21 - 토스 서비스를 구성하는 서버 기술 그리고 "나"
1. 토스 서비스를 구성하는 서버 기술들
- MSA 구성으로 많은 서버가 뜨는데, 많은 서버를 관리하고 효율적인 인프라 활용을 위해 K8S를 container orchestration으로 도입
- calico cni와 service mesh로 Istio를 사용중
- 민감자료 저장 용도로 ceph를 이용해 내부 스토리지 서비스를 제공하고 있음
- 이전에는 memcached를 캐시로 많이 사용했으나 데이터 보존 문제와 optimistic lock 구현 편이성 때문에 redis cluster를 캐시로 더 많이 사용 중
- kafka는 로그 데이터 파이프라인으로 사용하는 클러스터 / 서비스에서 메세지큐로 사용하는 클러스터 하나를 운영 중
- 로깅과 모니터링으로는 elk + filebeat, thanos, grafana를 사용
2. 데이터 센터 트래픽 조절
3. K8s + Istio
- dc/os --> k8s로 마이그레이션
- container orchestration에서 가장 중요한게 service discovery와 container lifecycle management인데 dc/os에서 canary 배포로 사용하던 vamp는 service discovery 기능의 비효율과 한계가 있었음. 때문에 DC2에서 K8s를 도입하고 DC1에서는 dc/os를 동시 운영함. 한번에 마이그레이션을 진행하다가 문제가 발생하면 롤백이 불가능하기 떄문에 점진적인 이동으로 작업함.
- k8s 도입을하면서 Istio도 도입함. 이유는 service mesh의 장점이 크기 때문. msa로 넘어가면서 하나의 서비스에서 여러 서버의 호출이 발생. 때문에 서버 간의 네트워크 처리가 필요했고 circuit breaker, retry, fallback 등을 애플리케이션에서 처리함. Istio를 도입하면 istio-proxy가 sidecar 형태로 붙어서 모든 네트워크를 proxy하게됨. proxy하면서 어플리케이션에서 하는 일을 대신 할 수 있게됨. 코드단에서 처리가 아닌 Infra 차원에서 한번에 해결해 줄 수 있게됨.
4. api-gateway / webflux
- spring cloud gateway를 사용한 이유? webflux를 도입하면서 reactor에 대한 운영 자신감이 있었고 성능이 검증되어서 선택함
- api-gateway는 필터를 통해 여러 처리를 하게 됨. cusom filter를 구현하여 기존의 인증을 진행할 수 있도록 함
- 라우팅은 static이나 dynamic으로 추가할 수 있음. dynamic의 경우 routing 검증 프로세스가 확립되지 않아 static으로 진행 중
- client의 종류가 다양해지며, 여러 gateway의 공통 로직이 생기며 gateway가 무거워짐. gateway 로직을 분리하기 위해 새로운 gateway추가. 공통로직은 module화해서 처리
- 토스의 대부분의 프로젝트는 MVC를 사용하지만, I/O가 많은 프로젝트에서는 WebFlux를 사용하고 있음. 그리고 초기에는 reactor로 개발했지만 점차 coroutine을 도입하게됨. Reactor를 사용하면 러닝 커브가 큼(e.g. operator에 대한 이해가 필요함, 테스트 필요) coroutine을 사용하면 direct style로 개발 가능하여 러닝 커브가 줄어듦.
5. monitoring
- 여러 서버에서 발생한 log의 중앙집중화가 필요. logback과 filebeat을 통해 어플리케이션 로그를 Kafka로 보내고 kibana에서 검색해 볼 수 있음.
- 로그에 포함되는 요소들 (컨테이너 ID / 서비스 ID / 배포 ID / Request ID / Pinpoint ID)
- apm으로 pinpoint를 사용하고 있음. 해당 요청이 Pinpoint의 어떤 요청과 일치하는지 알수 있도록.
- metric은 쿠버네티스와 잘 맞는 Prometheus를 사용 중
- HA와 데이터 장기보관을 위해 thanos를 사용 중
- 데이터 센터별 thanos에 쿼리를 하기 위해 global thanos가 따로 존재
- ceph를 내부 저장소로 사용하고 grafana로 데이터를 확인
- 알림은 개별 서비스에서는 sentry로 알림을 설정하고 전체적인 알림은 es로그를 활용하는 프로젝트를 만들어 사용
- 에러나 워닝 로그 발생이 많아지거나 특정 로그가 많아지면 slack으로 알림이 옴 + grafana로는 http error / Istio error flag 발생 시 Slack 알림
6. kafka
- 카프카가 자체적으로 HA 기능이 있어 하나의 클러스터로 두 개의 데이터센터를 묶을 지 아니면 다른 클러스터로 구성해 replication을 걸지 고민 --> uber의 사용을 보고 두개의 cluster로 운영하는 것이 좋을 것이라고 판단
- 하나의 클러스터로 운영 시 데이터 센터급 장애가 발생하면 다른 센터에 있는 서비스에서 일부 장애가 발생할 수 있지만 두 개의 클러스터로 운영하면 다른 데이터 센터에는 문제가 발생하지 않음. 다만 두개의 cluster로 운영 시, 다른 데이터 센터로 active하게 넘어갈 때 두 개의 kafka는 consumer offset이 다름. 때문에 이 둘의 sync를 맞추는 application이 필요. producer는 active/active라 장애에 대한 대응은 필요 없고 consumer는 active/standby로 평상시 한쪽 데이터센터의 Kafka를 바라보다 장애시 반대편 데이터센터의 Kafka를 바라보도록 변경하여 장애에 대응을 함.
- consumer에서 메시지 처리 실패에 대한 대응 방법?
- 같은 topic에서 retry로 처리시간이 오래 걸리면, 뒤에 있는 메시지 처리가 밀리기 때문에 다른 topic에서 retry를 하는 것이 성능적 이점이 있음. 또한 Requeue를 하면 필연적으로 따라오는 것은 delay (consume하는 서비스가 정상화가 안되었는데 requeue하면 당연히 실패하니깐) topic을 나누어야 retry시 delay주는 것도 자유로움
7. redis
- lettuse