Python 트러블슈팅 가이드: 메모리, 로그, 비동기 장애를 어디부터 볼까
마지막 업데이트

Python 트러블슈팅 가이드: 메모리, 로그, 비동기 장애를 어디부터 볼까


Python 장애는 원인을 빨리 아는 것보다, 첫 분기를 잘 고르는 쪽이 더 중요할 때가 많습니다. 메모리 문제처럼 보여도 실제로는 backlog가 먼저일 수 있고, asyncio가 의심돼도 관측성이 약해서 로그부터 바로잡아야 하는 경우도 있습니다.

이 글은 Python 운영 이슈를 증상 기준으로 분기하기 위한 허브입니다. 아래 질문에 답하도록 구성했습니다.

  • 지금 보이는 장애가 메모리, 로그, 비동기, worker 압박 중 어디에 더 가까운지
  • 초반 5분 안에 어떤 신호를 보면 다음 글을 빨리 고를 수 있는지
  • Python 장애에서 자주 하는 오진은 무엇인지

결론부터 말하면 Python 장애는 “가장 먼저 신뢰할 수 있는 증상”을 기준으로 출발하는 편이 가장 안전합니다. FastAPI 서비스에서 응답이 점점 느려지는 장애가 있었는데, 처음에는 asyncio 이벤트 루프 문제인 줄 알고 task 상태를 뒤졌습니다. 알고 보니 DB 커넥션 풀이 고갈되어 모든 요청이 커넥션 대기 상태에 빠져 있었고, asyncpg 풀 사이즈를 올리고 idle timeout을 조정하니 즉시 해결됐습니다. 비동기 문제처럼 보이지만 실제 병목은 downstream이었던 전형적인 케이스입니다.


이 허브가 필요한 순간

  • 메모리가 계속 늘어난다
  • 운영 로그가 비거나 들쭉날쭉하다
  • asyncio task가 끝나지 않거나 너무 빨리 취소된다
  • CPU는 바쁜데 처리량이 기대만큼 나오지 않는다
  • worker 수를 늘렸더니 메모리와 오버헤드만 같이 늘었다
  • DB나 외부 네트워크 경계에서 요청이 오래 머문다

이 단계에서 중요한 건 해결책을 바로 적용하는 것이 아니라, 어떤 종류의 문제를 먼저 의심해야 하는지 좁히는 일입니다.

먼저 믿을 수 있는 관측인지 확인하세요

Python 이슈는 관측이 흔들리면 거의 모든 조사 단계가 같이 흔들립니다. 로그가 비고 메트릭도 애매하면, 메모리 문제를 메모리답게 보고 있는지조차 확신하기 어렵습니다.

아래 질문부터 확인해 보세요.

  • 지금 보이는 로그가 실제 흐름을 반영하는가
  • 한 프로세스만 문제인지, 모든 worker가 같은 패턴을 보이는가
  • backlog, stall, memory growth 중 무엇이 먼저 시작됐는가

작은 asyncio 상태 스냅샷 예시

비동기 작업 정체가 의심될 때는 이벤트 루프 안에서 task 상태를 아주 짧게 훑어보는 것만으로도 첫 분기를 고르기 쉬워집니다.

import asyncio

for task in asyncio.all_tasks():
    print(task.get_name(), task.done(), task.cancelled())

이 코드만으로 원인을 확정할 수는 없지만, 작업이 끝나지 않는 쪽인지, 취소가 과한 쪽인지, 아예 루프가 굳은 쪽인지 감을 잡는 데 도움이 됩니다.

메모리 축으로 들어가야 할 때

아래에 가깝다면 Python Memory Usage High부터 보는 편이 좋습니다.

  • 특정 프로세스 또는 모든 worker 메모리가 계속 커진다
  • 트래픽이 줄어도 메모리가 잘 내려오지 않는다
  • 큰 payload, cache, retained object가 의심된다
  • worker 수를 바꾼 뒤 메모리 사용량이 확 뛰었다

worker 프로세스를 늘린 뒤 메모리 사용량이 특히 커졌다면 Python Worker Memory Duplication도 바로 이어서 보는 것이 좋습니다.

로그 또는 관측성 축으로 들어가야 할 때

아래에 가깝다면 Python Logging Not Showing부터 보세요.

  • 운영 환경에서 로그가 비어 있다
  • basicConfig()가 먹지 않는 것처럼 보인다
  • 환경마다 log level과 handler 동작이 다르다
  • propagation, formatting, handler 중 하나가 흐름을 흐리게 만든다

이 축이 중요한 이유는, 관측성이 약하면 나머지 모든 조사도 절반은 감에 기대게 되기 때문입니다.

asyncio 축으로 들어가야 할 때

비동기 coordination 문제가 더 강하면 아래 글들이 잘 맞습니다.

핵심은 세 가지를 나누는 것입니다.

  • task가 영원히 기다리는가
  • task가 너무 빨리 취소되는가
  • 동기 작업이 이벤트 루프를 막아 전체 스케줄링이 무너졌는가

worker 또는 queue pressure 축으로 들어가야 할 때

운영형 backlog나 처리량 압박이 더 선명하면 아래 글들부터 들어가면 됩니다.

이 축은 시스템이 accepted work를 completed work로 잘 바꾸지 못하는 상황에서 특히 유용합니다.

프로세스 레이아웃 또는 배포 변화 축으로 들어가야 할 때

배포 후부터 증상이 달라졌거나 process model이 바뀐 뒤 문제가 시작됐다면 아래 분기를 먼저 보세요.

애플리케이션 코드보다 worker 수, preload, 배포 형태가 더 많이 바뀌었다면 이쪽이 실제 원인에 더 가깝습니다.

downstream 자원 고갈 축으로 들어가야 할 때

다음에 가깝다면 Python Database Connections Not Closed부터 보는 편이 좋습니다.

  • DB 근처에서 요청이 오래 쌓인다
  • crash보다 pool exhaustion이 먼저 보인다
  • 메모리나 CPU보다 connection starvation이 더 직접적인 문제다

Python 서비스는 실제 원인이 connection pool인데도 처음엔 그냥 느린 서비스처럼 보이는 경우가 많습니다.

자주 하는 오진

로그가 약한데 바로 메모리 튜닝부터 하는 경우

관측성이 약하면 무엇이 먼저 늘었는지조차 잘못 읽을 수 있습니다.

asyncio 문제처럼 보여서 모든 것을 event loop 탓으로 돌리는 경우

실제 원인은 downstream stall, DB 대기, worker backlog일 수도 있습니다. 비동기 문제는 종종 결과로 보입니다.

worker 수를 먼저 올리는 경우

queue 모양과 병목 위치를 모르는데 worker만 늘리면 메모리 오버헤드와 컨텍스트 전환 비용만 커질 수 있습니다.

아주 빠른 분기표

  • 메모리 증가가 제일 선명하다: 메모리 글부터
  • 로그가 비고 관측이 흐리다: 로그 글부터
  • task가 안 끝나거나 취소가 이상하다: asyncio 글부터
  • backlog나 처리량 압박이 먼저 보인다: worker 또는 queue 글부터

그리고 첫 글 하나만 보고 끝내지 말고, 바로 인접 분기 한 개를 더 비교하는 편이 좋습니다. Python 장애는 관측성과 실행 모델이 서로 영향을 많이 주기 때문입니다.

장애 조사 순서

  1. 지금 보이는 로그와 메트릭이 믿을 만한지 먼저 확인합니다.
  2. 시스템이 느린 건지, 멈춘 건지, 계속 불어나는 건지 나눕니다.
  3. 메모리 압박과 처리량 압박을 분리합니다.
  4. asyncio coordination 문제와 process-level capacity 문제를 분리합니다.
  5. 가장 강한 증상에 맞는 좁은 글로 들어간 뒤, 인접 분기 글 하나를 바로 비교합니다.

FAQ

Q. 이 글은 Python 설정 가이드인가요?

아니요. 증상 기준으로 다음 분기를 정하는 허브 글입니다.

Q. 로그 누락과 메모리 증가가 같이 보이면 어디부터 볼까요?

메모리 진단을 신뢰하기 어려울 정도로 관측성이 약하면 로그부터 보세요. 그렇지 않다면 먼저 나타난 증상부터 가면 됩니다.

Q. Celery, asyncio, API timeout이 한꺼번에 보이면요?

가장 먼저 backlog나 stall이 보인 지점부터 잡으세요. 여러 증상은 종종 하나의 초기 병목에서 시작됩니다.

Q. queue 모양도 모르는데 worker 수부터 조정해도 될까요?

보통은 아닙니다. process 수를 바꾸면 메모리 오버헤드만 늘고 실제 원인을 가릴 수 있습니다.

먼저 읽어볼 가이드

검색 유입이 많은 핵심 글부터 이어서 보세요.

광고