if (kakao) dev 2019, Day 1 - 서비스 장애를 극복하는 게임플랫폼 구축하기 요약

by Devellany

Topic /

    서비스 장애가 발생하였을 때 개발자는 서버 상태는 괜찮은지, CPU 사용률은 어떤지 살펴본다. 이와 동시에 서버에 적재된 에러 로그를 확인한다. 그렇다면 왜 서비스 장애가 발생하는지, 어떻게 해결할 수 있는지 살펴보도록 하자.

서비스 장애란

    포스팅 실패, 메세지 발송 실패, 사진 불러오기 실패, 로그인 실패, 아이템 지급 실패 등 서비스가 정상적으로 기능 동작하지 않는 상황을 통틀어서 우리는 서비스 장애라 부른다. 서비스 장애가 발생하면 고객의 불만이 증가하고, 빈번할 경우 고객 신뢰도를 잃어버리게 된다.

서비스 장애 발생 과정

    API 에러 발생 → 응답 처리 속도 지연 → 서버 장비 처리 불가 → 전체 시스템 정지

    서비스 장애는 매우 빠른 속도로 전파 되면서 해당 과정이 진행 되기 때문에 장애가 발생하는 시간은 굉장히 짧다. 어떻게 해서든지 한 서비스의 장애가 전체 시스템 장애까지 이르는걸 막아야 한다. 그래야 고객 불만이 줄어들고 고객들이 크게 체감하지 못할 것이다.

다양한 시스템 장애 요인

  • 하드웨어: 서버, 인터넷, 스위치(라우터 등 네트워크 장비)
  • 소프트웨어: OS, 프로그래밍 언어, 컨테이너 그리고 소스코드

장애 요인을 파악하고 있어야 문제가 발생해도 빠른 대응이 가능하다.

장애 허용 시스템, Fault Tolerant System

1. 모든 구성요소에서 문재 발생 가능성이 존재한다는 사실을 인정

2. 문재 발생 요소 최소화

3. 오류 해결 자동화 처리 구현

4. 전체 시스템 장애 전파 차단

    장애 허용 시스템이 갖춰야 할 특성은 여러가지가 존재한다. 이중화 시스템, 장애차단, 장애 분리, 동작 지속 등이 있는데, 그 중에서 이중화와 장애차단을 살펴보고자 한다.

시스템 이중화 구성

    1차적으로 물리적 인프라 장비의 이중화/다중화 구성이 있다. 백앤드 서버를 마이크로 서비스로 구성하여 서비스 간 분리가 가능하다. 각 서비스는 최소 2대 이상 서버로 구축하고 다중 커넥션으로 관리해야 한다. 또한 오토스케일링 설정을 통해 특정 상황에서 부족한 리소스로 인한 장애 발생 가능성을 줄여야 한다.

저장소 장비 이중화 구성

    어떤 상황에 있어서도 데이터 처리가 동작해야 한다. 이를 위해 Master/Slave를 구성하기도 하고 MySQL MHA를 통해 서비스 다운타임을 최소화 할 수 있다. 또한 한 테이블에 데이터가 지나치게 많이 쌓일 경우 샤딩을 통해 이를 해소할 수 있다. 이는 레디스도 마찬가지다. 한 편, Master/Slave 노드 역할 변경을 할 수 있도록 서버를 구성해놓으면 마스터 노드에서 장애가 발생할 때나, 서버 점검시 효율적인 대응이 가능하다.

물리적 장비 이중화를 제외한 서비스 이중화

    데이터 기록 로직 이중화를 통해 일시적인 장애 상황을 극복할 수 있다.

메세지 발송 요청 → 파라미터 검증 → 제약사항 확인 → Queue 발행 → 기록 요청 → Remote DB

    만약 기록 요청 중 DB 장애나 네트워크 문제가 생긴다면 데이터가 유실될 수 있다. 데이터가 유실되면 그 즉시 고객 문의가 발생한다. 따라서 원격 저장소 장애 시 로컬 저장소에 임시 저장 후 자동 복구 처리를 할 수 있도록 중간 과정을 만들었다. 원격 저장소에 데이터 저장을 실패하였다면 자동 복구 배치를 통해 동일한 로직을 다시 처리하도록 추가하였다. 이와 함께 복구에 따른 결과에 대한 알람을 받도록 모니터링 기능을 더하였다.

자동 복구 기능이 중복 없이 잘 처리할 수 있을까?

    중복 처리를 막기 위해서 고유 ID를 발급하였다. 고유 ID를 통해 중복 처리를 막을 수 있다. 요청 파라미터 값을 활용하여 고유ID KEY를 만들었다.

앱 아이디 + 유저 아이디 + 이벤트 아이디 + 시간 기준 값 ⇒ 메세지 고유 아이디

서비스에서 가장 중요한 로그인 처리는?

    로그인 시 외부 서비스의 아이디 프로바이더 토큰 인증을 확인하는 절차는 다음과 같다.

OAuth 인증 → 로그인 요청 → IdP 인증 토큰 확인 → 자체 인증 토큰 생성 → 로그인 성공

    사용자에게 자체 인증 토큰을 전달하여 외부 서버 통신에 의한 장애 발생 요소를 최소화 하도록 구성하였다. 만료된 인증 토큰을 전달 받았을 때 네트워크 장애로 에러가 발생할 수 있는데, 이 때 인증 방식을 자동으로 전환하도록 구성하였다. 네트워크 장애로 인하여 외부 인증이 동작하지 않을 경우 외부 인증을 거치지 않고 자체 인증 토큰을 갱신하여 로그인에 문제가 없도록 처리하였다.

    다만, 신규 유저의 로그인까지는 허용할 수 없었다. 필수 정보 획득이 불가능해서 장애가 발생하였을 때 신규 유저가 생성되지 않도록 막았다. 그럼에도 불구하고 실제로 장애가 발생하였을 때 전체 로그인 유저 중 95%는 문제 없이 서비스를 이용하였다. 로그인을 실패한 신규 유저는 5% 이하에 불가했다.

장애 차단/장애 알람 구축하기

    장애 발생을 최소화하고 차단시킬 수 있는 방법 중 하나로 API 처리 시 스레드 그룹을 기능에 맞춰서 구분하면 도움이 된다. 모든 API를 공용 쓰레드 풀에서 처리한다면 전체 시스템에 장애가 전파될 수 있다. 특정 API에서 문제가 발생하여 처리 속도가 느려지면 전체 스레드의 점유율이 늘어나게 되고, 문제가 발생한 API가 모든 스레드를 점유하는 순간 서비스 전체에 장애가 발생하게 된다.

    쓰레드풀이 나눠져 있으면 특정 API가 느리게 처리 되더라도 다른 API는 영향을 미치지 않게 된다.따라서 용도 별로 스레드 풀 그룹을 분리하였고, 그룹 별로 쓰레드를 관리할 수 있도록 만들었다. 영역을 나눠서 관리하면 장애 영향 범위를 최소화할 수 있다.

외부 통신이 포함되어 있는 API 구현은 어떻게 장애 처리할까?

    서비스 로직 처리 시 동일한 스레드에서 순차적으로 모두 실행된다면 외부 서버와의 통신 시 문제가 발생할 수 있다. 외부 통신에서 네트워크 지연이 발생한다면 작동 중인 스레드는 계속 점유된 상태를 유지하게 된다. 이 때 위에서 언급한 것과 마찬가지로 전체 처리 속도에 영향을 주게 되어 서비스 장애가 발생하게 된다. 따라서 비동기 서비스 로직으로 처리하도록 변경하였다.

    응답값에 대한 처리는 새로운 스레드에서 처리하도록 변경하였는데, 두 가지 이점을 얻을 수 있었다. 첫 번째로 로그인 처리 시간이 단축되었고, 두 번째로 처리 속도 지연으로 인한 스레드 풀이 고갈되는 것을 방지할 수 있었다.

데이터에 의한 문제가 발생하는 경우는?

    푸시 서버에 푸시 토큰을 등록하는 과정은 다음과 같다.

푸시토큰 등록 요청 → 토큰 검증기 → 토큰 업데이터 → 토큰 기록 → 토큰 DB

    푸시 토큰은 Primary Key로 되어 있다. 단순히 푸시 토큰만 업데이트하면 될 것 같지만 오판이었다. 유저들은 실제 모바일 기기 뿐만 아니라 에뮬레이터를 사용하여 서비스를 이용하는 경우가 있는데, 이를 파악하지 못 하였다. 에뮬레이터의 ID가 동일하여 예외적인 상황이 발생하였고, 푸시 토큰 등록에서 문제가 발생하게 되었다.

    짧은 시간 내에 동일한 키에 대해 요청이 생기면 DB는 Write Lock 문제가 발생하게 된다. 따라서 서로 다른 유저가 동일 토큰 값으로 동시에 토큰 등록 요청을 시도하는 경우 비정상 유저의 토큰값으로 처리하도록 변경하였다. 비정상적으로 사용되는 토큰값을 캐싱해놓고 미리 검증하여 Write Lock 문제를 해결하였다.

장애가 발생했는지 어떻게 알 수 있을까?

    이를 위해 장애 감지 시스템과 함께 알람 시스템이 필요하다. 우선 인프라 장비의 시스템 정보를 수시로 모니터링하여 문제 발생 시 알람 서버를 통해 담당자에게 전달하도록 시스템을 구축하였다. 하드웨어 장비 뿐만 아니라 서비스 API가 잘 동작하는지 확인하는 것도 중요하다.

API 모니터링을 어떻게해야 효율적일까?

서버 구동 → API 정보 전송 → API 정보 획득 → 테스트 호출 → 테스트 요청 정보 저장 → API Health Check 시작

    위와 같이 API 모니터링을 구축하였는데, 너무 많은 알람으로 인하여 업무를 처리할 수 없었다. 지나치게 많은 알람은 불필요한 정보가 되어버려서 알람도 정리가 필요하다는 것을 느꼈다. 무분별하게 알람을 많이 받으면 아무도 보지 않는다. 따라서 다음과 같이 로그를 필터링하고 정제하여 알림이 가도록 설정하였다.

모든 서버 알람 수신 → 그룹핑하여 정리 → 발송 빈도 조절(5분) → 임계치 설정(10회 이상)

    이렇게 정제 되어 온 알람을 받게 될 경우 절대 무시하지 못하도록 프로세스를 만들었다.

요약

  • 서비스 장애: 기능이 정상 동작하지 않는 상태
  • 장애 허용 시스템: 일부 문제가 발생해도 전체 서비스는 정상 동작하는 시스템
  • 적용 사례: 이증화, 장애 차단, 알람 구축

시스템 장애가 발생해도 준비만 잘 되으면 쉽게 극복할 수 있다.

IT 서비스 명성 = 고객을 위한 서비스 신뢰도

장애가 발생하지 않도록 최선을 다하자!

참조: https://if.kakao.com/program?sessionId=f287c4fe-748a-43aa-9ae4-31c818805f02

Author

Devellany

Devellany

back-end Developer

PHP, Java, JavaScript, MySQL, Redis, Ubuntu, Nginx
Codeigniter, Laravel, Zend, Phalcon, Spring Boot, JPA
PHPStorm, IntelliJ, Upsource, SVN, Git, Telegram Bot

로그인

디코에 오신 것을 환영해요!
전문가들의 수많은 아티클 창고 🤓