Эта поломка стоила нам одной машины на три часа простоя и просадки рейтинга с 0.99 до 0.79 на другой. Коварна она тем, что не видна ни одной привычной проверкой: канал до биржи идеальный, туннель работает, клиенты крутятся, в логах ни единой ошибки. А машина при этом для биржи мертва.
Ниже — как её распознать, почему обычная диагностика уводит в сторону, и готовый сторож, который ловит и чинит это сам за три минуты.
Как это выглядит
Машина показывается на сайте неактивной. Клиенты её не видят, новые аренды не приходят. Если на ней в этот момент кто-то работает — рейтинг начинает течь вниз, и довольно быстро.
Первое, что показывает API хоста:
| поле | здоровая машина | больная |
|---|---|---|
timeout | 0 | 1000000 либо растёт: 18516 и дальше |
listed | true | true — машина числится в каталоге |
verification | verified | verified — верификация на месте |
| рейтинг | стабилен | течёт вниз, если есть аренда |
То есть машина формально всё ещё в каталоге и верифицирована — просто биржа с ней не разговаривает.
На самом риге картина такая:
# сколько ответов от биржи за последние 300 строк журнала
tail -300 /var/lib/vastai_kaalia/kaalia.log | grep -c Heartbeat
# 0 <-- вот он, признак
# при этом наши запросы уходят исправно
tail -300 /var/lib/vastai_kaalia/kaalia.log | grep -c keepalive
# 19 <-- агент жив и стучится
# и раз за разом пересоздаёт соединение
grep -ac lmsg_elap /var/lib/vastai_kaalia/kaalia.log
# 127 за сутки
Ключевое сочетание: keepalive уходит, а Heartbeat не приходит. Агент исправно зовёт биржу, биржа молчит. Через 55 секунд молчания агент считает соединение мёртвым, пересоздаёт сокет — и всё повторяется по кругу.
Заодно в системе видны сотни перезапусков службы за сутки. Это не сбой: у vast есть собственный сторож, который ежеминутно проверяет связь и дёргает агента, если её нет. Он честно пытается помочь, но проблема не в агенте, поэтому перезапуски ничего не меняют.
Почему обычная диагностика заводит в тупик
Мы потратили на поиск причины несколько часов, последовательно проверяя всё, что обычно ломается. Все четыре версии оказались ложными.
Канал до биржи? Идеальный. Десять TCP-соединений из десяти, отклик одна миллисекунда. Ни одного провала.
Туннель или VPS? Тоже чисто: ошибок в журнале нет, сервер без нагрузки, выход в интернет отвечает правильным адресом. И главный аргумент — две наши машины сидели на одном VPS, одна была мертва, вторая работала нормально. Сервер тут ни при чём.
Может, порт биржи закрыт? Нет, открыт и отвечает мгновенно. Вот это самая коварная деталь: TCP-порт мёртвого контроллера принимает соединения как ни в чём не бывало. Молчит только служба уровня приложения. Поэтому любая проверка вида «жив ли порт» покажет зелёный свет.
Проблема на риге? Тоже нет: клиенты работают, видеокарты видны, ошибок ядра ноль.
Что происходит на самом деле
У биржи не один сервер, а несколько контроллеров. Агент на риге при первом подключении получает адрес одного из них и запоминает его в файле:
cat /var/lib/vastai_kaalia/controller_connection
# 3.239.230.153:7071
Дальше агент держится за этот адрес намертво. Если контроллер на стороне биржи перестаёт отвечать — риг будет долбиться в него бесконечно, пересоздавая соединение каждую минуту. Сам он другой адрес не выберет.
Это сбой на стороне площадки, а не наша поломка. За одни сутки мы насчитали четыре мёртвых контроллера. И вот что важно понимать:
Список живых контроллеров протухает за часы. Адрес, который утром работал нормально, к обеду умер и утащил с собой машину на три часа простоя. Составлять «белый список» бесполезно — нужно реагировать на симптом, а не на конкретные адреса.
Распределение по контроллерам случайное. Поэтому часть парка может работать прекрасно, пока другая часть лежит — и это сбивает с толку: кажется, что дело в конкретных машинах.
Сколько это стоит
Три реальных случая за один день.
| машина | что было | цена |
|---|---|---|
| риг с активной арендой | залипание замечено спустя часы | рейтинг 0.99 → 0.79 |
| риг без аренд | три часа молчания, поймано вручную | простой, рейтинг почти не тронут |
| риг с двумя арендами | залипал дважды, вылечен автоматически | по 48 секунд, потерь нет |
Разница между первой и третьей строкой — только в том, что на третьей машине уже стоял сторож. Он поймал оба залипания и переподключил риг раньше, чем это успело сказаться на деньгах.
Лечение вручную
Само лечение простое: стереть кэш и дать агенту выбрать контроллер заново. Контейнеры клиентов при этом не затрагиваются — проверено на машинах с активными арендами.
systemctl stop vastai
sleep 3
mv /var/lib/vastai_kaalia/controller_connection \
/var/lib/vastai_kaalia/controller_connection.bak.$(date +%s)
systemctl start vastai
sleep 45
# проверяем результат
echo "новый контроллер: $(cat /var/lib/vastai_kaalia/controller_connection)"
echo "heartbeat: $(tail -150 /var/lib/vastai_kaalia/kaalia.log | grep -c Heartbeat)"
Адрес должен смениться, а счётчик heartbeat — стать больше нуля. В поле timeout у машины обнуляется в течение пары минут, и она снова появляется в каталоге.
Сторож, который делает это сам
Ловить такое вручную бессмысленно: залипание случается в произвольный момент, а замечаешь его, когда уже потерял деньги. Ниже — служба, которая следит за признаком и лечит сама.
Логика простая: раз в полминуты смотрим, когда биржа отвечала последний раз. Если она молчит дольше двух с половиной минут, но наши запросы при этом уходят — значит это именно залипание, и надо сбросить кэш. Второе условие важно: если не уходят и запросы, проблема другая, и вмешиваться нельзя.
cat > /usr/local/bin/vast-controller-watchdog.sh <<'EOF'
#!/bin/bash
# Ловит залипший контроллер биржи: агент шлёт запросы, а ответов нет.
# Лечение — сброс кэша адреса. Контейнеры клиентов не трогает.
K=/var/lib/vastai_kaalia/kaalia.log
CC=/var/lib/vastai_kaalia/controller_connection
LOG=/var/log/vast-controller-watchdog.log
CHECK=30 # как часто смотреть, секунд
STALE=150 # столько секунд молчания биржи = залипание
COOLDOWN=1800 # не лечить чаще раза в 30 минут
last_fix=0
log(){ echo "$(date -u "+%F %T") $*" >> "$LOG"; }
last_hb(){
local t
t=$(grep -a "handle_message mtype: Heartbeat" "$K" 2>/dev/null | tail -1 \
| grep -oE "^\[[0-9-]+ [0-9:]+" | tr -d "[")
[ -z "$t" ] && { echo 0; return; }
date -u -d "$t" +%s 2>/dev/null || echo 0
}
log "=== СТАРТ. Текущий контроллер: $(cat $CC 2>/dev/null) ==="
while true; do
sleep $CHECK
now=$(date -u +%s)
hb=$(last_hb)
[ "$hb" = "0" ] && continue
age=$(( now - hb ))
[ $age -lt $STALE ] && continue
# если и наши запросы не уходят — случай другой, не вмешиваемся
ka=$(tail -300 "$K" | grep -c keepalive)
if [ "$ka" -eq 0 ]; then
log "биржа молчит $age с, но и keepalive не идёт — случай не наш"
continue
fi
if [ $(( now - last_fix )) -lt $COOLDOWN ]; then
log "залипание ($age с), но лечили недавно — жду окончания паузы"
continue
fi
old=$(cat "$CC" 2>/dev/null)
log "ЗАЛИПАНИЕ: биржа молчит $age с, контроллер $old — сбрасываю кэш"
systemctl stop vastai
sleep 3
mv "$CC" "$CC.bak.$now" 2>/dev/null
systemctl start vastai
sleep 45
new=$(cat "$CC" 2>/dev/null)
hb2=$(tail -150 "$K" | grep -c Heartbeat)
if [ "$hb2" -gt 0 ]; then
log " ВЫЛЕЧЕНО: $old -> $new, heartbeat=$hb2"
else
log " ВНИМАНИЕ: $old -> $new, но heartbeat всё ещё 0 — нужен ручной разбор"
fi
last_fix=$(date -u +%s)
done
EOF
chmod +x /usr/local/bin/vast-controller-watchdog.sh
bash -n /usr/local/bin/vast-controller-watchdog.sh || echo "СИНТАКСИС СЛОМАН"
cat > /etc/systemd/system/vast-controller-watchdog.service <<'UNIT'
[Unit]
Description=Watchdog for stuck vast controller
After=network-online.target vastai.service
[Service]
Type=simple
ExecStart=/usr/local/bin/vast-controller-watchdog.sh
Restart=always
RestartSec=30
[Install]
WantedBy=multi-user.target
UNIT
systemctl daemon-reload
systemctl enable --now vast-controller-watchdog
sleep 3
echo "служба: $(systemctl is-active vast-controller-watchdog)"
tail -2 /var/log/vast-controller-watchdog.log
В конце вывода должно быть служба: active и запись о старте с текущим адресом контроллера.
Почему порог именно две с половиной минуты
Соблазн поставить меньше велик — хочется реагировать за полминуты. Так делать нельзя, и вот почему.
Агент биржи сам считает соединение мёртвым, когда она молчит 55 секунд, и пересоздаёт сокет. Это его штатная реакция на сетевую заминку, и с короткими перебоями он справляется без посторонней помощи. Порог в 150 секунд даёт ему две-три такие попытки.
Поставьте меньше — и сторож начнёт вмешиваться там, где всё починилось бы само: перезапускать службу на ровном месте и менять исправно работающий контроллер на случайный другой. Лечение станет вреднее болезни.
| параметр | значение | смысл |
|---|---|---|
CHECK | 30 с | как часто смотреть журнал |
STALE | 150 с | молчание дольше этого = залипание |
COOLDOWN | 1800 с | защита от цикла: не лечить чаще раза в полчаса |
С такими настройками полный цикл — от начала залипания до восстановленной связи — занимает около трёх с половиной минут.
Как убедиться, что сторож работает
Не стоит верить механизму, который ни разу не срабатывал. Проверить его можно честно: подсунуть заведомо мёртвый адрес и посмотреть, поймает ли.
Делать это надо только на машине без аренд — на несколько минут она реально пропадёт из каталога.
CC=/var/lib/vastai_kaalia/controller_connection
echo "было: $(cat $CC)"
systemctl stop vastai
sleep 2
cp -a $CC $CC.before_test
echo -n "192.0.2.1:7071" > $CC # адрес из документационного диапазона
systemctl start vastai
echo "подсунут нерабочий адрес, ждём срабатывания 3-4 минуты"
echo "смотреть: tail -f /var/log/vast-controller-watchdog.log"
В журнале должно появиться две записи: обнаружение залипания с указанием, сколько секунд молчит биржа, и результат лечения с новым адресом. У нас это выглядело так:
11:09:29 ЗАЛИПАНИЕ: биржа молчит 347 с, контроллер 3.239.230.153:7071 — сбрасываю кэш
11:10:17 ВЫЛЕЧЕНО: 3.239.230.153:7071 -> 34.226.220.129:7071, heartbeat=6
А вот записи с боевого срабатывания на машине с двумя активными арендами — в тот же день, без нашего участия:
13:11:20 ЗАЛИПАНИЕ: биржа молчит 168 с — сбрасываю кэш
13:12:08 ВЫЛЕЧЕНО: heartbeat=6
17:48:52 ЗАЛИПАНИЕ: биржа молчит 178 с — сбрасываю кэш
17:49:40 ВЫЛЕЧЕНО: heartbeat=5
Сорок восемь секунд на лечение, обе аренды продолжили работать. Хозяин узнал об этом только из журнала.
Быстрая проверка вручную
Если хочется просто посмотреть, всё ли в порядке прямо сейчас — три строки:
echo "контроллер: $(cat /var/lib/vastai_kaalia/controller_connection)"
echo "heartbeat: $(tail -300 /var/lib/vastai_kaalia/kaalia.log | grep -c Heartbeat)"
echo "keepalive: $(tail -300 /var/lib/vastai_kaalia/kaalia.log | grep -c keepalive)"
Heartbeat больше нуля — связь есть, всё хорошо. Heartbeat ноль при живом keepalive — залипание, сторож должен сработать в ближайшие минуты.
Коротко
- Машина может числиться в каталоге, быть верифицированной и при этом не отвечать бирже — признак один: heartbeat ноль при живом keepalive
- Ни канал, ни туннель, ни VPS тут ни при чём: открытый порт контроллера ничего не доказывает, молчит служба уровня приложения
- Причина — агент запомнил адрес контроллера и держится за него, даже когда тот умер
- Лечение занимает секунды: стереть кэш и перезапустить агента, клиенты не страдают
- Списки живых адресов бесполезны — они меняются за часы; реагировать надо на симптом
- Сторож окупается с первого срабатывания: у нас он дважды за день спас машину с двумя арендами