Python CPU 사용률이 계속 높을 때 가장 흔한 실수는 모든 incident 를 순수한 application logic 문제로 보는 것입니다. 실제 bottleneck 은 hot loop, serialization overhead, worker oversubscription, retry churn, 혹은 끝나지 않는 background task pattern 일 수 있습니다.
짧게 말하면 핵심은 이것입니다. 아무 코드나 최적화하기 전에 어떤 process class 가 CPU 를 태우는지 먼저 알아야 합니다. 한 worker 타입만 뜨겁고 다른 쪽은 안정적이라면, 문제는 “Python 전체가 느리다” 보다 훨씬 좁은 경우가 많습니다.
바쁜 process 부터 본다
먼저 아래를 구분하세요.
- web worker
- background worker
- scheduler
- one-off job
이 구분이 중요한 이유는, 한 worker class 의 high CPU 는 대개 특정 hot path, 특정 concurrency setting, 특정 job type 을 가리키기 때문입니다.
high Python CPU 는 운영에서 어떻게 보이나
보통 이런 모습으로 나타납니다.
- 한 worker class 만 붙고 다른 쪽은 비교적 정상이다
- CPU 와 함께 latency 도 오른다
- retry loop 나 polling pattern 이 프로세스를 계속 뜨겁게 만든다
- 너무 많은 worker 가 동시에 경쟁해 total CPU 가 계속 꽉 찬다
- 운영자는 원인을 모른 채 scale 만 한다
첫 목표는 useful work 와 wasted work 를 나누는 것입니다.
흔한 원인
1. hot loop
하나의 tight loop 나 반복 polling path 만으로도 core 하나를 예상 밖으로 태울 수 있습니다.
특히 이런 곳이 흔합니다.
- real backoff 없는 retry logic
- busy waiting
- 반복적인 scanning/filtering
- 잘 끝나지 않는 loop condition
2. serialization / parsing overhead
JSON encode/decode, template rendering, 큰 payload transformation 은 생각보다 빨리 CPU-heavy 해질 수 있습니다.
작은 테스트에서는 harmless 해 보이지만, 운영 payload 크기에서는 지배적인 경로가 되기도 합니다.
3. worker 가 너무 많거나 concurrency 가 과하다
각 worker 가 평범해 보여도 oversubscribed 상태면 total CPU 는 계속 꽉 찰 수 있습니다.
concurrency 증가가 아래를 함께 늘릴 수 있기 때문입니다.
- context switching
- duplicated work
- 병렬 parsing / serialization
- shared resource contention
4. 끝나지 않는 background job
retry loop, scheduler, consumer, task runner 가 request traffic 과 무관하게 steady CPU pressure 를 만들 수 있습니다.
그래서 “CPU 높음” 은 request path 문제만이 아니라 worker shape 문제인 경우도 많습니다.
5. async 또는 queue pressure 가 CPU pressure 로 번진다
event loop 가 overloaded 되거나 task 가 계속 retry 되면, 겉으로는 high CPU 로 보일 수 있습니다.
그래서 CPU incident 는 task lifecycle 과 coordination bug 와도 자주 연결됩니다.
실전 점검 순서
1. 어떤 process class 가 CPU 를 태우는지 찾는다
어떤 process 타입이 뜨거운지도 모른 채 generic Python profiling 으로 들어가면 시간이 많이 낭비됩니다.
이 첫 구분만으로도 범위가 크게 줄어드는 경우가 많습니다.
2. CPU spike 와 traffic / job timing 을 비교한다
질문은 이렇습니다.
- request traffic 과 함께 오르는가
- scheduled job 과 함께 오르는가
- retry 가 시작된 뒤 오르는가
이 구분이 foreground traffic 문제인지, background activity 문제인지 알려 줍니다.
3. loop, parsing, payload-heavy path 를 본다
전형적인 Python CPU hotspot 은 이런 곳입니다.
- 반복 loop
- serialization / deserialization
- 큰 object transform
- text 또는 JSON-heavy request processing
4. worker count 와 concurrency 설정을 확인한다
최근 concurrency 를 높였다면, CPU pressure 는 나쁜 코드 하나보다 oversubscription 때문일 수 있습니다.
5. runtime coordination 문제와 같이 본다
task backlog, loop delay, retry 도 같이 보인다면 CPU 는 computation 보다는 coordination bug 의 결과일 수 있습니다.
예시: 하나의 hot parse path
for item in items:
expensive_parse(item)
하나의 hot Python loop 나 serialization path 만으로도 wider system issue 로 가기 전에 core 하나를 충분히 태울 수 있습니다.
그래서 작아 보이는 CPU bug 가 data transformation 코드 안에 숨어 있는 경우가 많습니다.
hot path 를 찾은 뒤 무엇을 바꾸면 좋나
loop 하나가 뜨겁다면
iteration 당 작업량을 줄이거나, real backoff 를 넣거나, 불필요한 polling 을 멈춰야 합니다.
parsing 또는 serialization 이 지배적이라면
payload 를 줄이고, batching 을 다시 보고, 반복 transformation 을 줄여야 합니다.
worker 가 oversubscribed 라면
host 와 workload 가 감당할 수 있는 수준으로 concurrency 를 낮춰야 합니다.
background work 가 끝나지 않는다면
scale out 보다 retry, job scheduling, consumer behavior 를 먼저 고쳐야 합니다.
CPU 가 async pressure 의 downstream 이라면
task coordination 을 같은 incident 의 일부로 다뤄야 합니다.
장애 중에 던져볼 질문
이 질문이 꽤 유용합니다.
정확히 어떤 process type 과 code path 가 CPU 를 태우고 있고, 그 CPU 는 useful work 인가 repeated waste 인가?
이 질문이 “왜 Python 이 느리지?” 보다 훨씬 빨리 fix 로 이어집니다.
FAQ
Q. high CPU 면 항상 application logic bug 인가
아닙니다. worker oversubscription, retry, polling, coordination failure 도 충분히 원인이 됩니다.
Q. 가장 빠른 첫 단계는 무엇인가
어떤 process class 가 CPU 를 태우는지 찾고, 그걸 traffic 또는 job timing 과 맞춰 보는 것입니다.
Q. 먼저 worker 를 더 늘려야 하나
현재 worker 가 useful work 를 하는지 waste 를 반복하는지 알기 전에는 보통 아닙니다.
Q. asyncio 문제도 CPU pressure 로 보일 수 있나
그렇습니다. retry loop, oversized fan-out, overloaded runtime 은 모두 high CPU 로 나타날 수 있습니다.
Read Next
- runtime pressure 의 중심이 event loop 라면 Python asyncio Event Loop Blocked 와 비교해 보세요.
- CPU pressure 가 worker churn 으로도 이어진다면 Python Gunicorn Workers Restarting 을 이어서 보세요.
- 같은 incident 에서 memory 도 오른다면 Python Memory Usage High 와 같이 보세요.
- 전체 Python 분기 지도를 보려면 Python Troubleshooting Guide 로 가면 됩니다.
Related Posts
- Python asyncio Event Loop Blocked
- Python Gunicorn Workers Restarting
- Python Memory Usage High
- Python Troubleshooting Guide
Sources:
심사 대기 중에는 광고 대신 관련 가이드를 먼저 보여줍니다.
먼저 읽어볼 가이드
검색 유입이 많은 핵심 글부터 이어서 보세요.
- 미들웨어 트러블슈팅 가이드: Redis vs RabbitMQ vs Kafka 개발자를 위한 미들웨어 트러블슈팅 허브 글입니다. Redis, RabbitMQ, Kafka 중 어떤 증상부터 먼저 봐야 하는지와 어떤 문제 패턴이 각 시스템에 가까운지 정리합니다.
- Kubernetes CrashLoopBackOff: 먼저 볼 것들 startup failure, probe, config, resource limit 관점에서 CrashLoopBackOff를 어떻게 나눠서 봐야 하는지 정리한 가이드입니다.
- Kafka consumer lag가 계속 늘 때: 트러블슈팅 가이드 Kafka consumer lag가 계속 늘어날 때 무엇부터 봐야 하는지 정리합니다. poll 주기, 처리 속도, rebalance, consumer 설정까지 실전 기준으로 다룹니다.
- Kafka Rebalancing Too Often 가이드 Kafka consumer group에서 rebalance가 너무 자주 일어날 때 membership flapping, poll timing, protocol, assignment churn을 어떤 순서로 봐야 하는지 설명하는 실전 가이드입니다.
- Docker container가 계속 재시작될 때: 먼저 확인할 것들 exit code, command failure, environment mistake, health check 관점에서 Docker restart loop를 푸는 실전 가이드입니다.
심사 대기 중에는 광고 대신 관련 가이드를 먼저 보여줍니다.