TT Lab
블로그 시작하기
배우기 러닝패스 코스

프로파일링: 느린 요청 하나를 함수까지

이미 떠 있는 프로세스를 밖에서 붙잡는다

TT Lab 에서 이어서 보기

이 실습은 VM 에서 돕니다

우분투 24.04 VM 한 대입니다. root 이고 커널 권한이 있습니다. 이 실습의 도구들은 남의 프로세스나 커널에 붙는데, 실습 파드에서는 그 문이 닫혀 있습니다(아래 참고에 잰 값이 있습니다). 그래서 이 실습만 VM 에서 돕니다. 처음 뜨는 데 몇 분 걸립니다.

목표

코드를 고치거나 다시 띄울 수 없는 프로세스를 밖에서 들여다봅니다. 떠 있는 파이썬 일꾼을 py-spy 로, C 프로그램을 perf 로 프로파일하고, 멈춘 프로그램의 스택을 gdb 로 꺼내고, 코어 덤프를 남겨 나중에 읽고, 마지막으로 eBPF(bpftrace)로 시스템 전체를 한 번에 표본합니다.

왜 중요한가

앞 실습들은 프로그램이 스스로 프로파일을 내도록 만들어 두었습니다. 현실의 장애는 그런 준비가 없는 프로세스에서 납니다. 다시 띄우면 증상이 사라지는 일도 많습니다. 그때 쓰는 것이 붙잡는 도구들입니다. 이 도구들은 강력한 만큼 커널에 특별한 권한(ptrace, perf_event_open, bpf)을 요구하고, 그 권한은 곧 남의 메모리를 읽는 권한입니다. 어떤 도구가 어떤 문을 여는지 알아야 운영 환경에서 무엇을 허락하고 무엇을 막을지 판단할 수 있습니다.

단계

  1. 커널 판, kernel.yama.ptrace_scope, kernel.perf_event_paranoid, 세 서비스의 상태를 /root/prof/01-env.txt 에 저장하세요.
  2. py-spy dump 로 prof-slowapi 의 지금 스택을 /root/prof/02-dump.txt 에 저장하세요.
  3. py-spy record 로 그 프로세스를 10초 표본해 접은 스택(raw 형식)을 /root/prof/03-pyspy.folded 에 저장하세요.
  4. perf record -g 로 crunch 를 10초 표본해 /root/prof/04-perf.data 에, 함수별 표를 /root/prof/04-perf.txt 에 저장하세요.
  5. 멈춘 stuck 에 gdb -p 로 붙어 모든 스레드의 스택을 /root/prof/05-bt.txt 에 저장하세요.
  6. gcore 로 stuck 의 코어를 /root/prof/06-stuck.<pid> 에 남기고, 그 코어에서 꺼낸 스택을 /root/prof/06-core-bt.txt 에 저장하세요.
  7. 코어가 /root/prof/cores/ 에 떨어지도록 kernel.core_pattern 을 바꾸고(재부팅 뒤에도 남게 /etc/sysctl.d/60-prof-core.conf 에도), crashy 를 죽게 해 코어를 남긴 뒤 그 스택을 /root/prof/07-crash-bt.txt 에 저장하세요.
  8. bpftrace 의 profile:hz:99 로 10초 동안 시스템 전체를 표본해 프로세스 이름별 횟수를 /root/prof/08-bpf.txt 에 저장하세요.
  9. 도구마다 커널에 무엇을 요구했는지 /root/prof/09-report.md 에 정리하세요.

참고

이 자리에서 열려 있는 문

uname -r, sysctl kernel.yama.ptrace_scope kernel.perf_event_paranoid, systemctl is-active prof-slowapi prof-crunch prof-stuck 의 출력을 /root/prof/01-env.txt 에 이어 저장하세요.

ptrace_scope 는 「누가 누구에게 붙을 수 있나」, perf_event_paranoid 는 「누가 성능 카운터를 열 수 있나」입니다. root 는 둘 다 넘을 수 있습니다. 실습 파드의 값과 견줘 보세요.

파이썬의 지금 스택

py-spy dump --pid <prof-slowapi 의 PID> 의 출력을 /root/prof/02-dump.txt 에 저장하세요. 일꾼 스레드에서 price_rules 가 보여야 합니다.

dump 는 한 순간의 스택입니다. 코드를 고치거나 다시 띄우지 않고 「지금 무엇을 하고 있나」를 봅니다. 여러 번 찍으면 자주 보이는 곳이 뜨거운 곳입니다.

파이썬을 표본한다

py-spy record --pid <PID> --duration 10 --rate 100 --format raw --output /root/prof/03-pyspy.folded 로 10초 표본하세요. raw 형식은 플레임 그래프의 원자료인 접은 스택입니다.

py-spy 는 대상 프로세스의 메모리를 밖에서 읽어(process_vm_readv) 파이썬 스택을 다시 짓습니다. 대상에는 아무 코드도 넣지 않습니다.

네이티브 프로그램을 표본한다

perf record -F 99 -g -p <crunch 의 PID> -o /root/prof/04-perf.data -- sleep 10 으로 표본하고, perf report -i /root/prof/04-perf.data --stdio --no-children --sort symbol 의 출력을 /root/prof/04-perf.txt 에 저장하세요.

-F 99 는 초당 99번입니다. 100 이 아닌 이유는 다른 주기적인 일과 박자가 맞아 한쪽만 찍히는 것을 피하려는 관습입니다. -g 는 호출 그래프(스택)를 함께 받습니다.

멈춘 프로그램의 스택

gdb -p <stuck 의 PID> -batch -ex 'thread apply all bt' 의 출력을 /root/prof/05-bt.txt 에 저장하세요. 두 스레드가 각각 어느 함수에서 무엇을 기다리는지 보여야 합니다.

CPU 를 쓰지 않고 멈춘 프로세스에는 CPU 프로파일이 아무것도 말해 주지 않습니다. 이럴 때는 스택 한 장이 답입니다. 두 스레드가 서로 상대가 쥔 잠금을 기다리면 교착입니다.

프로세스를 멈추지 않고 코어를 뜬다

gcore -o /root/prof/06-stuck <stuck 의 PID> 로 코어를 남기고(/root/prof/06-stuck.<pid>), gdb -batch -ex 'thread apply all bt' /opt/prof/bin/stuck <코어 파일> 의 출력을 /root/prof/06-core-bt.txt 에 저장하세요.

코어는 그 순간 메모리의 사진입니다. 남겨 두면 프로세스를 되살린 뒤에도, 다른 기계에서도 같은 스택을 다시 볼 수 있습니다. gcore 는 잠깐 멈췄다 놓아 주므로 프로세스는 계속 돕니다.

죽은 프로그램이 남긴 것

kernel.core_pattern 을 /root/prof/cores/core.%e.%p 로 바꾸고 같은 줄을 /etc/sysctl.d/60-prof-core.conf 에도 적은 뒤, ulimit -c unlimited 셸에서 /opt/prof/bin/crashy 를 실행해 코어를 남기고, gdb -batch -ex bt /opt/prof/bin/crashy <코어> 의 출력을 /root/prof/07-crash-bt.txt 에 저장하세요.

코어를 어디에 쓸지는 커널이 정합니다(core_pattern). 우분투 기본값은 apport 로 보내는 파이프라서 그대로면 실습 자리에 아무것도 안 남습니다. 크기 제한(ulimit -c)이 0 이어도 남지 않습니다.

시스템 전체를 한 번에

bpftrace -e 'profile:hz:99 { @[comm] = count(); } interval:s:10 { exit(); }' 의 출력을 /root/prof/08-bpf.txt 에 저장하세요. 프로세스 이름별 표본 수가 나옵니다.

앞의 도구들은 프로세스 하나를 골라 붙었습니다. eBPF 프로파일러는 커널의 타이머에 작은 프로그램을 걸어 모든 CPU 에서 무엇이 돌고 있는지 한 번에 셉니다. 어느 프로세스가 범인인지 모를 때 먼저 씁니다.

도구마다 연 문

/root/prof/09-report.md 에 ## 도구와 권한 절을 두고 py-spy·perf·gdb·bpftrace 가 각각 커널에 무엇(ptrace·process_vm_readv·perf_event·bpf)을 요구했는지, ## 파드에서는 절에 실습 파드에서 그중 무엇이 막혀 있는지 쓰세요.

1단계에 적은 값과 참고의 파드 측정값을 나란히 두면 됩니다. 이 표가 운영 환경에서 프로파일러에 어떤 권한을 줄지 정할 때의 근거가 됩니다.