Skip to content
isdnetworks
Go back

하나로 보이는 두 문제를 갈라내기

(수정됨)

서비스가 계속 죽어서 메모리 부족으로 보고 태스크 정의의 memory를 두 배로 늘렸다. 덜 죽었지만 여전히 죽었고 파고들어 보니 원인이 둘이었으며 그중 하나만 고쳤던 것이다.

Table of contents

Open Table of contents

원인 — 같은 증상을 내는 두 원인

첫째는 메모리 부족이었다. 큰 파일을 매 요청마다 읽고 변환하니 사용량이 치솟았고 memory 상한을 넘으면 OOMKilled로 강제 종료됐다.

둘째는 상태 확인 실패였다. 그 무거운 처리가 도는 동안 다른 요청을 처리하지 못해서 주기적으로 오는 /health 확인이 응답을 못 받고 unhealthy로 판정돼 교체됐다. 둘 다 결과가 프로세스가 사라지는 것이라 겉으로는 같은 증상이었다.

판단 기준 — 종료 사유가 가른 것

memory를 늘리면 첫째는 해소되고 둘째는 그대로여서 덜 죽지만 여전히 죽는 상태가 됐다. 둘을 가른 것은 종료 사유였는데 메모리 부족은 종료 코드 137에 흔적이 남고 상태 확인 실패는 StoppedReason에 검사 실패로 교체됐다는 기록이 남는다.

같은 죽음인데 StoppedReason이 달랐고 그것을 안 보고 전부 메모리로 취급한 것이 문제였다. 한쪽을 고쳐서 부분적으로 나아진 것이 함정이었는데 전혀 안 나았으면 다시 봤을 텐데 절반이 나아지니 부족했나 보다 하고 더 늘리게 됐다.

주의 — 겹치고 이어지는 사례들

그 뒤로 비슷한 것을 몇 번 더 만났다. 처리 방식을 sync로 바꾸니 상태 확인은 통과했는데 이번에는 여러 개가 동시에 돌아 RSS가 합산됐고 원인이 해결된 것이 아니라 이동한 것이었다.

세 결함이 겹쳐 하나로 보인 적도 있다. 외부가 나중 단계에서야 값을 발급하는데 우리 수집 범위가 짧고 재수집해도 빈 값을 안 채우는 구조여서 셋 중 하나만 고치면 여전히 안 됐다. 네 단계가 이어진 경우도 있었는데 느린 처리가 timeout을 부르고 대체 경로로 넘어간 뒤 그쪽 출력 한도를 넘겨 응답이 잘리면서 파싱이 실패했다.

조치 — 갈라내는 방법

지금 쓰는 방법이 넷이다. 실패를 유형별로 나눠 세는데 실패 100건이 아니라 timeout과 데이터 오류와 403 외부 거부로 나누면 어디를 고칠지가 비율로 정해진다.

두 가지 방법이 있었는데 의심되는 것을 한꺼번에 고치는 것과 한 번에 하나만 바꾸는 것이다. 뒤쪽을 골랐다. 이유는 동시에 고치면 무엇이 들었는지 알 수 없어서다. 대신 배포 횟수가 늘어 느린 것을 감수했다. 그리고 부분 개선을 의심하는데 고쳤는데 절반만 나아졌으면 덜 고친 것이 아니라 다른 원인이 있는 것일 수 있다.

교훈 — 옮겨 간 원인

가장 헷갈리는 것이 원인이 옮겨 가는 경우다. 고쳤더니 증상이 사라졌는데 실제로는 다른 곳으로 옮겨 간 것일 때가 있다.

앞의 사례에서 처리 방식을 sync로 바꾸니 한 문제가 사라지고 다른 문제가 생겼는데 겉으로는 고쳤다가 다른 것이 터진 것으로 보이지만 근본은 요청마다 무거운 작업을 한다는 같은 구조였다. 그래서 고친 뒤에 무엇이 근본이었는지를 한 번 더 묻는데 증상이 사라진 것과 원인이 사라진 것은 다르기 때문이다.

정리


Share this post on:

Previous Post
증거가 되는 다이제스트
Next Post
도움말이 낡는 자리