서비스를 운영하다보면 꼭 서버가 죽을 때가 생긴다. 코드를 잘못 짠 걸테고, 그게 내가 짰던, 우리 팀 멤버가 짰던, 아니면 어디서 가져다 쓰는 라이브러리의 버그던, 톰캣의 버그던, 하여튼 어딘가 문제가 생겨서 죽을 것이다. 사람이 하는 일이 그렇듯, 언젠가 누군가는 분명히 실수를 할 것이다. 물론 평소에 잘 해야겠지만 그렇다고 서버가 어떤 일이 있어도 안 죽는 평화로운 날은 영원히 오지 않을 것이다.
그렇다고 서버가 죽었는데 손을 놓고 있을 순 없으니, 여러 대를 띄워놓았다면 다른 서버로 돌리거나, 재시작을 해보거나, 기존 버전으로 돌리거나, 긴급하게 핫픽스를 해서 다시 돌아가게 해야 할 것이다. 일단 급하게 땜빵은 해야 한다. 그럼 그 다음에는? 왜 그런지 근본 원인을 찾아서 제거해야 다음에 또 그러지 않을 것이다.
예를 들어 자바 앱의 경우는 문제가 생기면 바로 죽이지 말고 일단 덤프를 떠놓고 나중에 분석한다. 쓰레드 덤프와 힙 덤프를 뜨는데, 몇 초 안에 끝나니까 그렇게 부담은 없다. 몇 초의 장애 시간을 추가하더라도 덤프를 떠놔야 나중에 장애 보고서를 쓸 때 뭐라도 할 말이 생기지 않을까? 애초에 장애를 내면 안되고, 장애를 내는 순간, 아 나는 망했다, 올해 고과 평과는 글렀구나 싶지만, 이미 늦었더라도 늦은대로 뭐라도 할 수 있는 최선을 다해야 하지 않을까. 그래야 조금이라도 만회하지 않을까. 그래서 살짝 덤프를 떠놓고 나중에 뒤져봐야 한다.
장애 리포트는 영어로는 부검 리포트(Postmortem Report)라고 하는데 제일 중요한 건 근본 원인을 파악(Root Cause Analysis)하는 것이다. 임시방편(workaround)으로 때우는 것도 한두번이지, 적어도 재발은 막아야 하지 않을까. 생각하는 의자(Naughty Chair)에 앉혀서 며칠동안 다른 업무를 중단하고 오로지 장애 리포트만 쓰게 한다. 그래서 다음날 임원 분들 앞에서 발표하면서 하나에 정신, 둘에 통일, 정신을, 차리자… 이러는 건 아니고 하여튼 앞으로 안 그러면 된다.
하여튼 그래서 자바 덤프를 뜨는 방법은 다음과 같다.
jstack {pid} > {fileName} # thread dump
jmap -dump:format=b,file={fileName} {pid} # heap dump
example:
jstack 12345 > thread_dump_1.tdump
jstack 12345 > thread_dump_2.tdump
jstack 12345 > thread_dump_3.tdump
jmap -dump:format=b,file=heap_dump_1.hprof 12345
jmap -dump:format=b,file=heap_dump_2.hprof 12345
jmap -dump:format=b,file=heap_dump_3.hprof 12345
덤프를 여러 개 뜨는 건 변화를 보기 위해서다. 보통 자바 앱이 죽는게 뭔가 CPU나 메모리를 엄청 먹어서인데, 이게 무거워도 잠깐 돌다 만다면 죽는 정도까지는 아니겠지만, 계속 무거우면 죽을 것이다. 그럼 몇 초 동안 계속 똑같은게 엄청 잡아먹는다면 그놈이 문제일 수 있다. 물론 디스크가 꽉 차도 죽을 수 있는데, 이건 자바 덤프를 떠봤자 모르니까 디스크를 봐야 한다. 10년 전만 해도 디스크가 꽉차면 log4j에서 멈춰서 세상을 멈추곤(stop-the-world) 했는데, 요즘은 그렇게까지는 아니지만 하여튼 디스크는 du –max-depth 1 -h 해서 따라 내려가면 되니까 다른 얘기다.
참고로 당연하겠지만 덤프를 뜨는 것 자체가 부하를 주는 일이고, 특히 힙 덤프처럼 메모리를 4G 쓰면 덤프도 4G, 8G를 쓰면 덤프도 8G가 나오는 덩치일 경우에는 정말로 몇 초동안 세상이 멈추니까(stop-the-world) 평소에는 안하거나 특별히 해도 괜찮은 환경을 만들어서 따로 해야한다. 장애니까 특별히 하는 거다.
그런 다음 덤프를 분석하는 툴은 여러가지가 있는데, IBM 것은 아래와 같다.
- Thread analyzer : https://www.ibm.com/support/pages/ibm-thread-and-monitor-dump-analyzer-java-tmda
- Heap analyzer : https://www.ibm.com/support/pages/ibm-heapanalyzer
그래서 도대체 무슨 쓰레드가 CPU를 많이 먹는건지, 아니면 뭔가 락이 걸려서 CPU를 쓰지도 않는데 멈춘 건지, 그리고 힙에서 어떤 데이터가 메모리를 많이 먹어서 그런건지를 찾으려면, 이 덤프를 한참 까봐야 한다. 매우 고통스러운 일이다. 정말 생각하는 의자에 앉아서 생각하는 벌을 받는 것 같다. 사체 부검(postmortem)이라는 말이 괜히 나온게 아니다. 끔찍한 일이다.
그 전에 혹시라도 자바 앱이 살이있을때, 아직 안 죽어서 죽을랑 말랑 하다면, 떠 있는 쓰레드를 직접 봐서 힌트를 얻을 수 있다.
ps -mo, pid,lwp,stime,pcpu -p {pid}
그래서 CPU를 많이 먹는 lwp를 잡아내서 쓰레드 덤프에서 확인하려면… 안 나온다. 왜냐하면 lwp는 10진수고 쓰레드 덤프의 Native ID는 16진수이기 때문이다. 변환해서 찾으면 된다.
그리고 힙 덤프를 분석할 때 아까도 얘기했듯이 힙 덤프 자체가 4G, 8G 이렇게 엄청 크니까, 내 컴퓨터의 메모리를 충분히 비우고 돌려야지 안그러면 이것도 자꾸 죽는다.