본문 바로가기
Infra2026년 7월 26일5분 읽기

지속적 프로파일링 — 관측성의 네 번째 기둥, Pyroscope와 플레임그래프

YS
김영삼
조회 4
지속적 프로파일링 — 관측성의 네 번째 기둥, Pyroscope와 플레임그래프

지속적 프로파일링(continuous profiling)은 운영 중인 서비스에서 CPU·메모리 사용을 아주 낮은 오버헤드로 상시 수집해, 어느 함수가 자원을 먹는지를 시간축으로 남기는 관측성 기법이다. 트레이스가 "요청이 어디서 느렸나"를 보여준다면, 프로파일링은 "그 시간 동안 CPU가 정확히 어떤 코드 줄에서 타고 있었나"를 보여준다. 로그·메트릭·트레이스에 이어 네 번째 기둥이라고 부르는 사람이 많다.

예전엔 프로파일러를 "성능 이상하면 그때 잠깐 붙이는 도구"로만 썼다. 문제는, 진짜 사고는 새벽에 터지고 그때는 이미 프로파일러가 안 붙어 있다는 것이다. 지속적 프로파일링은 이 딜레마를 없앤다. 항상 켜 두니까, 사후에 "그 시각으로 되감기"가 된다.

어떻게 상시 수집이 가능한가

비결은 샘플링이다. 매 순간을 기록하는 게 아니라 초당 100회 정도 스택 트레이스 스냅샷만 찍는다. 그래서 오버헤드가 보통 CPU 1~2% 수준으로 낮다. 요즘은 eBPF 기반 수집기가 나오면서 애플리케이션 코드를 전혀 안 건드리고도 커널에서 스택을 떠 올 수 있게 됐다. Grafana Pyroscope, Parca, Polar Signals 같은 도구가 이 계열이다.

# Pyroscope 서버를 도커로 띄우기
docker run -d --name pyroscope -p 4040:4040 grafana/pyroscope:latest
# Go 앱에 SDK로 push (pull 방식도 있음)
# import "github.com/grafana/pyroscope-go"
# pyroscope.Start(pyroscope.Config{
#   ApplicationName: "checkout.service",
#   ServerAddress:   "http://pyroscope:4040",
#   ProfileTypes: []pyroscope.ProfileType{
#     pyroscope.ProfileCPU, pyroscope.ProfileAllocObjects,
#   },
# })

플레임그래프 읽는 법 — 폭이 곧 비용

프로파일 데이터는 보통 플레임그래프로 본다. 처음엔 이 그림이 낯설다. 규칙은 간단하다.

  • 가로 폭이 그 함수가 소비한 시간(또는 샘플 수)에 비례한다. 넓을수록 비싸다.
  • 세로는 호출 스택의 깊이다. 위로 쌓일수록 더 안쪽에서 호출된 함수다.
  • 맨 위에서 넓고 평평한 봉우리를 찾는다. 거기가 실제로 CPU를 태우는 지점, 즉 "self time"이 큰 함수다.

색깔은 대개 의미가 없다(가독성용). 색으로 뭘 판단하려다 삽질한 적이 있는데, 볼 건 오직 폭이다. 개인적으로는 플레임그래프보다 두 시점을 빼서 보여주는 diff 뷰가 더 실전적이라고 본다. 배포 전/후를 겹쳐 보면, 어떤 함수가 갑자기 넓어졌는지가 빨간색으로 도드라진다.

실제로 이걸로 잡은 것들

가장 흔한 수확은 "설마 저기가?" 싶은 곳이다. 로그 포맷터의 정규식, 매 요청마다 새로 만드는 JSON 직렬화기, 방어적으로 깊게 복사하던 유틸 함수. 트레이스로는 전혀 안 보이던 것들이다. 트레이스는 스팬을 건 곳만 보이지만, 프로파일러는 스팬이 없는 코드까지 다 훑기 때문이다.

신호답하는 질문한계
메트릭문제가 있나? (CPU 80%)원인 위치를 모름
트레이스어느 서비스/스팬이 느린가?스팬 밖 코드는 안 보임
프로파일어느 함수가 자원을 태우나?서비스 간 인과는 약함

도입할 때 챙길 점

먼저 심볼 정보다. 컴파일러가 최적화하며 함수 이름을 날려버리면 플레임그래프가 주소 덩어리로 나온다. 프로덕션 바이너리에도 디버그 심볼을 유지하거나, 심볼 파일을 따로 업로드해 둬야 한다. 그리고 저장 비용. 프로파일은 생각보다 부피가 크니 보존 기간을 짧게(예: 며칠~2주) 두고 중요한 구간만 스냅샷으로 남기는 편이 현실적이다.

자주 묻는 질문

프로파일러를 항상 켜 두면 성능이 나빠지지 않나요?

샘플링 기반이라 보통 CPU 1~2%, 메모리도 소폭 증가에 그칩니다. 매 함수 호출을 다 기록하는 계측형(instrumenting) 프로파일러와 달리, 지속적 프로파일러는 초당 수십~수백 회 스택 스냅샷만 뜨기 때문에 상시 운영이 가능합니다.

트레이싱이 있으면 프로파일링은 필요 없지 않나요?

겹치지 않습니다. 트레이싱은 스팬을 건 경계에서 서비스 간 흐름을 보여주고, 프로파일링은 한 프로세스 안에서 스팬이 없는 코드까지 함수 단위로 훑습니다. CPU 100%인데 트레이스는 멀쩡해 보이는 상황이 딱 프로파일러가 필요한 순간입니다.

eBPF 방식과 SDK 방식 중 뭘 써야 하나요?

코드를 못 건드리거나 여러 언어가 섞여 있으면 eBPF 방식이 편합니다(재배포·재컴파일 불필요). 대신 언어 런타임 내부의 세밀한 정보나 커스텀 라벨이 필요하면 SDK 방식이 더 풍부합니다. 둘을 함께 쓰는 팀도 많습니다.

플레임그래프에서 뭘 먼저 봐야 하나요?

맨 위에서 가로로 가장 넓고 평평한 봉우리입니다. 그 함수가 실제로 자원을 태우는 지점입니다. 색깔은 대개 가독성용이라 의미를 두지 마세요. 배포 전후를 비교할 땐 diff 뷰가 원인을 훨씬 빨리 드러냅니다.

댓글 0

아직 댓글이 없습니다.
Ctrl+Enter로 등록