Прямой ответ
Continuous profiling — профилирование, которое работает всегда (агент сэмплирует стеки прода 24/7 с оверхедом ~1% — профили — в хранилище с тегами — «чем занят прод прямо сейчас/вчера в пик» — вопрос с ответом за секунды); отличие от локального профилирования (cProfile/py-spy локально): локально — вы ищете горячую точку в воспроизведённой ситуации (стенд не воспроизводит прод-данные/нагрузку/конфигурацию — «локально быстро» — классика), continuous — правда прода (реальные ветки кода, реальные объёмы, реальные соседи — ёмкость-контекст); три способа подключения: (1) eBPF-агент (без изменения кода — снимает стеки всех процессов машины/пода (eBPF-основы) — нативные стеки из коробки, Python-фреймы — через unwinder (pyroscope-агент/parca — качество зависит от версий Python — проверить на своём); (2) SDK в приложении (pyroscope-io/python-sdk — точные Python-стеки + свои теги (endpoint/tenant — «профиль именно этого маршрута» — структура-меток); (3) OTLP-профили (стандарт профилей в OTel-пайплайн (OTel-контекст — единый коллектор — Alloy-пайплайн); главные применения: диффы релизов (профиль до/после деплоя — «p99 вырос на 20%» — дифф flamegraph'ов — новая горячая функция видна сразу — деплой-стратегии — canary-профили против baseline — регрессия производительности до полного rollout), поиск «куда уходит CPU/деньги» (агрегат за месяц — топ-функции парка сервисов — «json-сериализация — 14% CPU всего кластера» — инвестиция в оптимизацию обоснована цифрами — техдолг), профили-по-трейсам (связка trace↔profile — «долгий спан» → профиль этого временного окна (Tempo/Jaeger — exemplars-механика — метрики-связка), расследования инцидентов постфактум («вчера в 03:00 что-то тормозило» — профиль за окно — история хранится (retention 14–90 дней — S3-хранилище — дешёвое хранение — метрики-стоимость); оверхед-честность: сэмплирование (не инструментация каждой функции — cProfile-стиль «точный, но тормозит» — не continuous — сэмплинг 19 Гц — статистика при объёме точна — GIL-нюанс Python (GIL — профилируются нативные стеки — C-вызовы видны — «GIL-wait» — виден в off-CPU-профилях — eBPF-off-CPU); хранение/стоимость: профили компактнее трейсов (агрегация стеков — гистограммы-логика — месячный профиль сервиса — мегабайты — retention щедрый — «храним всё за квартал» — реально).
Практика: внедрение и рабочие процессы
Внедрение (Grafana-стек): Pyroscope-сервер (helm/docker — рядом с Tempo/Loki — стек-контекст) → агенты (eBPF DaemonSet на ноды — zero-code покрытие всех подов; SDK — в критичные сервисы (точные Python-стеки — pyroscope-io + теги service/env/version — версия — ключ к релиз-диффам — семвер); Grafana-интеграция (Explore → Pyroscope-источник — flamegraph/таблица — Grafana-практика); рабочие процессы (ритуалы, без которых профили мертвы): (1) релиз-чек (после деплоя — дифф профиля новой версии против предыдущей за равные окна — 10 минут — регрессии CPU/аллокаций — CI-культура — «проверка производительности» без нагрузочного стенда — continuous-профили закрывают 80% нужд — locust-дополнение для точных цифр); (2) ежемесячный топ (агрегат «куда уходит CPU парка» — топ-20 функций — кандидаты на оптимизацию с обоснованием — планирование — «оптимизировали сериализацию — минус 9% CPU-счёта» — цифры из профилей); (3) инцидент-окна (латентность-инцидент → профиль окна (+off-CPU — eBPF) — runbook-пункт — «профиль за окно инцидента» — стандартный шаг); (4) canary-сравнение (canary-деплой — профили canary-подов против стабильных — HPA-нейтральность — решение «катим/откатываем» с данными); тонкости Python: мультипроцессность (gunicorn-воркеры — eBPF видит все процессы — SDK-тег pid — «какой воркер горячий» — обычно все одинаково — асимметрия — сигнал (профили-дисциплина); async-код (asyncio — CPU-профиль покажет event loop-функции — «ожидание IO» — off-CPU — CPU-профиль «чист» при тормозящем async — связка двух профилей — eBPF-раздел); аллокации (memory-профилирование — pyroscope memory-тип (alloc_objects) — утечки — утечки-контекст — continuous-память — тренд аллокаций по версиям — «утечка появилась в 1.4» — дата/версия сразу).
Практический сценарий: «стало медленно после релиза» за 15 минут вместо дня
Контекст: API-сервис, релиз 1.7 во вторник, в среду — p95 +40% (300→420 мс) при том же трафике (метрики — SLO горит); старый путь (до continuous): локальное воспроизведение (день — «на стенде не воспроизводится» — прод-данные/конфигурация), откат релиза «на всякий» (стратегии — минус фичи недели); новый путь (Pyroscope внедрён месяц назад): (1) flamegraph-дифф 1.6 против 1.7 за равные часы (тег version — семвер — 2 минуты): новая широкая полоса — validate_response → pydantic-сериализация — в релизе включили «строгую валидацию ответов» (feature-flag по умолчанию on — флаги — «безобидная настройка»); (2) подтверждение: профиль окна пика — validate_response — 31% CPU (было 2% на базовой сериализации — Pydantic-контекст — response_model с глубокой вложенностью — валидация каждого поля); (3) решение за час: flag off для горячих list-эндпоинтов (JSON-пагинация — ответы по 500 объектов — валидация дорогая), строгая валидация оставлена на write-путях (где ценность); (4) проверка: профиль после — validate_response 4% CPU, p95 вернулся к 310 мс (Grafana — 40 минут от начала); (5) постмортем-действия: response_model-оптимизация (selective-валидация — валидация-практика — спринт), релиз-чек в практику (дифф профилей — обязательный шаг релиза — CI-пайплайн — «релиз без профил-диффа не катим»), тест производительности на list-эндпоинты (pytest-метки — ассерт на время сериализации 500 объектов — регресс-защита); итог: 15 минут диагностики вместо дня, откат не понадобился (фича спасена настройкой — флаги-ценность); вывод: continuous profiling превращает «стало медленно» из расследования в дифф двух картинок — инвестиция (агент+сервер — день настройки, 1% CPU) окупается первым же инцидентом — техдолг-стратегия — observability-зрелость (три столпа+профили — четвёртый столп — не роскошь, а рутина).
FAQ
Какой оверхед у continuous profiling и когда он заметен? Сэмплинг-природа: стек снимается ~19 раз/сек на поток (не каждая функция — cProfile-инструментация в 10–100 раз дороже — не путать — профили-основы); типичный оверхед: <1% CPU на eBPF-агент (нод-уровень), 1–2% на SDK в приложении (GIL-взаимодействие Python — py-spy-механика attach — паузы микросекундные); когда заметен: высокоплотные CPU-задачи (числовые вычисления — numpy-контекст — 1% от числа — ок; латентность-критичные микросекундные пути — измерить на canary — canary-практика); проверка перед включением: canary-поды с агентом против без — p95/CPU-дифф (метрики — «оверхед = разница canary» — честная цифра, не из статей); экономический баланс: 1% CPU против часов расследований (сценарий выше — «дорого» — ложная экономия — SLO-контекст).
Continuous profiling против периодического ручного — зачем платить 1% постоянно? Проблема ручного: инцидент уже прошёл («профилируем сейчас» — состояние другое; «профилировать по расписанию» — попадание в нужный момент — лотерея); continuous: история (инцидент вчера/на прошлой неделе — профиль есть — retention — S3-дешевизна); релиз-диффы (ручное «до/после» — организация окон/условий — continuous — теги версий — сравнение любых периодов — семвер); «медленные» деградации (утечка CPU по 2% в неделю — месяц — видно на тренде — метрики-тренды — ручное не поймает момент); ручное профилирование остаётся для: глубокой локальной работы (оптимизация конкретной функции — py-spy/cProfile — точность выше сэмплинга), стендовых экспериментов (locust — под нагрузкой); вывод: continuous — радар (всегда видит), ручное — микроскоп (глубоко по цели) — не замена, а пара — диагностика-культура.
Как связать профили с трейсами и метриками? Уровни связки: общие теги (service/env/version/instance — структура-меток — «профиль этого сервиса этой версии» — минимум обязательный); exemplars/trace-to-profile (Grafana-стек: метрика-гистограмма → exemplar-трейс (процентилям) → профиль временного окна — Tempo-связка — клик-путь «p99 → спан → профиль» — расследование без переключения контекста); SDK-теги по запросам (pyroscope SDK — tag-функция — профиль маршрута/тенанта — «горячий тенант» — виден — ёмкость); OTLP-конвергенция (профили — сигнал OTel (OTel — единый пайплайн/коллектор — Alloy — трейс-профиль-метрика — одна телеметрия — 2026-тренд); практика: даже без полной связки — общие теги дают 80% пользы (время+сервис+версия — окно инцидента находится руками за минуту — runbook).
Что выбрать: Pyroscope, Parca или коммерческие APM? Pyroscope (Grafana-стек — Grafana-интеграция из коробки, eBPF+SDK, S3-хранение (MinIO) — дефолт для Grafana-команд); Parca (Polar Signals — eBPF-first, whole-system — сильнее в системных профилях — eBPF — если Grafana нет); коммерческие APM (Datadog Continuous Profiler и др. — профили в одном UI с трейсами/логами — удобство против цены/вендора — выбор-контекст — аналогия managed против self-hosted); self-hosted-критерии: данные остаются у вас (комплаенс), стоимость на масштабе (40 сервисов — Datadog-счёт против дня поддержки Pyroscope — ёмкость-мышление); Python-специфика: проверить качество Python-unwinding на своей версии (3.11/3.12 — стеки — eBPF vs SDK — SDK точнее для Python — гибрид: eBPF для системы, SDK для критичных сервисов — сценарий-практика); правило: начать с Pyroscope (день настройки), APM — когда стек уже там (монолит-платформа — выбор по контексту стека).
Источники: документация Grafana Pyroscope, agentless profiling, Python SDK — ссылки в шапке.