fix(metrics): гасим crash-loop tg-relay через профиль relay #3492
3 changed files with 78 additions and 4 deletions
|
|
@ -91,8 +91,12 @@ jobs:
|
||||||
METRICS_TELEGRAM_INFRA_TOPIC_ID: ${{ secrets.METRICS_TELEGRAM_INFRA_TOPIC_ID }}
|
METRICS_TELEGRAM_INFRA_TOPIC_ID: ${{ secrets.METRICS_TELEGRAM_INFRA_TOPIC_ID }}
|
||||||
METRICS_TELEGRAM_ONCALL: ${{ secrets.METRICS_TELEGRAM_ONCALL }}
|
METRICS_TELEGRAM_ONCALL: ${{ secrets.METRICS_TELEGRAM_ONCALL }}
|
||||||
ALERT_ACK_GLITCHTIP_SECRET: ${{ secrets.ALERT_ACK_GLITCHTIP_SECRET }}
|
ALERT_ACK_GLITCHTIP_SECRET: ${{ secrets.ALERT_ACK_GLITCHTIP_SECRET }}
|
||||||
|
# #3471: секрет ретранслятора Telegram Bot API (tg-relay). Пусто —
|
||||||
|
# профиль relay не включаем (см. PROFILES ниже), а не падаем в
|
||||||
|
# рестарт-луп: контейнер сам делает SystemExit на пустом секрете.
|
||||||
|
TG_RELAY_SECRET: ${{ secrets.TG_RELAY_SECRET }}
|
||||||
with:
|
with:
|
||||||
envs: METRICS_TELEGRAM_BOT_TOKEN,METRICS_TELEGRAM_CHAT_ID,METRICS_TELEGRAM_TOPIC_ID,METRICS_TELEGRAM_INFRA_TOPIC_ID,METRICS_TELEGRAM_ONCALL,ALERT_ACK_GLITCHTIP_SECRET
|
envs: METRICS_TELEGRAM_BOT_TOKEN,METRICS_TELEGRAM_CHAT_ID,METRICS_TELEGRAM_TOPIC_ID,METRICS_TELEGRAM_INFRA_TOPIC_ID,METRICS_TELEGRAM_ONCALL,ALERT_ACK_GLITCHTIP_SECRET,TG_RELAY_SECRET
|
||||||
host: ${{ secrets.INFRA_DEPLOY_HOST || secrets.DEPLOY_HOST }}
|
host: ${{ secrets.INFRA_DEPLOY_HOST || secrets.DEPLOY_HOST }}
|
||||||
username: ${{ secrets.INFRA_DEPLOY_USER || secrets.DEPLOY_USER }}
|
username: ${{ secrets.INFRA_DEPLOY_USER || secrets.DEPLOY_USER }}
|
||||||
key: ${{ secrets.INFRA_DEPLOY_SSH_KEY || secrets.DEPLOY_SSH_KEY }}
|
key: ${{ secrets.INFRA_DEPLOY_SSH_KEY || secrets.DEPLOY_SSH_KEY }}
|
||||||
|
|
@ -259,6 +263,19 @@ jobs:
|
||||||
echo "::warning title=Алерты выключены::METRICS_TELEGRAM_BOT_TOKEN/CHAT_ID не заданы. Метрики и логи собираются, но при срабатывании правила НИКТО не будет уведомлён. Канал доставки — открытый вопрос #3078."
|
echo "::warning title=Алерты выключены::METRICS_TELEGRAM_BOT_TOKEN/CHAT_ID не заданы. Метрики и логи собираются, но при срабатывании правила НИКТО не будет уведомлён. Канал доставки — открытый вопрос #3078."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Ретранслятор Telegram Bot API (#3471, PR #3487 сломал прод: сервис
|
||||||
|
# без profiles уходил в SystemExit на пустом секрете и висел в
|
||||||
|
# Restarting). Профиль relay включаем НЕЗАВИСИМО от alerts — это
|
||||||
|
# разные каналы (один шлёт алерты боту, другой ретранслирует
|
||||||
|
# продуктовый Bot API трафик с Selectel). PROFILES — список через
|
||||||
|
# запятую, как того требует COMPOSE_PROFILES.
|
||||||
|
if [ -n "${TG_RELAY_SECRET:-}" ]; then
|
||||||
|
PROFILES="${PROFILES:+$PROFILES,}relay"
|
||||||
|
echo "Ретранслятор Telegram: секрет задан, профиль relay включён."
|
||||||
|
else
|
||||||
|
echo "::warning title=Резервный ретранслятор Telegram выключен::TG_RELAY_SECRET пуст — tg-relay не поднимается (профиль relay выключен). Продуктовый Telegram-трафик пойдёт напрямую с Selectel, где теряется примерно каждый четвёртый короткий запрос."
|
||||||
|
fi
|
||||||
|
|
||||||
# ── Цели file_sd для Prometheus (#3155) ────────────────────────
|
# ── Цели file_sd для Prometheus (#3155) ────────────────────────
|
||||||
# Включатель профиля и цель для Prometheus обязаны стоять в ОДНОМ
|
# Включатель профиля и цель для Prometheus обязаны стоять в ОДНОМ
|
||||||
# условии. Пока они жили порознь, вышло так: 27.08 профиль alerts
|
# условии. Пока они жили порознь, вышло так: 27.08 профиль alerts
|
||||||
|
|
@ -277,19 +294,27 @@ jobs:
|
||||||
AM_TARGETS_FILE=ops/metrics/prometheus/alertmanager_targets.gen.yml
|
AM_TARGETS_FILE=ops/metrics/prometheus/alertmanager_targets.gen.yml
|
||||||
: > "$AM_TARGETS_FILE"
|
: > "$AM_TARGETS_FILE"
|
||||||
echo "# Файл рендерится деплоем (deploy-metrics.yml), правки руками затрутся." >> "$AM_TARGETS_FILE"
|
echo "# Файл рендерится деплоем (deploy-metrics.yml), правки руками затрутся." >> "$AM_TARGETS_FILE"
|
||||||
if [ "$PROFILES" = "alerts" ]; then
|
# Сравнение через case, а не "=": PROFILES теперь может быть
|
||||||
|
# комбинацией через запятую ("alerts,relay") с тех пор, как #3471
|
||||||
|
# завёл независимый профиль relay — точное равенство строке
|
||||||
|
# "alerts" сломалось бы молча в тот момент, когда оба профиля
|
||||||
|
# включены разом.
|
||||||
|
case ",$PROFILES," in
|
||||||
|
*,alerts,*)
|
||||||
echo '- targets: ["alertmanager:9093"]' >> "$AM_TARGETS_FILE"
|
echo '- targets: ["alertmanager:9093"]' >> "$AM_TARGETS_FILE"
|
||||||
echo " labels:" >> "$AM_TARGETS_FILE"
|
echo " labels:" >> "$AM_TARGETS_FILE"
|
||||||
echo " host: infra" >> "$AM_TARGETS_FILE"
|
echo " host: infra" >> "$AM_TARGETS_FILE"
|
||||||
echo "Prometheus: приёмник alertmanager:9093 прописан в целях."
|
echo "Prometheus: приёмник alertmanager:9093 прописан в целях."
|
||||||
else
|
;;
|
||||||
|
*)
|
||||||
# Пустой список, а НЕ отсутствующий файл: одиночный бинд-маунт
|
# Пустой список, а НЕ отсутствующий файл: одиночный бинд-маунт
|
||||||
# несуществующего пути docker подменяет каталогом, и Prometheus
|
# несуществующего пути docker подменяет каталогом, и Prometheus
|
||||||
# не стартует вовсе.
|
# не стартует вовсе.
|
||||||
echo "# Профиль alerts выключен — приёмников нет." >> "$AM_TARGETS_FILE"
|
echo "# Профиль alerts выключен — приёмников нет." >> "$AM_TARGETS_FILE"
|
||||||
echo "[]" >> "$AM_TARGETS_FILE"
|
echo "[]" >> "$AM_TARGETS_FILE"
|
||||||
echo "Prometheus: профиль alerts выключен — целей нет, это штатно."
|
echo "Prometheus: профиль alerts выключен — целей нет, это штатно."
|
||||||
fi
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
# ── read-only роль для датасорса GlitchTip ─────────────────────
|
# ── read-only роль для датасорса GlitchTip ─────────────────────
|
||||||
# Идемпотентно. Прав на запись не выдаём вовсе: датасорс Grafana
|
# Идемпотентно. Прав на запись не выдаём вовсе: датасорс Grafana
|
||||||
|
|
@ -301,6 +326,43 @@ jobs:
|
||||||
COMPOSE_PROFILES="$PROFILES" \
|
COMPOSE_PROFILES="$PROFILES" \
|
||||||
docker compose -p gendesign-metrics -f docker-compose.metrics.yml up -d --remove-orphans
|
docker compose -p gendesign-metrics -f docker-compose.metrics.yml up -d --remove-orphans
|
||||||
|
|
||||||
|
# ── alert-ack / tg-relay: код монтируется с хоста ────────────────
|
||||||
|
# Тот же класс бага, что у Alertmanager (см. ниже) и Caddyfile:
|
||||||
|
# `up -d` сравнивает ОПИСАНИЕ сервиса, а не содержимое бинд-маунта.
|
||||||
|
# alert-ack и tg-relay получают код именно бинд-маунтом файла
|
||||||
|
# (./ops/metrics/{alert-ack,tg-relay}/app.py:/app/app.py:ro), а не
|
||||||
|
# сборкой образа — правка app.py оставляет уже запущенный
|
||||||
|
# контейнер работать на СТАРОМ коде в памяти интерпретатора сколько
|
||||||
|
# угодно, и `up -d` этого не видит вовсе.
|
||||||
|
#
|
||||||
|
# Пойман на проде 12.09.2026: PR #3490 (фикс alert-ack) слился,
|
||||||
|
# `git reset --hard` обновил файл на диске (grep по новому
|
||||||
|
# комментарию находил его), а gendesign-alert-ack, запущенный за
|
||||||
|
# 25 минут до этого, продолжал отвечать по старой логике —
|
||||||
|
# зелёный деплой, тихо неверное поведение. Починил только ручной
|
||||||
|
# `docker restart gendesign-alert-ack`. force-recreate здесь —
|
||||||
|
# замена этому ручному шагу.
|
||||||
|
#
|
||||||
|
# case ",$PROFILES," — пересоздаём только если профиль сервиса
|
||||||
|
# реально включён в ЭТОМ прогоне, иначе force-recreate ругается на
|
||||||
|
# несуществующий контейнер (сервис не создан вовсе).
|
||||||
|
case ",$PROFILES," in
|
||||||
|
*,alerts,*)
|
||||||
|
COMPOSE_PROFILES="$PROFILES" \
|
||||||
|
docker compose -p gendesign-metrics -f docker-compose.metrics.yml \
|
||||||
|
up -d --force-recreate alert-ack
|
||||||
|
echo "alert-ack: контейнер пересоздан — код монтируется с хоста, up -d его не подхватывает (#3490)."
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
case ",$PROFILES," in
|
||||||
|
*,relay,*)
|
||||||
|
COMPOSE_PROFILES="$PROFILES" \
|
||||||
|
docker compose -p gendesign-metrics -f docker-compose.metrics.yml \
|
||||||
|
up -d --force-recreate tg-relay
|
||||||
|
echo "tg-relay: контейнер пересоздан — код монтируется с хоста, up -d его не подхватывает (#3490)."
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
# ── Alertmanager: пересоздать, если конфиг перерисовали ─────────
|
# ── Alertmanager: пересоздать, если конфиг перерисовали ─────────
|
||||||
# `up -d` выше СЧИТАЕТ alertmanager неизменившимся: он сравнивает
|
# `up -d` выше СЧИТАЕТ alertmanager неизменившимся: он сравнивает
|
||||||
# описание сервиса, а содержимое бинд-маунта в это сравнение не
|
# описание сервиса, а содержимое бинд-маунта в это сравнение не
|
||||||
|
|
|
||||||
|
|
@ -236,6 +236,11 @@ services:
|
||||||
tg-relay:
|
tg-relay:
|
||||||
image: python:3.12-slim
|
image: python:3.12-slim
|
||||||
container_name: gendesign-tg-relay
|
container_name: gendesign-tg-relay
|
||||||
|
# #3471 (PR #3487 инцидент): без profiles сервис поднимался ВСЕГДА, а при
|
||||||
|
# пустом TG_RELAY_SECRET делает SystemExit — то есть уходит в бесконечный
|
||||||
|
# Restarting сразу после деплоя. Профиль включает deploy-metrics.yml, и
|
||||||
|
# только когда секрет реально задан (см. PROFILES там).
|
||||||
|
profiles: ["relay"]
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
user: "65534:65534"
|
user: "65534:65534"
|
||||||
command: ["python", "-u", "/app/app.py"]
|
command: ["python", "-u", "/app/app.py"]
|
||||||
|
|
|
||||||
|
|
@ -425,6 +425,13 @@ services:
|
||||||
# (env_file ниже); пусто/нет токена = бот молча не стартует (см. tgbot_main.py).
|
# (env_file ниже); пусто/нет токена = бот молча не стартует (см. tgbot_main.py).
|
||||||
# ⚠️ НЕ дублировать TELEGRAM_* в блоке environment: (см. предупреждение у backend
|
# ⚠️ НЕ дублировать TELEGRAM_* в блоке environment: (см. предупреждение у backend
|
||||||
# выше про environment: перекрывающий env_file при пустой host-env).
|
# выше про environment: перекрывающий env_file при пустой host-env).
|
||||||
|
# #3471: TELEGRAM_RELAY_BASE_URL/TELEGRAM_RELAY_SECRET (клиент резервного
|
||||||
|
# ретранслятора tg-relay на инфраструктурном хосте) заводятся тем же
|
||||||
|
# способом — строкой в backend/.env.runtime на ЭТОМ (продуктовом, Selectel)
|
||||||
|
# хосте. deploy-tradein.yml секреты приложения в CI не инжектит вовсе —
|
||||||
|
# только читает уже лежащий на хосте .env.runtime, поэтому Forgejo-секрет
|
||||||
|
# TELEGRAM_RELAY_SECRET сюда сам по себе не долетит: файл на хосте нужно
|
||||||
|
# дополнить вручную (см. README-АДМИНУ.md).
|
||||||
tgbot:
|
tgbot:
|
||||||
image: ghcr.io/lekss361/gendesign-tradein-backend:${IMAGE_TAG:-latest}
|
image: ghcr.io/lekss361/gendesign-tradein-backend:${IMAGE_TAG:-latest}
|
||||||
container_name: tradein-tgbot
|
container_name: tradein-tgbot
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue