Back to feed
ASH avatar
ASH

2026. 5. 4.·base·

Docker Compose cpus 제한이 p95를 터뜨리는 이유

NestJS 트러블슈팅

로컬에서 BoostAD 부하 테스트 중, 평소엔 잘 되던 NestJS 백엔드 컨테이너가 갑자기 처리 시간이 급격히 증가했다. 원인은 docker compose 실행 시 아래 환경변수 라인을 빼먹어 리소스 제한이 의도치 않게 빡세게 걸린 것이었다.

shell
# 예시: CPU 2코어 / RAM 4GB로 제한, 이전 project의 볼륨을 쓰기 위해 '-p web27-boostcamp'
BACKEND_CPUS=2.0 BACKEND_MEM_LIMIT=4g \ 
docker compose -p web27-boostcamp -f docker-compose.local.yml -f docker-compose.local.backend.yml up -d --build backend mysql redis

• 원래 의도: BACKEND_CPUS=2.0, BACKEND_MEM_LIMIT=4g
• 실제 실행(실수): 디폴트값인 CPUS=1, 메모리=1GB 수준으로 제한

Node.js는 싱글 스레드라 어차피 최대 cpus=1까지만 사용이 가능한데 왜 문제가 생겼던 것일까?

그건 바로 cpus=1에서 1코어조차 안정적으로 못쓰는 스로틀링이 발생하기 때문이다.
cpus=1이면 컨테이너 전체 예산이 빡빡해져서 메인 스레드가 동기 CPU 작업을 수행하는 동안 cpus로 컨테이너에게 할당된 CPU 예산이 빠르게 소진이 되어 스로틀링이 걸리게되고 이 때문에 메인 스레드가 강제로 쉬는 구간이 생기게 된다.
이 때문에 큐에 작업이 계속 쌓이게 되고 레이턴시가 폭발하게 되는 것이었다.

cpus=2로 수정했을 때 이 문제가 바로 정상화가 됐는데 그건 cpus=2옵션이 코어 두개를 쓸 수 있게하는 옵션이라 병렬처리가 되어서가 아니고 (애초에 그런 옵션이 아니고, 싱글스레드는 어차피 코어 두개 못씀)
컨테이너가 쓸 수 있는 CPU 예산이 커져서 스로틀링이 줄게되고 따라서 메인스레드가 1코어를 안정적으로 사용할 수 있게되어서 지연이 안정화된 것 이었다.

또 이번 이슈에서는 CPU만이 아니라 메모리 제한도 1GB로 같이 걸렸는데 Node에서 메모리 상한이 낮으면 힙영역의 여유가 부족해서 GC 빈도가 늘어나 GC pause도 많이 발생하고 GC 작업 자체도 CPU를 많이 써 cpus=1한도를 더 빨리 소진시켜 스로틀링을 악화시키기도 했다.

즉 메모리 압박 -> GC 증가 -> CPU 소모 증가 -> 스로틀링 증가 -> 이벤트루프 지연 증가의 연쇄가 발생한 것이다.

0
Comments

Join the thread

Leave feedback, ask for clarification, or keep a focused discussion attached to this article.

0 comments
No comments yet. Start the first thread for this article.
Current user avatar
Styling with Markdown is supported