سه سیگنال اصلی را ساختیم: متریکها گفتند «چهقدر»، لاگها گفتند «چرا» و ردیابی نشان داد «زمان در کدام مرحله» صرف شد. حالا یک لایهی عمیقتر اضافه میکنیم که به پرسش آخر پاسخ میدهد: در خودِ کد، زمان پردازنده دقیقاً روی کدام تابع مصرف میشود؟ پاسخ این پرسش با پروفایلینگ پیوسته و 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 و در نهایت منطق برنامه:
SyncWorker تا میانافزارهای Django و WSGIHandler. پهنای هر مستطیل، سهم آن تابع از زمان پردازنده است.نمای جدولی همان داده، توابع را بر اساس بیشترین مصرف پردازنده مرتب میکند. اینجا نکتهی جالبی دیده میشود: پرمصرفترین بخش، اجرای پرسوجوی پایگاهداده (SQLiteCursorWrapper.execute) است؛ دقیقاً همان span ای که در بخش قبل در ردیابی بهشکل SELECT دیدیم:
این همان قدرت همراستا بودن سیگنالهاست: متریکها یک کندی را نشان میدهند، ردیابی آن را به مرحلهی پایگاهداده محدود میکند، و پروفایلینگ در سطح کد تأیید میکند که زمان پردازنده هم دقیقاً همانجا مصرف میشود.
جمعبندی مجموعه
مجموعه کامل شد. از یک Kubernetes ساده با k0s شروع کردیم، یک اپلیکیشن Django را با GitOps مستقر کردیم و سپس هر چهار نوع دادهی Observability را روی آن سوار کردیم:
- متریکها با Mimir؛ «چهقدر و چه زمانی».
- لاگها با Loki؛ «چه اتفاقی و چرا».
- ردیابی با Tempo؛ «زمان در کدام مرحله».
- پروفایلینگ با Pyroscope؛ «در کدام خط از کد».
همهچیز، از اپلیکیشن تا کل استک نظارت و داشبوردها، بهشکل کد در یک مخزن نگهداری میشود و با Argo CD بهطور خودکار مستقر میشود؛ یعنی این آزمایشگاه بهطور کامل قابل بازتولید است. کد کامل در مخزن عمومی مجموعه در دسترس است:
https://github.com/obsernetics/observability-lab
Observability یک ابزار واحد نیست، بلکه کنار هم قرار گرفتن چند سیگنال است؛ وقتی اینها با برچسبهای مشترک به هم پیوند بخورند، رفتن از «مشکلی هست» به «ریشهی مشکل اینجاست» به کار چند دقیقه تبدیل میشود.
بخشهای این مجموعه
- بخش ۱: پیشنیاز، یک Kubernetes ساده با k0s
- بخش ۲: استقرار اپلیکیشن Django با GitOps و Argo CD
- بخش ۳: متریکها با Mimir
- بخش ۴: لاگها با Loki
- بخش ۵: ردیابی (Tracing) با Tempo
- بخش ۶: پروفایلینگ پیوسته با Pyroscope (همین بخش)