Хвосты #3467, не попавшие в main вместе с #3476: - prometheus.yml смонтирован одним файлом: после git reset --hard reload перечитывал СТАРЫЙ инод с rc=0 и новым lastConfigTime (стенд prom/prometheus:v3.1.0, 17.09). Комментарий в деплое утверждал обратное. Теперь promtool проверяет файлы С ДИСКА одноразовым контейнером, а при расхождении инода контейнер пересоздаётся до reload. - loki-config.yml — тот же пофайловый маунт, перезагрузки у Loki нет: пересоздание при расхождении инода. - Датасорсы Grafana применяются только при старте: POST /api/admin/provisioning/datasources/reload, отказ роняет деплой. Общий шаг — ops/metrics/recreate-stale-mount.sh: пересоздаёт только при расхождении инода и перепроверяет после; тесты исполняют его с подставным docker. Гейт берёт пофайловые маунты из docker-compose.metrics.yml. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
65 lines
3.6 KiB
Bash
Executable file
65 lines
3.6 KiB
Bash
Executable file
#!/bin/sh
|
||
# Пересоздать контейнер, если он читает СТАРЫЙ инод пофайлового bind-маунта
|
||
# (#3467, #3486).
|
||
#
|
||
# sh ops/metrics/recreate-stale-mount.sh ПРОЕКТ COMPOSE-ФАЙЛ СЕРВИС ФАЙЛ-НА-ХОСТЕ ПУТЬ-В-КОНТЕЙНЕРЕ
|
||
#
|
||
# ЗАЧЕМ. `git reset --hard` пишет изменившийся файл НОВЫМ инодом, а bind-маунт
|
||
# одного файла держит прежний: контейнер продолжает читать старый текст, `up -d`
|
||
# этого не видит (сравнивает описание сервиса, а не содержимое маунта), и
|
||
# перезагрузка по API тоже — процесс перечитывает путь, за которым в контейнере
|
||
# всё тот же старый инод. Проверено на prom/prometheus:v3.1.0 (17.09): файл
|
||
# подменён, POST /-/reload → rc=0, а в /api/v1/status/config остался прежний
|
||
# job. Лечит только пересоздание контейнера — его и делаем, но лишь при
|
||
# расхождении, чтобы не рвать работу на каждом деплое.
|
||
#
|
||
# Та же ловушка уже ловилась на Alertmanager (27.08), Alloy (#3380) и Caddy
|
||
# (#3443, ops/caddy-apply.sh). Контейнер не запущен — старого инода держать
|
||
# некому (при старте маунт берётся с хоста заново), выходим с 0. Сервис
|
||
# выключенного профиля сюда передавать нельзя: `up` с явным именем включает
|
||
# профиль и поднял бы сервис без окружения — гейт по профилю у вызывающего.
|
||
# Сверка не прочиталась — считаем расхождением. После пересоздания сверяем
|
||
# снова: не сошлось — exit 1, а не зелёный деплой со старым конфигом.
|
||
set -eu
|
||
|
||
if [ "$#" -ne 5 ]; then
|
||
echo "usage: $0 PROJECT COMPOSE_FILE SERVICE HOST_FILE CONTAINER_FILE" >&2
|
||
exit 2
|
||
fi
|
||
project=$1 compose_file=$2 service=$3 src=$4 dst=$5
|
||
|
||
cd "$(dirname "$0")/../.."
|
||
|
||
# Ошибку `ps` НЕ глушим: пустой ответ от упавшего compose читался бы как
|
||
# «контейнер не запущен» — и зелёный выход со старым конфигом.
|
||
cid() { docker compose -p "$project" -f "$compose_file" ps -q "$service"; }
|
||
seen() { docker exec "$1" stat -c %i "$dst" 2>/dev/null || true; }
|
||
|
||
id=$(cid)
|
||
if [ -z "$id" ]; then
|
||
echo "$service: контейнер не запущен — старый инод $src держать некому."
|
||
exit 0
|
||
fi
|
||
|
||
want=$(stat -c %i "$src")
|
||
if [ "$(seen "$id")" = "$want" ]; then
|
||
echo "$service: $src доехал до контейнера (инод $want)."
|
||
exit 0
|
||
fi
|
||
|
||
echo "$service: контейнер не видит текущий $src (инод на хосте $want) — пересоздаю."
|
||
docker compose -p "$project" -f "$compose_file" up -d --no-deps --force-recreate "$service"
|
||
|
||
i=0
|
||
while [ "$i" -lt 15 ]; do
|
||
id=$(cid)
|
||
if [ -n "$id" ] && [ "$(seen "$id")" = "$want" ]; then
|
||
echo "$service: пересоздан, $src доехал (инод $want)."
|
||
exit 0
|
||
fi
|
||
i=$((i + 1))
|
||
sleep 2
|
||
done
|
||
|
||
echo "::error::$service: после пересоздания контейнер всё ещё не видит $src (инод $want)"
|
||
exit 1
|