느린 파이썬 서버, 재시작 없이 Tachyon으로 병목 찾기

가끔 느려지는 서버를 재시작하면 그 순간의 원인도 함께 사라진다. 실행 중인 프로세스에 Tachyon을 붙이면 무엇을 어디까지 볼 수 있을까?

느린 파이썬 서버, 재시작 없이 Tachyon으로 병목 찾기
세 줄 요약
  • Python 3.15의 profiling.sampling은 실행 중인 프로세스에 attach해 호출 스택을 수집한다.
  • wall과 cpu 모드를 나눠 보면 대기가 긴 구간과 연산이 많은 구간을 구분하는 데 도움이 된다.
  • 연결 전 Python 버전·빌드·권한을 맞추고, 스레드와 워커 프로세스의 범위를 확인해야 한다.

서버를 다시 띄우지 않고 30초를 관찰한다

느려진 서버를 재시작하면 정작 보고 싶었던 상태가 사라질 수 있다. Python 3.15의 Tachyon은 프로세스 밖에서 호출 스택을 주기적으로 읽는 내장 샘플링 프로파일러다. 코드에 측정 구문을 넣지 않고 실행 중인 PID에 연결할 수 있다.

다음은 대상 프로세스를 30초 동안 관찰해 HTML 플레임 그래프로 저장하는 명령이다. 12345는 예시 PID이며, 실제 요청을 처리하는 Python 프로세스의 PID로 바꿔야 한다. python3.15 역시 대상과 맞는 인터프리터 실행 경로를 뜻한다.

python3.15 -m profiling.sampling attach --flamegraph -d 30 -o wall.html 12345

attach 명령은 정해진 시간만큼 수집한 뒤 연결을 끝내고 결과를 만든다. wall.html을 브라우저에서 열면 어느 호출 경로가 많은 샘플을 차지했는지 볼 수 있다. 관찰 시간은 느린 요청이 발생하는 구간을 포함하도록 정한다.

sleep이 넓게 보인다고 CPU가 바쁜 것은 아니다

기본 wall 모드는 입출력·잠금·sleep 대기도 포함한다. 외부 API를 기다리는 함수가 크게 보일 수 있다. 계산량을 보고 싶다면 cpu 모드로 실제 CPU에서 실행 중인 스레드의 샘플에 집중한다.

python3.15 -m profiling.sampling attach --mode cpu --flamegraph -d 30 -o cpu.html 12345
관찰한 모습다음 조사 방향
wall에서 크고 cpu에서는 작다.해당 호출 경로의 네트워크·디스크·잠금·대기를 살핀다.
두 모드에서 모두 크다.그 경로에서 많은 연산을 하는 코드를 살핀다.

두 번의 수집은 서로 다른 시간에 일어난다. 요청 종류와 부하가 비슷한 구간을 골라야 모드 차이와 트래픽 차이를 덜 혼동한다. 플레임 그래프의 폭만으로 외부 서비스나 특정 알고리즘을 원인으로 확정하지 않고, 해당 함수가 수행하는 작업으로 조사 범위를 좁힌다.

버전·권한·관찰 범위를 맞춘다

프로파일러와 대상은 같은 Python 마이너 버전이어야 한다. 사전 배포판이 섞이면 정확히 같은 버전이어야 하며, 일반 빌드와 free-threaded 빌드 사이의 교차 연결도 지원하지 않는다. 단지 서버에 Python 3.15를 추가 설치했다고 이전 버전 프로세스를 그대로 관찰할 수 있는 것은 아니다.

다른 프로세스의 메모리를 읽을 권한도 필요하다. Linux에서는 ptrace 관련 권한·정책, macOS에서는 root 또는 디버거 권한, Windows에서는 관리자 권한이나 SeDebugPrivilege가 관계한다. 운영체제별 연결 조건에 따라 허용된 진단 계정과 실행 파일을 선택한다.

기본 관찰 대상은 메인 스레드다. 스레드 풀의 작업까지 보려면 -a를 추가한다. 여러 워커가 별도 프로세스로 돌아간다면 한 PID의 결과를 전체 서버의 결과로 해석해서는 안 된다. 먼저 느린 요청을 처리하는 워커를 찾아야 한다.

샘플은 자주 머문 위치를 보여준다

샘플 100개에서 함수가 발견됐다고 그 함수가 100번 호출된 것은 아니다. 긴 호출 하나가 여러 번 관찰될 수도 있다. 정확한 호출 횟수와 전체 호출 관계가 필요하면 profiling.tracing 같은 결정적 프로파일러가 맞는다. 기존 cProfile 이름도 호환을 위해 유지된다.

Tachyon은 기본적으로 대상을 멈추지 않고 읽지만, 프로파일러 자체도 머신 자원을 사용한다. 별도 --blocking 옵션은 읽는 동안 대상을 잠시 멈추므로 기본 실행과 구분해야 한다. 운영 조사에서는 짧은 관찰 구간과 필요한 범위부터 시작해, 대기가 많은 호출인지 계산이 많은 호출인지에 맞춰 다음 측정을 고르는 편이 낫다.