글을 작성하기에 앞서
약 3주 전에 한빛세미나에서 주최한 서비스 장애 잘 이해하고 대비하기 라는 세미나를 들은적이 있습니다.
이번 아티클은 위의 세미나를 요약하고, 그리고 제 생각또한 적당하게 녹여보았습니다.
글의 순서는 아래와 같은 순서로 전개할 예정인데, 세미나의 순서와 아예 동일하지만 중간중간에 제 사견도 섞어서 말씀드리도록 하겠습니다.
- 어디까지 장애라고 볼 수 있을까?
- 장애는 어떻게 잘 대응할 수 있을까?
- 장애를 예방할 수는 있을까?
- 장애 대응의 두 가지 사례 소개
어디까지 장애라고 볼 수 있을까? (장애의 정의)
우선 장애가 무엇인지 정의하기에 앞서서, 장애에는 어떠한 구성요소가 존재하는지부터 알아볼 필요가 있습니다. 장애에는 아래의 두 가지 구성요소가 존재합니다.
- 민감도 (범위)
- 심각도
민감도
민감도에는 장애를 경험하는 사용자의 범위, 그리고 장애가 발생한 기능의 범위로 구성이 됩니다.
여기서 사용자 범위는, 장애/오류를 경험하는 사용자의 수 또는 범위를 의미하고, 기능 범위는 장애가 발생한 기능이나 시나리오, 또는 서비스 전면 장애 등을 의미합니다.
예시를 들어보겠습니다.
첫번째 예시
A라는 서비스는 구독 회원에 한해서 특정 상품군을 우선 노출해주는 기능을 제공하고 있는데, 오류로 인해서 특정 상품군이 없다고 뜨는 현상이 발생하였습니다.
두번째 예시
B라는 서비스는 신규 기능 런칭 기념으로 선착순 이벤트를 진행하였는데, 이벤트 개시가 일어나자마자 예측하지 못할 정도의 트래픽이 발생하여 해당 이벤트로 랜딩할 때 호출하는 API에 Gateway Timeout이 발생하였고, 해당 서버의 CPU 사용률이 100%를 찍으면서 해당 서버를 경유하는 모든 기능들에 Gateway Timeout이 발생하였습니다.
첫번째 예시로 든 장애의 경우, 구독 회원에 한정해서 특정 기능만 장애가 발생하였다고 볼 수 있고, 두번째 예시의 경우 사용자 범위는 고객 전체이고, 서비스 전면 장애라고 볼 수 있을 것 같습니다.
심각도
장애가 발생하는 경우에는, 해당 장애로 인해서 어떠한 영향이 발생할 수 있는지 또한 측정해야합니다. 여기서 주로 사용되는 지표가 심각도인데요, 심각도는 물질적 피해와 신뢰 손실이 존재할겁니다.
물질적 피해의 경우 장애로 인해서 발생하는 손해액, 배상액, 시간, 그리고 LTV (고객 생애 주기) 라는 것이 존재합니다.
그리고 신뢰 손실은 말 그대로, 해당 서비스에 대한 고객의 신뢰도가 하락하는 현상입니다.
장애는 어떻게 잘 대응할 수 있을까?
사실 장애는 언제든지 발생할 수 있다는 가능성을 열어두어야합니다. 아무리 테크리더 또는 팀장이 장애가 발생할 수 있는 요인들을 모두 파악하였다고 자부할지라도, 장애가 발생하는 요인은 예상 밖에 있고, 또한 너무나도 많기 때문에 모두 대응하는것은 사실상 불가능합니다.
또한 장애는 언제든지 발생할 수 있기에, 최대한 빠르게 감지하고, 빠르게 복구하고, 또한 재발을 방지하는 것이 핵심입니다.
빠르게 감지
장애가 발생하면 최대한 고객보다 빠르게 감지하는 것이 매우 중요합니다.
장애가 고객의 생애주기에 영향을 미치거나 물질적 피해를 야기할 가능성이 존재한다고 판단되는 경우, 차라리 장애가 발생하였다는 사실을 고객이 먼저 감자하기 전에 알리는 것이 물질적 피해를 줄이는 하나의 방법이 될 수도 있기 때문입니다.
또한 장애가 발생하면 장애의 영향도, 심각도등을 평가하고 유관 부서에 공유하여야 하는데요, 장애의 대처는 비단 개발팀 내에서 뿐만이 아니라 연관된 CS파트 라던지, 혹은 마케팅 부서에서도 대비를 할 필요가 있기 때문입니다.
빠르게 복구
우선 장애가 발생하면 완벽하게 복구를 목표로하는 것이 아니라, 우선 임시방편으로라도 고객이 장애를 인지하지 못하게 한다거나, 서비스의 다운이 발생하였다면 DownTime을 어떻게든 최대한 줄이는 것이 1차 목표가 되어야만 합니다.
가령, API 서버의 증설 속도가 고객이 유입되는 속도를 따라잡지 못한다면, desired state를 강제로라도 늘려서 배포를 하는 것이 방법이 될 수도 있을 것이고, 미리 준비된 스탠바이 서버를 미리 배포를 해주는 것 또한 방법이 될 수 있을겁니다.
실제로도 서비스가 K8S 환경에서 운영이 된다고 가정한다면, 파드 개수를 늘리는데 있어서 기존에 떠있는 워커노드의 용량을 초과해서 Pod를 스케일아웃 해야하는 상황이 도래한다면, 아무리 워커노드에 오토스케일러를 적용하였다 할지라도 워커노드 자체가 뜨는데도 시간이 필요하기 때문에 미리 busybox를 이용해서 장애를 대비한 standby worker node를 확보해두는 것 또한 장애를 대비하는 하나의 방법이 될 수도 있을겁니다. (제 경험상 워커노드가 새로 하나 뜨려면 4~5분 정도는 필요했던 것 같습니다)
재발방지
빠르게 장애를 복구하였다면, 근본 원인을 찾아내서 완벽하게 장애를 대처해야합니다.
그러기 위해서는 장애의 근본적인 원인을 분석해야하고, 회고해야만 합니다. 여기서 회고란, 단순하게 인과관계만을 분석하는 것이 아니라, 상세한 기술적인 원인을 파악하고, 최대한 low level까지 파고들어서 분석을 하는 것을 의미합니다.
그 정도의 회고가 이뤄지고, 또한 문서화가 이뤄진다면 차후에 같은 실수를 반복하지 않을 수 있을겁니다.
일반적인 장애 대응 프로세스
위의 내용까지를 정리하자면, 일반적으로 서비스의 장애가 발생한다면 아래의 프로세스를 따라서 진행될겁니다.
감지 → 전파(팀 내에 온콜을 한다거나, 또는 유관부서에 전파) → 판정(시니어 레벨에서 해당 장애의 심각도와 영향도를 분석) → 복구(일단 임시적인 조치부터 진행한다) → 분석(최대한 근거를 가진 상태로 low level까지 분석) → 공유 → 회고
시스템의 가시성 (Observability)
사실 장애하면 빠지지 않고 등장하는 주제는 시스템의 가시성입니다.
가시성이 존재하지 않는 시스템 하에서는 회고가 사실상 불가능하기에, 이런 시스템은 매우 중요합니다. 시스템의 가시성이 존재하지 않는다는 것은, 장애가 발생하더라도 명확한 근거를 수집할 수 없을 가능성이 높다는 뜻이고, 명확한 근거를 수집하지 못한다는 뜻은 장애가 발생할지라도 장애가 발생한 원인 또는 프로세스를 추측성으로 분석할 수 밖에 없어진다는 것을 의미합니다.
대부분의 스타트업들이 내일 당장 배포할 것이 중요하기 때문에 시스템의 가시성을 간과하는 경우들이 많이 존재하는데 (저 또한 사실 그랬습니다), 장기적으로 운영할 프로세스를 담당한다면 장애의 가능성을 언제든지 안고 있기 때문에 매우 중요합니다.
가시성은 단순히 인프라 또는 서버의 지표만을 관측하는 것으로 끝나는게 아니라, 전파 시스템까지를 포괄하는 개념입니다. 따라서 시스템의 가시성을 확보하기 위해서는 아래의 구성요소가 필요하다고 볼 수 있습니다.
시스템 -> 모니터링(APM, ELK, Prometheus) -> 전파 시스템(온콜, 슬랙, etc...)
💡 어플리케이션의 로그를 쌓는 것도 매우 중요하지만, 애플리케이션의 로그는 그저 시계열 데이터이기 때문에 시간에 따라 흘러갈 뿐입니다. 따라서 로그만 쌓는 것 보다는, 이슈트래커를 도입하여 특정 에러가 발생하면 어떠한 로그를 가지고 발생하였는지, 또는 연관된 다른 지표를 관측할 수 있는 시스템을 갖춰야합니다.
장애 대응할 지점 파악하기 (약한 고리부터 파악해보자)
장애가 발생하면 우선 원인부터 파악해야 임시 조치라도 취해볼 수 있는데요, 우선 장애를 발생시킬 수 있는 약한 고리부터 탐색하는 것이 효율적일 것입니다.
예를 들어보자면, 최근에 발생한 코드의 변화 또는 배포를 추적해보는 것도 방법일 수도 있고, 최근의 시스템 변화, 트래픽 변화, 또는 데이터의 변화 또한 포함될 것입니다. (저또한 시스템 장애가 발생하면 이런 약한고리부터 탐색해보는 편입니다)
연관 취약점이 발견되었다고 한다면, 경험에 의거한 취약점 소거가 필요합니다. (예를 들어서, 해당 장애를 발생시킬 가능성이 존재하는 요소 5가지 정도가 발견되었다고 한다면, 실제로 장애의 원인이 될 것 같은 요소들만 골라내기 위해서 소거하는 과정이 필요하다는 뜻입니다)
위의 사항은 경험이란게 매우 중요한 요소로 작용하기 때문에, 대부분은 시니어의 도움을 받을 수 밖에 없습니다. (사실 그게 시니어의 역할일 수도 있다는게 연사님의 의견이기도 했습니다!)
저 또한 시스템의 전면 장애가 발생하였을 때 시니어 혹은 회사 내에서 제일 오래 근무하였던 동료에게 찾아가서 도움을 구했던 적이 많습니다.
장애를 예방할 수는 있을까요?
결국 장애를 예방한다는 것은, 장애가 일어나지 않도록 안정적인 시스템을 구축한다는 것을 의미합니다.
물론 안정적인 시스템이라고 한다면 완벽한 코드를 떠올리기 쉽지만, 코드를 아무리 완벽하게 짠다고 한들 위에서 말씀드린 것처럼, 장애를 일으킬 수 있는 요소는 코드 밖에서도 산재하기 때문에 다른 요소들 또한 챙겨야할겁니다.
서비스의 안정성 유지란, 시스템의 상태와 가용성의 확인, 문제에 대한 긴급 대응, 변화의 추적과 관리, 서비스 트래픽의 수요 예측이 포함될겁니다.
여기서는 위에서 언급한 요소들을 제외하고, 추가적으로 서비스 안정성을 위해서 필요한 요소들만 소개해보려 합니다. (여기에는 저의 개인적인 주관도 존재하고, 연사님이 공유해주신 요소들도 존재합니다)
주요 시스템 지표
결국 안정적 시스템을 운영하려고 한다면, 주요하게 관측해야할 시스템 지표들이 존재할겁니다.
시스템 지표에는 크게 가용성에 대한 지표(Usage), 그리고 응답 성능에 대한 지표(Latency), 그리고 오류 지표 가 존재할겁니다.
- 가용성에 대한 지표: Connection의 개수, CPU, MEM, POD 개수, Pool, 스레드 개수 등..
- 응답 성능에 대한 지표: Write/Read IO, Network Latency, Application Latency, Slow Query 등..
- 오류 지표: Error Rate (5xx), DeadLock, Cache Lock, Exception Rate (4xx가 얼마나 자주 발생하는가)
이 중에서 응답 성능에 대한 지표의 경우, Latency들이 다소 포함이 되어있는데, 평균 응답 성능보다는 상위 5%의 레이턴시의 측정치를 주요 지표로 삼는 것을 일반적으로 추천드리고, 가용성 지표또한 평균치보다는 최대치를 주요 측정치로 가져가는 것을 추천하는 바입니다. (해당 값들이 최대치를 찍게되면 일반적으로 클라이언트는 클레임을 제기하게 되어있습니다)
신뢰성 있는 테스트와 품질
지금까지는 시스템의 안정성에 대해서만 다뤘는데, 사실 시스템을 운영하는 코드의 품질또한 매우 중요합니다.
연사님이 소개해주신 몇 가지 요소들만 간략하게 요약하고 넘어가겠습니다.
- 추적 가능한 배포 프로세스: 배포 기능의 명확한 추적 및 릴리즈 노트 작성을 해야 장애 파악에 용이해진다
- 적절한 테스트 코드, 코드 품질을 유지하는 개발 문화: 복잡한 기능 배포의 경우 TDD를 지향하는 조직이 아니라고 할지라도 테스트코드를 써야만 개발 의도가 공유되기 때문에 적절한 테스트 코드는 필요하다고 생각합니다.
High-Tolerance 가 보장된 시스템의 설계
이는 장애가 발생한 이후의 문제인데, 장애가 발생하더라도 서비스의 가용성은 최대화 되어야한다는 의미입니다.
이 또한 몇 가지 구성요소를 정리하고 넘어가겠습니다. (물론 여기에는 현실적으로 쉽게 적용할 수 있는 요소도 존재하고, 여건 상 쉽게 도입하지 못하는 요소들 또한 포함이 되어있을겁니다)
- 데이터베이스의 실패: 데이터베이스의 실패를 대비한 데이터베이스 이중화 (일반적으로 Master/Replication이 이에 해당합니다)
- 서버 간 통신의 실패: fallback 메소드 구현 (예를 들어 WebSocket 서버가 실패한다면 API Polling을 하더라도 시스템을 견고하게 운영할 수 있어야한다는 뜻입니다)
- 급격한 트래픽의 대응 실패: 파킹 서버, 서킷 브레이커 마련 등이 방법이 될수는 있음
- 배포 실패: 빠른 롤백, 블루그린 및 카나리 배포
- 인프라 실패: 각 인프라의 이중화, 임시 백업 시스템
글 마무리
저 또한 한빛미디어 세미나를 다녀오면서 많은 깨달음을 받았습니다.
저는 현재 만 1년차의 백엔드 엔지니어인데, 저또한 작은 장애를 경험을 많이 해보았고, 그리고 서비스가 전면적으로 다운되버리는 커다란 장애도 매우 많이 경험해보았습니다.
그런 저이기에 해당 세미나는 저에게 크게 와닿았는데요, 그렇기에 해당 세미나를 듣고나서 사내에 해당 세미나 내용을 공유해드렸으며, 많은 동료분들이 이에 공감하여 시스템의 가시성 확보를 위한 여정에 있습니다.
그 과정에서 깨달음을 얻게된다면 블로그 포스팅을 통해서 공유드리도록 하겠습니다.
긴 글 읽어주셔서 감사합니다!
'devOps' 카테고리의 다른 글
Redis OOM 장애 (0) | 2024.06.25 |
---|---|
Docker File System (Overlay2) (0) | 2023.08.06 |
Kubernetes 워커노드의 OOM에 의한 클러스터 장애 (0) | 2023.04.11 |
Jenkins on K8S를 설정하며 겪은 일들 (0) | 2023.03.04 |
내가 쿠버네티스 설정하며 겪은 삽질들 (alb-controller, jenkins, monitoring) (0) | 2023.02.27 |