Сначала провожу границу и задаю модель сравнения
Я, Семёнов Евгений Сергеевич, технический директор, основатель и ведущий системный администратор IT Fresh, начинаю с точного вопроса: стала ли конкретная стадия сопоставимых успешно завершённых pipeline устойчиво медленнее собственной нормальной истории. Эта формулировка удерживает наблюдение в границах производительности. Объект не мониторит очередь Runner, failed job, artifacts и Container Registry. Я не развиваю эти направления внутри тревоги и не использую их показатели как замену длительности стадии. Общую duration pipeline сохраняю отдельным рядом: она описывает путь pipeline целиком от его начала до успешного завершения в наблюдаемой выборке GitLab. Для stage строю свой показатель только из jobs со статусом success, относящихся к одному имени стадии; failed jobs в агрегацию не попадают. Эти величины связаны, но не тождественны. Успешные jobs одной стадии могут исполняться параллельно, стадии зависят друг от друга, а календарное время pipeline включает больше, чем простая сумма исполнений. Поэтому сначала показываю, изменился ли итог pipeline, затем нахожу стадию с изменившимся профилем и только после этого перехожу к причинной проверке. Так сигнал не обещает готовую причину, а задаёт проверяемое направление расследования.
Строю показатель только из успешных наблюдений
Источником списка служит Pipelines API: для проекта я запрашиваю только pipeline со status=success в ограниченном временном диапазоне, с явными ref и source. Для каждого выбранного идентификатора отдельное чтение pipeline даёт его duration, временные отметки и контекст. Jobs API вызываю как GET /projects/:id/pipelines/:pipeline_id/jobs?scope[]=success: он возвращает только jobs со статусом success, включая stage, duration, status, started_at и finished_at. В расчёт stage-duration я допускаю только jobs со статусом success, числовой duration и ожидаемым именем stage; failed jobs исключены запросом и в расчёт не попадают. Правило агрегации фиксирую заранее. Для последовательных успешных jobs уместна сумма длительностей; при параллельных работах она отражает потреблённое job-время, но не календарную протяжённость стадии. Если нужен именно календарный интервал стадии, вычисляю его как разность между самым ранним started_at и самым поздним finished_at выбранных успешных jobs и называю показатель иначе. Я не смешиваю эти две метрики. Для baseline обычно сохраняю медиану и высокий перцентиль по успешно завершённым pipeline, размер выборки и идентификатор правила агрегации. Нулевое число успешных jobs, null duration или запись с иным статусом становятся состоянием неполноты, а не быстрым результатом.
Не смешиваю проекты, ref, stages и типы pipeline
Ключ сопоставимости у меня составной: стабильный идентификатор проекта, точный ref, имя stage, source pipeline и версия правила расчёта. В зависимости от процесса я дополнительно разделяю обычный push, merge request, schedule и parent pipeline, потому что они могут запускать различный набор jobs. Тег и ветка тоже не становятся одной группой только из-за похожего имени. Переименование stage создаёт новую серию, а не продолжает старую без проверки. Повторные jobs требуют заранее выбранной политики: по умолчанию Jobs API не включает retried jobs, и я сохраняю это правило либо осознанно меняю его для всей выборки. Исторический baseline формирую на периоде, который владелец pipeline считает стабильным: без известной миграции, массового изменения состава и необычного режима. Окно должно содержать достаточно успешно завершённых запусков, но его длина не универсальна. Для частого pipeline это могут быть несколько недель, для редкого нужен иной горизонт. Текущую медиану сравниваю с медианой baseline относительным изменением, например отношением или процентом, одновременно показывая абсолютные секунды и число наблюдений. Владелец подтверждает, что сравниваемые запуски действительно выполняют одну задачу.
Собираю в Zabbix одним контролируемым потоком
В Zabbix я создаю HTTP agent master item для получения ограниченного ответа от подготовленного read-only endpoint либо непосредственно от GitLab API, если пагинация и агрегация безопасно укладываются в контролируемую схему. HTTP agent выполняется сервером Zabbix или proxy и не требует агента на стороне GitLab. Токен храню в защищённом макросе, не помещаю в URL, item key, историю значений или уведомление. Сетевой доступ ограничиваю GitLab endpoint, проверяю TLS, ожидаемые HTTP-коды, размер ответа, тайм-аут и пагинацию. Один master item позволяет получить согласованный снимок, а dependent items извлекают stage_duration_seconds, pipeline_duration_seconds, sample_count, baseline_ratio и data_age_seconds. Для разложения JSON использую preprocessing и JSONPath там, где форма ответа стабильна; сложную агрегацию лучше выполнить в малом read-only адаптере, который возвращает уже обезличенный числовой документ. Историю сырого master payload не храню дольше диагностической необходимости. Ошибку HTTP, ошибку разбора, пустую страницу и отсутствие новых pipeline со статусом success развожу по разным состояниям. Частоту запроса выбираю ниже естественной частоты успешных завершений, чтобы не создавать лишнюю нагрузку.
Оставляю конфигурационный фрагмент безопасным и читаемым
Конфигурация описывает намерение, а не предлагает вставить неизвестные значения в рабочую систему. Master item использует адрес вида {$GITLAB.API.URL}/api/v4/projects/{$GITLAB.PROJECT.ID}/pipelines с параметрами ref={$GITLAB.REF}, source={$GITLAB.PIPELINE.SOURCE}, status=success, updated_after={$WINDOW.START} и ограниченным per_page. Заголовок PRIVATE-TOKEN получает значение из secret macro {$GITLAB.READ.TOKEN}; в экспорт и журнал оно не попадает. Затем контролируемый сборщик читает GET /projects/{$GITLAB.PROJECT.ID}/pipelines/{$PIPELINE.ID} и GET /projects/{$GITLAB.PROJECT.ID}/pipelines/{$PIPELINE.ID}/jobs?scope[]=success, обрабатывает все объявленные страницы и допускает в stage-агрегацию только jobs со статусом success, а failed jobs отбрасывает. Сборщик возвращает только числовые метрики, stage, ref, source, время расчёта и количество наблюдений. Dependent item gitlab.pipeline.stage.duration[{$GITLAB.PROJECT.ID},{$GITLAB.REF},{$GITLAB.STAGE},{$GITLAB.PIPELINE.SOURCE}] получает $.stage_duration_seconds, а отдельные items — $.pipeline_duration_seconds, $.baseline_ratio, $.sample_count и $.data_age_seconds. Это схема для ревью: реальные интервалы, поддерживаемые параметры версии GitLab и права токена проверяются до сохранения. Никаких POST, retry, cancel или изменения переменных здесь нет.
Читаю сигналы в причинно-следственном порядке
Первым проверяю свежесть: когда завершился последний включённый pipeline, когда рассчитан снимок, успешно ли обработаны все страницы и не изменился ли ответ API. Если данные устарели, событие сообщает о потере наблюдаемости, а не о нормальной длительности. Вторым проверяю размер текущего и базового окон. Единичный запуск не сравниваю с устойчивой медианой как равноправный набор. Третьим смотрю относительное изменение медианы выбранной стадии и высокий перцентиль: рост обоих сильнее поддерживает гипотезу устойчивого сдвига, тогда как один длинный хвост требует осторожности. Четвёртым сопоставляю stage-duration с общей pipeline duration. Если стадия выросла, а итог почти не изменился, возможна компенсация параллельностью или соседними стадиями; если обе величины выросли согласованно, влияние на общий путь вероятнее. Пятым раскладываю выбранную стадию только по jobs со статусом success и их временным отметкам, проверяя, не поменялись ли состав, параллельность, правила или образ выполнения; failed jobs в этот анализ не включаю. Затем сверяю подтверждённые изменения с владельцем pipeline. Соседство событий на графике остаётся корреляцией, пока одна обратимая проверка не даёт ожидаемого изменения.
Отвергаю быстрые, но слабые объяснения
Я не объявляю причиной самый долгий job только потому, что он виден на графике. Сначала выясняю, был ли он частью всех сопоставимых запусков и изменился ли его собственный профиль. Не складываю параллельные jobs и не называю сумму календарной длительностью stage. Не переношу baseline основной ветки на merge request pipeline и не объединяю schedule с push ради большего числа точек. Не заменяю отсутствующее значение нулём: это искусственно улучшит медиану и может скрыть разрыв сбора. Не строю вывод по текущему running pipeline, потому что его duration ещё не окончательна. Не выбираю универсальный процент или число секунд из чужого шаблона. Допустимая изменчивость зависит от конкретной стабильной среды, состава работ, частоты запусков и цены задержки; baseline и границу сигнала я сверяю с владельцем pipeline. Наконец, не превращаю мониторинг в управляющий контур: он не запускает повтор, не отменяет и не останавливает pipeline, не меняет ref, переменные или конфигурацию. Его задача — сохранить сопоставимые факты и сделать следующий read-only шаг очевидным.
Проверяю изменение контролируемо и без воздействия на GitLab
Безопасную проверку начинаю с replay ранее полученного обезличенного JSON в preprocessing test Zabbix либо на изолированной копии шаблона. Я беру несколько успешно завершённых pipeline из стабильного периода и один заранее известный более долгий успешный пример, удаляю токен и лишние поля, затем проверяю фильтры project, ref, stage, source и status=success для pipeline, scope[]=success и status success для jobs, расчёт медианы, высокого перцентиля, sample_count и свежести. Failed job добавляю как отрицательный тест и убеждаюсь, что она не меняет stage-duration и не попадает в расчёт. Меняю ровно одну входную величину в тестовой копии — duration одной успешной job выбранной стадии — и ожидаю, что изменятся только stage-duration и зависящий от него relative baseline, а общая pipeline duration останется исходным отдельным значением. Следующая проверка подставляет устаревший calculated_at: должен сработать сигнал свежести, но не сигнал ускорения или деградации. Затем имитирую пустую страницу или null duration и убеждаюсь, что набор помечается неполным. На рабочем GitLab разрешён только повторный GET того же ограниченного диапазона с status=success для pipeline и scope[]=success для jobs после согласования частоты. Если результаты различаются между повторами из-за пагинации, ретраев или изменившейся выборки, причинный вывод приостанавливаю до стабилизации правила.
Фиксирую стоп-критерии, ограничения и откат
Я останавливаю ввод сигнала, если нет минимального числа сопоставимых успешно завершённых pipeline, владелец не подтвердил стабильный период, изменились ref, source, имя или состав stage, не закрыта пагинация, ответ содержит непредусмотренные null, часы расходятся либо данные старше согласованного предела. Стоп-критерием также служит рост нагрузки API, серия ответов ограничения частоты, ошибка TLS, неожиданное расширение прав токена или попадание секрета в диагностический вывод. В этих случаях я отключаю только новые items и triggers, возвращаю прежнюю частоту опроса, удаляю временный read-only токен по процедуре владельца и прекращаю хранение временного payload согласно сроку. Из GitLab ничего не удаляется и ни один pipeline не изменяется. Ограничение метода принципиально: job duration и временные отметки показывают, где проявился сдвиг, но сами по себе не доказывают внутреннюю причину замедления. Небольшая выборка успешно завершённых pipeline нестабильна, медиана может скрыть редкий хвост, высокий перцентиль требует достаточного числа точек, а изменение графа pipeline разрывает сопоставимость. После отката прежний мониторинг продолжает работать независимо от пилота.
Завершаю проверяемым выводом и следующим шагом
В итоговом сообщении я указываю проект, ref, source, stage, правило агрегации, текущее окно, окно baseline, количество законченных наблюдений, абсолютную длительность, относительное изменение и возраст данных. Затем одной фразой описываю границу вывода: например, медиана календарной длительности выбранной стадии выросла относительно согласованного стабильного периода и совпала с ростом общей duration pipeline; причина внутри стадии пока требует проверки владельцем. Если изменилась только сумма job-времени, так и пишу, не называя её календарным замедлением. Рядом перечисляю уже отвергнутые альтернативы и первый безопасный шаг — чтение состава jobs и временных отметок тех же pipeline. Такой формат позволяет обсуждать деградацию предметно, не обещает универсального порога и не смешивает наблюдение с воздействием. Хотите согласовать модель сопоставимости и baseline для ваших GitLab stages в Zabbix — начните разговор с IT Fresh удобным способом: https://itfresh.ru/ https://t.me/ITfresh_Boss tel:+79037296241
Сигналы, которые сопоставляю
- Медиана длительности стадии, рассчитанной только по jobs со статусом success, в сопоставимом окне и её относительное изменение к согласованному baseline.
- Высокий перцентиль длительности стадии как признак ухудшения длинного хвоста без подмены им типичного значения.
- Доля последовательных успешно завершённых pipeline, в которых стадия превышает выбранную относительную границу baseline.
- Общая duration pipeline рядом с длительностями stages, но в отдельном ряду и без арифметического отождествления.
- Количество сопоставимых наблюдений в текущем и базовом окнах, чтобы не сравнивать единичный запуск с устойчивой историей.
- Возраст последнего успешно обработанного pipeline со статусом success и отдельное состояние ошибки или неполного ответа API.
- Момент изменения конкретной стадии относительно соседних стадий на общей шкале pipeline.
- Версия определения pipeline или подтверждённая владельцем отметка изменения, разделяющая старый и новый baseline.
Отвергнутые альтернативы
- Сравнивать только общую duration pipelineОбщее время показывает пользовательский итог, но не локализует стадию. Параллельность, зависимости и паузы означают, что сумма job duration по stages не обязана равняться общей duration.
- Сложить все jobs с одинаковым stage без разделения выборокРазные проекты, ref и источники pipeline могут иметь разный состав работ и нормальный профиль. Смесь создаёт искусственный baseline и скрывает реальное изменение.
- Считать один медленный pipeline достаточным доказательством деградацииЕдиничный выброс не показывает устойчивость. Нужны размер выборки, несколько последовательных наблюдений, свежесть и проверка изменения состава стадии.
- Задать один порог в секундах для всех stagesУ стадий различаются назначение, параллельность и ожидаемый диапазон. Baseline определяется по конкретной стабильной среде и согласуется с владельцем pipeline.
- Автоматически перезапускать pipeline после сигналаПовтор меняет наблюдаемую последовательность и воздействует на GitLab. Мониторинг остаётся read-only и передаёт проверяемый контекст владельцу.
Конфигурация и сбор
Отдельный токен имеет только минимальные права чтения API для согласованного проекта. Секрет хранится в secret macro или внешнем хранилище, не передаётся в URL и не сохраняется в history, логах, событиях либо экспорте. Разрешены только GET к Pipelines API и Jobs API в ограниченном диапазоне. Запуск, retry, cancel, удаление и изменение GitLab запрещены. Сетевой доступ, TLS, частота, пагинация, максимальный размер ответа и срок хранения master payload ограничены заранее.
- Зафиксировать составной ключ выборки: project_id, ref, stage, pipeline source и версию правила агрегации.
- Получить список только pipeline со статусом success в ограниченном окне, обработать пагинацию и сохранить время успешного расчёта.
- Для выбранных pipeline прочитать общую duration и запросить jobs через /projects/:id/pipelines/:pipeline_id/jobs?scope[]=success; допускать в расчёт только jobs со статусом success, а failed и неполные записи отбрасывать без подмены нулём.
- Рассчитать stage-duration одним зафиксированным способом, а pipeline duration оставить отдельной метрикой.
- Передать в Zabbix компактный JSON с числовыми значениями, размером выборок, контекстом и freshness; секреты и лишние поля удалить до передачи.
- Создать dependent items для длительностей, baseline ratio, sample count и data age; ошибки сбора и неполноту наблюдать отдельно.
- Согласовать окно baseline и относительную границу с владельцем pipeline, затем включить сигнал только после replay-проверки.
Контролируемая проверка и свидетельство
Проверка выполняется на сохранённом обезличенном ответе и изолированной копии элементов Zabbix. Сначала воспроизводятся несколько pipeline со status=success из стабильного периода и только их jobs со статусом success, полученные с scope[]=success. Failed job используется как отрицательный тест и не должна менять stage-duration или baseline ratio. Затем в одной тестовой записи изменяется только duration успешной job выбранной стадии: ожидается изменение stage-duration и baseline ratio без изменения отдельной pipeline duration. Отдельно подставляются устаревший calculated_at, пустая выборка и null duration; они должны давать freshness или incomplete, а не ноль и не ложное улучшение. На рабочем GitLab допустим только GET согласованного диапазона с фильтром status=success для pipeline и scope[]=success для jobs при ограниченной частоте. Тест прекращается при несовпадении пагинации, неожиданном поле, ошибке TLS, ограничении частоты, росте нагрузки или риске раскрытия секрета.
Фрагмент рабочего регламента
- GET {$GITLAB.API.URL}/api/v4/projects/{$GITLAB.PROJECT.ID}/pipelines?ref={$GITLAB.REF}&source={$GITLAB.PIPELINE.SOURCE}&status=success&updated_after={$WINDOW.START}&per_page={$PAGE.SIZE}
- Header PRIVATE-TOKEN: {$GITLAB.READ.TOKEN}; макрос секретный, значение не выводится в URL, history, события или журналы.
- Для каждого выбранного {$PIPELINE.ID}: GET /api/v4/projects/{$GITLAB.PROJECT.ID}/pipelines/{$PIPELINE.ID} и GET /api/v4/projects/{$GITLAB.PROJECT.ID}/pipelines/{$PIPELINE.ID}/jobs?scope[]=success&per_page={$PAGE.SIZE} с обработкой всех страниц.
- Принять в stage-агрегацию только jobs со статусом success и ожидаемыми project, ref, source и stage; failed jobs отбрасывать, а null, неполную страницу и ошибку разбора пометить как incomplete.
- Вернуть компактный JSON: stage_duration_seconds, pipeline_duration_seconds, baseline_ratio, sample_count, baseline_sample_count, calculated_at, data_age_seconds и aggregation_rule.
- В dependent items извлечь поля preprocessing JSONPath; freshness и incomplete держать отдельными сигналами.
- Перед эскалацией записать ключ выборки, оба окна, число наблюдений, правило агрегации, абсолютное и относительное изменение, а также подтверждение владельца.
Откат
При проблеме я отключаю только добавленные HTTP master item, dependent items и triggers, возвращаю прежний интервал опроса и удаляю временный минимальный токен по согласованной процедуре после подтверждения владельца. Временный master payload и тестовые данные прекращают храниться по установленному сроку. Если использовался read-only адаптер, его endpoint закрывается без изменения GitLab. Pipeline не запускаются, не повторяются, не отменяются, не останавливаются и не удаляются. После отката проверяю, что исходные элементы Zabbix продолжают получать данные и новые обращения к GitLab прекратились.
Ограничения вывода
Метод видит длительности и временную структуру только успешно завершённых pipeline и только jobs со статусом success, но не доказывает внутреннюю причину изменения. Failed jobs в расчёт не входят. Сумма job duration не равна календарной длительности стадии при параллельности, а сумма stages не обязана равняться pipeline duration. Редкие успешные pipeline дают слабый baseline; высокий перцентиль нестабилен на малой выборке. Изменение состава jobs, ref, source, имени stage или правила ретраев разрывает сопоставимость. API пагинируется, а неполная обработка страниц и устаревшие данные создают ложный профиль. Универсального окна и порога нет: baseline определяется по конкретной стабильной среде и сверяется с владельцем pipeline. Объект не охватывает очередь Runner, failed job, artifacts и Container Registry и не выполняет управляющих действий.

