대량 데이터 유입 시 지속적으로 pm2 로 기동된 node 의 worker 프로세스 다운 발생했다.
pm2 에서 worker 가 죽으면 다시 restart 시키지 않아서 문제가 계속해서 발생했다.
해결할려고 했지만 원인을 파악하지 못해서 메모리를 늘리거나 임시조치밖에 취할수 없었고
해결하기 위해서 어떤 옵션이 있고 적용해야 하나 확인한 과정을 적는다
Node.js는 Chrome V8 JavaScript 엔진 으로 빌드된 JavaScript
max-old-space-size 세팅
v8에서 제공하는 옵션중 하나 max-old-space-size을 세팅했다.
-max-old-space-size=SIZE (in megabytes)# Sets the max memory size of V8's old memory section. As memory consumption approaches the limit, V8 will spend more time on garbage collection in an effort to free unused memory.
max-old-space-size는 V8's old memory section 을 설정하는 옵션인데 세팅한 값에 가까워지면 메모리 확보를 위해 garbage collection 을 돌린다.
Memory limit of V8 in Node.js
Currently, by default v8 has a memory limit of 512mb on 32-bit and 1gb on 64-bit systems. You can raise the limit by setting --max-old-space-size to a maximum of ~1gb for 32-bit and ~1.7gb for 64-bit systems. But it is recommended to split your single process into several workers if you are hitting memory limits.
On a machine with 2 GB of memory, consider setting this to 1536 (1.5 GB) to leave some memory for other uses and avoid swapping.
(사용하는 노드 버전은 v12 인데 v18 에서도 내용이 같다. )
v8 엔진에서는 32 bit 에서는 기본 512M, 62bit 에서는 1GB
이 값은 max-old-space-size 를 통해서 올릴수 있다.
64-bit systems에서는 1.7gb 까지 지원 된다.
권장하는 방법은 메모리 제한에 도달할 경우 여러 worker로 분할 하는게 좋다고 한다.
참고로 머신이 2GB의 메모리를 가지고 있으면 1536MB으로 세팅하는것이 swapping을 피하기 위해서 좋다고 권장된다.
또 하나의 옵션 max-memory-restart
PM2 allows to reload (auto fallback to restart if not in cluster) an application based on a memory limit/ Please note that the PM2 internal worker (which checks memory), starts every 30 seconds, so you may have to wait a bit before your process gets restarted automatically after reaching the memory threshold.
이 옵션은 v8에서 제공하는 옵션이 아니라 pm2 제공 하는 옵션인데,
PM2는 메모리 제한을 기준으로 응용 프로그램을 다시 로드 할 수 있다.
PM2 내부 작업자(메모리를 검사하는 작업자)는 30초마다 시작되서 임계값에 도달한 후 자동으로 재시작되기 전에는 기다려야 할 수 있다.
그러면 처음 시작할때 max_memory_restart 값은 어떻게 되는것일까?
공식문서에서는 아무리 찾아봐도 나오지 않았고
stackoverflow 에서는 기본값이 1300MB 라고 되어 있다. (공식 제공하는것 아님!)
in cluster
아까 위에 설명한 cluster 모드에 대해
PM2 allows to reload (auto fallback to restart if not in cluster) an application based on a memory
클러스터 모드에서는 max_memory_restart 가 된다는 사람이 있고 안된다는 사람이 있는데
문서상(auto fallback to restart if not in cluster)에서는 안된다고 나온다
메모리 사용량인 max-memory-restart 2G에 도달하기 전에 max-old-space-size 가 설정되있지 않아
pm2에서 재시작이 안되는것으로 예상 했고 그러면서 추가 적인 궁금한게 더 많아졌다.
고민했던 부분
- max-memory-restart 설정이 실제로 pm2 에 잘 적용되어 있을까?
→ 로컬에서 값을 바꾸면서 테스트
→ pm2 prettylist 를 하면 max-memory-restart 값 확인 가능하다
→ pm2 restart 로 하면 변경된 값이 적용이 안되고 pm2 delete 하고 지운다음에 다시 띄워야 해당값이 적용된다.
→ 확인 결과 잘 나와있음
- max-old-space-size 를 2G로 설정하면 처음부터 2G의 메모리를 점유 하는거 아닐까
마치 자바의 gc 튜닝할때 메모리 설정하는것 처럼...?
→ 로컬에서 확인 결과 안하는것으로 보임
해결 방안
- max-old-space-size 값을 세팅한다 - 2G(기본값 1300MB)
max-memory-restart는 전체 memory이므로 old-space-size 도 포함된 메모리 설정임
→ old-space-size가 임계점에 도달했을때(1300MB) max-memory 는 2G에 도달 했는지는 알 수 없다.
→ 재시작이 정상 동작하는것으로 보아(아래 로그에서 확인) max-memory-restart 동작하는것 같다.
PM2 log: [PM2][WORKER] Process 3 restarted because it exceeds --max-memory-restart value (current_memory=2174074880 max_memory_limit=2147483648 [octets])
- max-old-space-size 값은 v8에서 제공하는 권장값이 있음.(컴퓨터 메모리 2GB 당 1536MB)
- max-memory-restart 값은 권장 값이 없다.
worker에서 pm2 log를 보면 max-memory-restart 가 정상 동작 하는것으로 보인다.
**max-old-space-size 값은 설정 되지 않았는데 메모리 2GB 당 1536MB 로 설정한다.
- auto fallback to restart if not in cluster
→ 위 로그를 봤을때 클러스터 모드에서도 정상동작을 확인 - 알 수 없는 원인으로 kill 하는 이슈
max-memory-restart 로그가 뜨지 않았는데 app 을 Stopping 하는 로그가 보인다.
04-20T14:13:49: PM2 log: Stopping app:worker id:_old_1
04-20T14:13:49: PM2 log: pid=27255 msg=failed to kill - retrying in 100ms
04-20T14:13:49: PM2 log: App name:worker id:_old_1 disconnected
04-20T14:13:49: PM2 log: App [worker:_old_1] exited with code [0] via signal [SIGINT]
04-20T14:13:49: PM2 log: pid=27255 msg=process killed
04-20T17:27:08: PM2 log: [PM2][WORKER] Process 3 restarted because it exceeds --max-memory-restart value (current_memory=2174074880 max_memory_limit=2147483648 [octets])
04-20T17:27:08: PM2 log: App [worker:3] starting in -cluster mode-
04-20T17:27:08: PM2 log: App [worker:3] online
뭔가 설정은 다시 잘 잡고도 계속해서 문제가 있는것 같아서 결국 heapdump를 떠서 메모리 분석을 하기로 했다.
3. npm heapdump 모듈을 통해서 어떤게 메모리 누수를 일으키는지 확인 해본다.
- 해당 모듈은 메모리를 복사하기 때문에 2배의 메모리가 사용되어 진다.
- 누수가 발생할때가 정확히 언제인지 모르기 때문에 스케쥴러나 다른 수단이 필요하다.
- heapdump 모듈에서 지원해주는것 중에 하나는 unix 플랫폼일때 SIGUSR2 시그널을 pid아이디로 보내게 되면 힙덤프가 남게된다.
- 노드가 돌고 있는 동안에 시그널을 보내서 heapdump 스냅샷을 찍을수 있도록 했다.
스냅샷 후 dump 분석을 통해서 메모리 누수를 확인했다.
'개발' 카테고리의 다른 글
Azure Virtual Machines 설명 (0) | 2023.03.29 |
---|---|
Azure의 핵심 아키텍처 구성 요소 간단 정리 (0) | 2023.03.24 |
spring cloud stream rabbitmq 처리량을 늘리는 방법 (0) | 2023.01.13 |
Spring Cloud 모듈들 간단한 설명 (0) | 2023.01.10 |
쉽게 이해할수 있는 Consistent Hashing (0) | 2023.01.09 |