راه‌اندازی Observability با LGTM و Pyroscope، بخش ۶: پروفایلینگ پیوسته با Pyroscope

سه سیگنال اصلی را ساختیم: متریک‌ها گفتند «چه‌قدر»، لاگ‌ها گفتند «چرا» و ردیابی نشان داد «زمان در کدام مرحله» صرف شد. حالا یک لایه‌ی عمیق‌تر اضافه می‌کنیم که به پرسش آخر پاسخ می‌دهد: در خودِ کد، زمان پردازنده دقیقاً روی کدام تابع مصرف می‌شود؟ پاسخ این پرسش با پروفایلینگ پیوسته و Pyroscope به‌دست می‌آید.

پروفایلینگ پیوسته و flame graph

پروفایلر به‌طور مرتب از پشته‌ی فراخوانی برنامه نمونه‌برداری می‌کند و می‌شمارد که هر تابع چند بار «در حال اجرا روی پردازنده» دیده شده است. نتیجه به‌شکل یک flame graph نمایش داده می‌شود: هر مستطیل یک تابع است و پهنای آن نشان می‌دهد چه سهمی از زمان پردازنده را گرفته است. «پیوسته» یعنی این کار همیشه و با هزینه‌ی بسیار کم در محیط عملیاتی انجام می‌شود، نه فقط هنگام اشکال‌زدایی.

ابزارگذاری اپلیکیشن

برای زبان Python از SDK رسمی Pyroscope استفاده می‌کنیم. آن را در همان قلاب post_fork گانی‌کورن (کنار OpenTelemetry) پیکربندی می‌کنیم تا هر worker پروفایل خودش را مستقیم به Pyroscope بفرستد:

import pyroscope

pyroscope.configure(
    application_name="guestbook",
    server_address="http://pyroscope.observability.svc:4040",
    tags={"env": "lab"},
)

Pyroscope نیز مانند بقیه‌ی اجزا به‌شکل مانیفست در همان مخزن قرار می‌گیرد و Argo CD آن را مستقر می‌کند. سپس آن را به‌عنوان یک datasource به Grafana هم معرفی می‌کنیم.

خواندن flame graph

پس از تولید ترافیک روی اپلیکیشن، Pyroscope نمودار مصرف پردازنده در طول زمان و flame graph مربوط به آن را نشان می‌دهد. از بالا به پایین، مسیر فراخوانی دیده می‌شود: از گانی‌کورن و worker ها تا لایه‌های میان‌افزار Django و در نهایت منطق برنامه:

flame graph مصرف پردازنده اپلیکیشن در Pyroscope
flame graph اپلیکیشن در Pyroscope: مسیر از گانی‌کورن و SyncWorker تا میان‌افزارهای Django و WSGIHandler. پهنای هر مستطیل، سهم آن تابع از زمان پردازنده است.

نمای جدولی همان داده، توابع را بر اساس بیشترین مصرف پردازنده مرتب می‌کند. اینجا نکته‌ی جالبی دیده می‌شود: پرمصرف‌ترین بخش، اجرای پرس‌وجوی پایگاه‌داده (SQLiteCursorWrapper.execute) است؛ دقیقاً همان span ای که در بخش قبل در ردیابی به‌شکل SELECT دیدیم:

جدول پرمصرف‌ترین توابع از نظر پردازنده در Pyroscope
نمای جدولی: توابع بر اساس زمان پردازنده مرتب شده‌اند. اجرای پرس‌وجوی پایگاه‌داده در صدر است، هم‌راستا با یافته‌ی بخش ردیابی.

این همان قدرت هم‌راستا بودن سیگنال‌هاست: متریک‌ها یک کندی را نشان می‌دهند، ردیابی آن را به مرحله‌ی پایگاه‌داده محدود می‌کند، و پروفایلینگ در سطح کد تأیید می‌کند که زمان پردازنده هم دقیقاً همان‌جا مصرف می‌شود.

جمع‌بندی مجموعه

مجموعه کامل شد. از یک Kubernetes ساده با k0s شروع کردیم، یک اپلیکیشن Django را با GitOps مستقر کردیم و سپس هر چهار نوع داده‌ی Observability را روی آن سوار کردیم:

  • متریک‌ها با Mimir؛ «چه‌قدر و چه زمانی».
  • لاگ‌ها با Loki؛ «چه اتفاقی و چرا».
  • ردیابی با Tempo؛ «زمان در کدام مرحله».
  • پروفایلینگ با Pyroscope؛ «در کدام خط از کد».

همه‌چیز، از اپلیکیشن تا کل استک نظارت و داشبوردها، به‌شکل کد در یک مخزن نگه‌داری می‌شود و با Argo CD به‌طور خودکار مستقر می‌شود؛ یعنی این آزمایشگاه به‌طور کامل قابل بازتولید است. کد کامل در مخزن عمومی مجموعه در دسترس است:

https://github.com/obsernetics/observability-lab

Observability یک ابزار واحد نیست، بلکه کنار هم قرار گرفتن چند سیگنال است؛ وقتی این‌ها با برچسب‌های مشترک به هم پیوند بخورند، رفتن از «مشکلی هست» به «ریشه‌ی مشکل اینجاست» به کار چند دقیقه تبدیل می‌شود.

بخش‌های این مجموعه

← بازگشت به وبلاگ