All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 9s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
Метрик в проекте не было ни одной: ни экспортеров, ни /metrics в бэкендах, единственный канал наблюдения — journald, единственный сигнал об аварии — исключение в GlitchTip. Из-за этого целый класс отказов невидим в принципе: задача рапортует done, строк ноль, исключения нет. Так протухли данные на семь месяцев (#2998), 34 дня был мёртв house_imv_backfill (#2698), 8 суток писал ноль newbuilding_enrich (#2767), 91 день копилось раздутие listings (#2992). Grafana не заменяет GlitchTip: ошибки остаются там. Grafana OSS не принимает Sentry DSN ни одним компонентом, а скрубберы в before_send — требование 152-ФЗ. Здесь появляется другой класс данных: ряды и алерты по трендам. Наблюдатель поставлен у ДРУГОГО провайдера, чем наблюдаемое: серверная сторона на Beget, рядом с GlitchTip. Если ляжет Poincare, мониторинг должен об этом сказать, а не лечь вместе с ним. Транспорт push, а не pull: агент на Poincare шлёт remote_write и логи исходящим HTTPS, поэтому там не открывается ни одного входящего порта сверх 22/80/443. При обрыве канала Alloy копит в WAL и досылает — pull-скрейп в той же ситуации терял бы точки именно в аварии, ради которой мониторинг и нужен. Два контура доступа с разными учётками. Пароль приёмника по построению лежит открытым на продуктовом хосте, значит его компрометация неизбежна вместе с хостом; будь это учётка витрины, утёк бы и доступ к дашбордам. GlitchTip читается прямым SQL, а не Sentry-плагином: у плагина на 6.1.6 stats_v2 отдаёт 500 (баг GlitchTip #381), Events/Discover — 404 (#416), а в grafana/sentry-datasource слово glitchtip не встречается ни разу. Схема сверена на живой базе: колонка времени называется timestamp, а не received, и отдельной таблицы IssueIndex не существует — агрегаты лежат на самой issue_events_issue. Алерты за профилем alerts: канал доставки — открытый вопрос #3078, и стек не должен на нём стоять. Деплой предупреждает, что уведомлять пока некому. Каждая настройка, способная отказать молча, закрыта явно: ретенция Prometheus задана и по времени и по размеру, retention_enabled у компактора Loki (без него retention_period не работает вовсе), путь к журналу и запуск Alloy от root (иначе агент читает ноль записей без ошибки), проверка Caddy до перезагрузки (на этом хосте тот же Caddy держит git, errors и obsidian). Refs #3078
78 lines
3.8 KiB
Bash
78 lines
3.8 KiB
Bash
#!/usr/bin/env bash
|
||
# Заводит read-only роль grafana_ro в базе glitchtip — под датасорс Grafana.
|
||
# Идемпотентен: повторный запуск только досогласует права.
|
||
#
|
||
# ПОЧЕМУ ОТДЕЛЬНАЯ РОЛЬ, А НЕ ВЛАДЕЛЕЦ БАЗЫ. Датасорс Grafana доступен всякому,
|
||
# кто вошёл в интерфейс, и позволяет выполнять произвольный SQL в панелях.
|
||
# Владельческая роль сделала бы витрину способом уронить трекер ошибок. Здесь
|
||
# прав на запись нет вовсе, поэтому худшее, что можно сделать через панель, —
|
||
# медленный SELECT.
|
||
#
|
||
# Запускать на инфраструктурном хосте. Вызывается из deploy-metrics.yml.
|
||
set -euo pipefail
|
||
|
||
CONT=gendesign-infra-postgres
|
||
DB=glitchtip
|
||
ROLE=grafana_ro
|
||
|
||
: "${GLITCHTIP_RO_PASSWORD:?GLITCHTIP_RO_PASSWORD не задан — запусти scripts/setup-metrics-secrets.sh}"
|
||
|
||
if ! docker inspect "$CONT" >/dev/null 2>&1; then
|
||
echo " $CONT не найден — пропускаю создание роли."
|
||
exit 0
|
||
fi
|
||
|
||
# Ищем роль с правом заводить других. Имя суперпользователя в этом кластере
|
||
# нигде не зафиксировано, а угадывать «postgres» неверно: в образе оно задаётся
|
||
# переменной POSTGRES_USER и здесь ею не является.
|
||
SUPER=""
|
||
for candidate in glitchtip forgejo postgres; do
|
||
if docker exec "$CONT" psql -U "$candidate" -d postgres -tAc \
|
||
"SELECT 1 FROM pg_roles WHERE rolname = CURRENT_USER AND (rolsuper OR rolcreaterole)" 2>/dev/null \
|
||
| grep -q 1; then
|
||
SUPER="$candidate"
|
||
break
|
||
fi
|
||
done
|
||
|
||
if [ -z "$SUPER" ]; then
|
||
echo " ОШИБКА: не нашёл роль с правом CREATE ROLE в $CONT." >&2
|
||
echo " Проверь вручную: docker exec $CONT psql -U <роль> -c '\\du'" >&2
|
||
exit 1
|
||
fi
|
||
echo " привилегированная роль: $SUPER"
|
||
|
||
# Пароль передаётся через переменную окружения psql, а не в тексте запроса —
|
||
# иначе он осел бы в pg_stat_statements и в логе запросов.
|
||
docker exec -e RO_PASS="$GLITCHTIP_RO_PASSWORD" -i "$CONT" \
|
||
psql -U "$SUPER" -d "$DB" -v ON_ERROR_STOP=1 <<'SQL'
|
||
\set ro_pass `echo "$RO_PASS"`
|
||
|
||
DO $$
|
||
BEGIN
|
||
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'grafana_ro') THEN
|
||
CREATE ROLE grafana_ro LOGIN;
|
||
RAISE NOTICE 'роль grafana_ro создана';
|
||
ELSE
|
||
RAISE NOTICE 'роль grafana_ro уже есть';
|
||
END IF;
|
||
END
|
||
$$;
|
||
|
||
ALTER ROLE grafana_ro WITH PASSWORD :'ro_pass';
|
||
|
||
-- Ограничение на число соединений: панель с автообновлением способна открыть
|
||
-- их десятками, а это тот же кластер, где живёт Forgejo.
|
||
ALTER ROLE grafana_ro CONNECTION LIMIT 8;
|
||
|
||
GRANT CONNECT ON DATABASE glitchtip TO grafana_ro;
|
||
GRANT USAGE ON SCHEMA public TO grafana_ro;
|
||
GRANT SELECT ON ALL TABLES IN SCHEMA public TO grafana_ro;
|
||
|
||
-- Таблицы событий партиционированы по неделям: новые партиции появляются сами,
|
||
-- и без этой строки датасорс начал бы отдавать пустоту на свежих данных,
|
||
-- оставаясь при этом «рабочим». Тихий отказ ровно того сорта, который мы ловим.
|
||
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO grafana_ro;
|
||
SQL
|
||
|
||
echo " grafana_ro: права выданы на $DB"
|