#!/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