2022 · Nudge Healthcare

메모리 32GB 를 다 쓰고 멈추던 API 서버 정상화

배경

입사하고 4개월쯤 지난 시점이었습니다. 머신마다 메모리 사용량이 이상할 만큼 제각각이었습니다. 어떤 머신은 20GB 넘게 쓰고 있고, 어떤 머신은 1GB 정도만 쓰고 있었습니다. 그리고 거의 모든 머신이 나흘에서 닷새쯤 지나면 강제로 종료되고 재시작됐습니다.

원인 찾기

처음에는 재부팅 규칙이 있는 줄 알았습니다. 다른 팀의 백엔드 서버는 실제로 일주일에 한 번 머신을 재부팅한다고 했습니다. 하지만 인스턴스가 메모리를 20GB 씩 쓰는 것은 재부팅 규칙과 상관없이 무언가 잘못된 상태였습니다.

인스턴스 한 대의 메모리, 닷새 만에 1GB 에서 32GB 까지

며칠 동안 모니터링한 결과, API 서버 어딘가에서 메모리 누수가 일어나 인스턴스의 메모리를 전부 쓰고 마지막에 OOM 으로 강제 종료된다는 사실을 알았습니다. 그런데 Sentry 에도 AWS 모니터링 도구에도 원인이 될 만한 흔적이 남지 않아, 디버깅에 난항을 겪었습니다.

메모리 덤프 뜨기

팀장님께서 상용 서버에 메모리 덤프를 떠서 조사해보자는 아이디어를 주셨습니다. 솔직히 내키지 않았습니다. 덤프를 뜨는 동안 서비스 장애율이 올라갈 수도 있고, 덤프를 가져와도 읽을 줄 몰라 유의미한 결과를 찾기는 어려울 것 같았습니다. 그래도 달리 방법이 없어 우선 해보기로 했습니다.

찾아보니 메모리 덤프 도구가 몇 가지 있었습니다. 팀장님께 공유드리고, 상용 서버에 직접 접속해 두 가지 도구로 메모리를 스캔했습니다. 첫 번째 도구에서는 건질 것이 없었지만, 두 번째 도구에서 같은 오류가 여러 번 발생한 것으로 보이는 덤프 로그를 발견했습니다.

원인과 해결

앱 푸시 발송을 병렬로 바꿔둔 코드가 문제였습니다. 병렬 처리를 위해 스레드를 만드는데, 스레드 안에서 exception 이 발생하면 그 스레드가 회수되지 않고 그대로 남아 메모리 누수가 쌓이고 있었습니다. 스레드 내부의 처리 로직에 try/catch 를 붙여 배포했고, 그 뒤로 모든 인스턴스가 1GB 정도만 쓰는 정상 상태로 돌아왔습니다.

처음에는 해결책이 도저히 떠오르지 않아 스트레스를 받았습니다. 결국 직접 열어서 들여다보면 답은 생각보다 가까운 곳에 있다는 것을 배운 작업이었습니다.