Skip to content

Latest commit

 

History

History
157 lines (151 loc) · 13.1 KB

토스ㅣSLASH 21 - 토스 서비스를 구성하는 서버 기술.md

File metadata and controls

157 lines (151 loc) · 13.1 KB

토스 | SLASH 21 - 토스 서비스를 구성하는 서버 기술

토스 | SLASH 21 - 토스 서비스를 구성하는 서버 기술


  1. 토스 서비스를 구성하는 서버 기술들
  2. 데이터 센터 트래픽 조절
  3. K8S + Istio
  4. api gateway / webflux
  5. monitoring
  6. kafka
  7. redis

1. 토스 서비스를 구성하는 서버 기술들

IMG_644E27B78732-1

  • Infra : 데이터센터 2중화, active-active로 운영 + aws 일부 서비스 이용
  • K8S + Istio + Ceph
    • K8S : 효율적으로 서버를 관리하기 위해 Container Orchestration
    • Istio : Calico cni, Service mesh
    • Ceph : 민감자료 저장 용도, 내부 스토리지 서비스
  • Cache, Monitoring :
    • Memcached : 이전에는 자주 사용하던 캐시 스토리지 였으나 최근에는 데이터 보전 문제와 optimistic lock 구현 편이성 때문에 Redis Cluster를 더 많이 사용ㅇ중
    • Kafka :
      • 로그용 클러스터 : 로그 데이터 파이프라인으로 사용 중
      • 메시지큐 클러스터 : 서비스에서 메시지 큐로 사용중
    • ELK + filebeat + thanos + grafana : 로깅 & 모니터링
  • Spring :
    • 송금, 채팅 서비스, 카드사 알림, 은행 대출 추천서비스와 같은 외부 연동 서비스, 여러 서비스의 데이터를 통합해서 보여주는 서비스
    • 이전에는 Java를 많이 사용했지만 최근엔 Kotlin을 많이 사용
  • AWS : DNS설정, 이미지 검수, 정적 파일 serving

2. 데이터 센터 트래픽 조절

IMG_728FD0660554-1

  • 평상시에는 2개의 데이터 센터에 트래픽이 50:50으로 들어가도록 하지만, 한 쪽이 100%가 되는 트래픽 옮기는 작업을 자주함 → 자동화
      1. 장애가 발생했을 때, 원인 파악과 해결이 오래걸리기에 해결보다는 복구를 중심으로 대응해, 장애가 나지 않는 반대편 센터로 트래픽 옮김
      2. 새로운 시스템 도입, 쿠버네티스 설정 변경 등의 작업시에 최대한 장애 여파를 줄이기 위해 트래픽을 한쪽으로 100% 옮기고 나서, 다시 반대편에는 트래픽의 1%정도만 준다.
      3. 적은 트래픽으로 문제가 없을시 점진적으로 트래픽 늘려남. 카나리 배포를 데이터 센터에 적용
      4. 트래픽을 조절할 수 있는 구간은 두 곳 : Route53, L7
      5. L7에서는 모든 트래픽이 곧바로 옮겨짐, Route53은 다른 라우터로 전파가 되어야 하기 때문에 시간이 소요됨

3. K8S + Istio

K8S

  • 19년도 쯤 DC/OS에서 K8S로 마이그레이션 함
  • 컨테이너 오케스트레이션에서 가장 중요한 것 : service discovery + container lifecycle management
  • Vamp : dc/os의 Service Discovery 기능 → 비효율적 한계
  • DC2에서는 쿠버네티스 먼저 도입, DC1에서는 DC/OS를 운영함
  • DC1에서도 점진적으로 DC/OS 노드를 줄여가고 점진적으로 쿠버네티스를 돌입
  • 두 군데서 안정적인 운영이 가능하다고 판단한 뒤 전체 쿠버네티스 도입

Istio

Istio 도입 이유

  • K8S를 도입하면서 함께 도입
  • Infrastructure Concern :
    • 모놀리틱 구조에서 MSA로 넘어가면서, 서버들간의 네트워크 처리가 필요하게 되었고 Circuit Breaker, Retry, Fallback등을 애플리케이션에서 처리
    • Istio를 도입하면 istio-proxy가 sidecar로 붙어서 모든 네트워크를 proxy하게 됨
    • proxy하면서 어플리케이션에서 하는 일을 대신할 수 있으니 어플리케이션의 언어와 상관없이, 개별 어플리케이션에서 하는것이 아닌 인프라 차원에서 해결
  • Client Side Load Balancing
    • MSA는 외부 → 내부 요청보다 내부<→내부 요청이 더 많음
    • 내부 요청을 중앙집중식으로 처리하다보면, 장애에 취약하고 부하도 많이 받게 됨
    • Istio는 내부 요청을 클라이언트 사이드에서 처리하게 해줌
  • DC/OS보다 더 넓은 생태계, 더 큰 서비스에서 검증됨

Istio 도입 이후!

IMG_12A524AEE02B-1

  • 더 높은 Observability
    • Istio는 모든 서비스에 sidecar로 붙어서 실행되고 iptable을 통해 모든 트래픽을 제어
    • 제어된 트래픽은 istio-proxy를 통해 나가고 받게 됨
    • Istio-proxy : envoy-proxy를 Istio에서 래핑한 것, proxy가 어떻게 동작하는지 명확히 보여줌
      • connection pool 동작방식, 커넥션이 유실된 곳 등을 통계로 확인할 수 있음
      • → 문제점 파악이 쉬워짐
    • 기존에는 네으퉈크 문제를 해결할 때 tcpdump로 보내는 쪽 받는 쪽 확인 했던 작업을 envoy가 대신 해줌
  • Application Concern을 Infrastructure Concern으로 변경하기 힘든 부분도 존재
    • 네트워크 처리를 인프라쪽으로 넘겨보려 시도했으나 아래와 같은 이유로 어려움
    • Circuit breaker : host별 설정이라 세부 설정이 힘듬
    • retry도 API의 트랜잭션 처리가 어떻게 되어있는지와 응답값에 따라 retry 필요성이 다름
    • 대신, mTLS설정을 통해 A서비는 B를 호출하고, C는 B호출이 안되는 권한을 적용하거나 envoy filter를 작성해 애플리케이션 변경없이 dynamic하게 네트워크 기능을 추가할 수 있는 장점
    • envoy filter로 헤더 처리, 접근 제어등을 하고 있음
  • 1% Canary 가능
    • Istio Route weight로 1% weight canary 배포 가능
    • 인스턴스 단위로 카나리 배포를 하면 세밀한 트래픽 조절이 어려움
      • 10개 인스턴스 단위에서 한개의 카나리 배포시 → 10% 카나리
      • 하지만 Istio는 라우터의 weight로 카나리를 할 수 있어 인스턴스의 개수와 상관없이 조절
  • Failure Injection Test & Squeeze Test
    • Failure Injection Test : 특정 조건에 맞는 요청일 경우 실패/늦은 응답을 보내 서비스에서 실패해 대한 처리가 잘 되어있는지 확인
    • Squeeze Test : 하나의 인스턴스에 요청을 높여 부하를 주는 테스트
    • → 라이브(프로덕션?)에서 진행되기 때문에 서비스의 최대성능이 아닌 어느 정도 요청부터 서비스가 부하를 느끼는지 확인 용도

4. api gateway / webflux

API Gateway

  • 클라이언트의 인증, 암복호화를 담당하는 서비스가 있었는데 트래픽이 커지면서 api gateway의 필요성이 커짐
  • zuul, kong, Spring Cloud Gateway 중 Spring Cloud Gateway선택
  • Spring Cloud Gateway 선택 이유 :reactor에 대한 운영 자신감 상승
    • 인증, oauth2, 암복호화 : custom filter를 만들어 구현
    • Static / dynamic routing : dynamic은 라우팅 검증 프로세스가 확립되지 않아 대부분 static으로 추가
    • 스프링 내부 코드는 reactor이지만 유지보수의 편리성을 위해 coroutine 사용
  • 서비스가 커지면서 생기는 API Gateway에 관한 고민들
    • 늘어나기 시작하는 클라이언트의 종류
    • GateWay는 누가 관리할 것인가?
      • 어느팀이 관리할지? 공통로직을 어떻게 처리할지?
      • → 토스에서는 플랫폼팀이 관리하고 각 팀의 PR을 받아 플랫폼팀에서 승인, 공통로직은 모듈화해서 처리

Webflux

  • 토스의 대부분의 서비스들은 MVC로 되어있으나 홈탭 및 내소비와 같이 여러 데이터를 모아 보여주는 I/O가 많은 프로젝트에서는 Webflux사용
  • 최근에는 스프링이 Kotlin dsl을 잘 지원하고 R2DBC를 사용하면서 많은 프로젝트가 webflux로 시작하는 중
  • WebFlux를 처음에는 Reactor로 개발했으나 러닝커브가 높아 코루틴 도입
    • Reactor를 사용하려면 reactor가 제공하는 operator를 활용해서 적절한 오퍼레이터 활용, 동작 확인해야하기 때문에 러닝커브가 높음
    • 코루틴을 사용하면서 기존처럼 다이렉트 스타일로 개발이 가능해짐

5. monitoring

log

  • MSA 구조이다보니, 로그의 중앙집중화 필요
  • logback, filebeat을 통해 어플리케이션 로그를 카프카로 보내고 Kibana에서 검색
  • 컨테이너ID, 서비스ID : 어느 데이터센터에서 어떤 컨테이너가 로그를 남겼는지를 확인
    • 이 정보로 특정 서비스의 문제인지 혹은 특정 장비의 문제인지를 확인
  • 배포 ID : 어느 배포 버전에서 발생하는 로그인지 확인, 신규 배포의 로그에만 에러가 발생하면 바로 롤백
  • Request ID : MSA 구조에서는 요청을 주는 쪽, 받는 쪽 어느쪽이 문제인지를 확인할 필요
  • PinPoint ID : 해당 요청이 핀포인트의 어떤 요청과 매칭되는지 확인
  • Hadoop : 5년이상 장기보관해야하는 데이터 Kafka → Hadoop
  • 데이터센터 이중화
    • 데이터센터급 장애가 발생하는 상황에서도 서비스 개발자가 확인할 수 있어야함
    • ES : 각각 50대, 하루에 15TB

Metric : thanos + ceph + grafana

  • 쿠버네티스와 잘 맞는 prometheus, HA와 데이터 장기보관을 위해서 thanos 사용 중
  • 데이터 센터별 타노스에 쿼리하기 위해서 글로벌 타노스 따로 존재
  • ceph를 내부저장소로 사용하고, grafana대시보드로 확인
  • 저장소로 사용하는 mysql도 이중화

Akert : Sentry + Toss ES Alert + Grafana

  • sentry : 개별서비스에서는 sentry활용
  • Toss ES Alert : 전체적으로 es 로그를 활용하는 프로젝트를 만들어 사용 → 슬랙 알림
  • grafana : http error code / Istio error flag 발생 시 슬랙 알림
  • flink : 서비스들의 응답을 파싱해서 응답시간이 튀거나 응댑 실패율이 높아질 때도 알림

6. kafka

카프카 클러스터 구성

이중화된 데이터센터 환경에서 카프카를 어떻게 안정적으로 운영할것인가에 대한 고민

  • 카프카 자체적인 HA가 있으니, 하나의 클러스터로 두 개의 클러스터를 묶을까? VS 다른 클러스터로 구성해 Replication 적용
  • 두 개의 클러스터로 구성하는 방식이 장애의 전파 없이 빠르게 복구 및 안정적인 운영 가능
  • 그러나 이렇게 두개의 클러스터로 구성할때는 active/standby인 컨슈머가 다른 데이터 센터로 active하게 넘어갈 때
  • 두 개의 카프카 컨슈머는 offset이 다르기 때문에 어느 시점부터 consume할지 모르게 됨
  • 두 개의 카프카 offset을 싱크해주는 어플리케이션이 필요 → wakuwaku라는 어플리케이션 개발
  • producer는 active/active로 운영해서 장애시 추가대응은 필요 없음
  • consumer는 active/standby로 운영해서 평상시는 한쪽 데이터센터 kafka를 바라보다 장애시에는 반대편 데이터센터 kafka를 바라보도록 변경하여 장애대응

카프카 컨슈머 메시지 처리 실패시 로직

image

  • 우버에서 사용하는 <토픽별 retry + dead letter queue> 구성을 본 떠 스프링 카프카를 래핑해 구현
  • 같은 토픽에서 리트라이로 처리시간이 오래걸리면 뒤에 있는 메시지 처리가 오래걸리기 때문에 다른 토픽에서 리트라이를 하는 것이 성능적인 이점
  • delay 처리 : 컨슈머가 아직 정상화가 되지 않았는데 retry하면 다시 에러가 발생하니 딜레이 처리가 중요
    • topic이 나누어져야 delay처리도 자유로움
    • 토픽별로 자동 delay requeue를 설정할 수 있고
    • dead letter topic에서는 수동배치로 리큐를 하고 있음

카프카 프로듀서 메시지 처리 실패시 로직

image

  • Kafka A에서 실패 → 약간의 딜레이 후 Kafka B로 보내서 retry → dlq로 이동 & 수동배치로 requeue
  • 프로듀서는 서로 다른 클러스터 2개로 Retry를 구현하지만
  • 완벽한 트랜잭션이 필요한 경우 : OutBox 패턴 활용해서 동일 트랜잭션처리 후 Retry

7. redis

image

  • Redis Cluster 사용 : 데이터 보존이 용이해 장기보관이 필요없는 단발성 데이터 저장 및 캐시로 활용
  • 데이터 센터가 2개밖에 없어 한쪽 센터에 마스터가 많은 상황. 데이터 센터간 네트워크가 끊겼을 떄 split brain 발생 해 두 개다 모두 마스터가 될 가능성
  • slot reblance : 운영 중 메모리 확장이 필요할때 활용
  • redlock (AWS) : 분산락으 redlock 이용해 구현
  • lettuce client : Redis클라이언트로는 Webflux, mvc에서 공통으로 활용할 수 있는 lettuce 활용