−25%

на годовую оплату Windows, до 31.10. К тарифам

EQVPS
Начать

Как запустить скрипт 24/7 на Windows Server: переживает выход, падения и перезагрузки

Бот или скрипт круглосуточно на VPS с Windows Server 2022 только средствами Планировщика заданий: старт при загрузке без входа пользователя, перезапуск в течение минуты после падения и никогда двух копий. Мы убивали процесс и перезагружали сервер, чтобы проверить.

Обычный способ запустить бота на Windows VPS — стартовать его в сеансе RDP и закрыть окно. Это работает, пока обновление не перезагрузит сервер в 3 часа ночи, и запускать бота станет некому. Планировщик заданий, который есть на любом Windows Server, решает это без дополнительных программ. Схема ниже:

  • запускает скрипт при загрузке, до того как кто-то вошёл;
  • перезапускает его в течение минуты, если он упал или его убили;
  • никогда не запускает две копии одновременно.

Все три пункта мы проверили на нашем образе Windows Server 2022: убивали процесс, перезагружали сервер и считали экземпляры.

Пример скрипта

Замена вашему боту: каждые 10 секунд пишет строку, так что легко видеть, жив ли он.

New-Item -ItemType Directory -Force C:\bots | Out-Null
Set-Content C:\bots\heartbeat.ps1 -Encoding ascii -Value @'
while ($true) {
    Add-Content -Path C:\bots\heartbeat.log -Value ("{0:u} pid {1}" -f (Get-Date), $PID)
    Start-Sleep -Seconds 10
}
'@

Зарегистрируйте задачу

Выполните в PowerShell с правами администратора:

$action   = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument '-NoProfile -ExecutionPolicy Bypass -File C:\bots\heartbeat.ps1' -WorkingDirectory 'C:\bots'
$atBoot   = New-ScheduledTaskTrigger -AtStartup
$everyMin = New-ScheduledTaskTrigger -Once -At (Get-Date) -RepetitionInterval (New-TimeSpan -Minutes 1)
$settings = New-ScheduledTaskSettingsSet -ExecutionTimeLimit ([TimeSpan]::Zero) -MultipleInstances IgnoreNew -StartWhenAvailable
Register-ScheduledTask -TaskName 'mybot' -Action $action -Trigger $atBoot, $everyMin -Settings $settings -User 'SYSTEM' -RunLevel Highest
Start-ScheduledTask -TaskName 'mybot'

Что делает каждая часть:

  • -AtStartup запускает бота при загрузке Windows, без вошедшего пользователя.
  • Повтор раз в минуту перезапускает задачу каждую минуту. Если бот жив, ничего не происходит; если он упал, он стартует снова.
  • -MultipleInstances IgnoreNew делает повтор безопасным: запуск, который застаёт бота работающим, пропускается.
  • -ExecutionTimeLimit 0 отключает стандартный предел в 72 часа, после которого Планировщик остановил бы бота.
  • -User 'SYSTEM' запускает его без пароля и без сеанса. У SYSTEM полные права на сервере, поэтому так запускайте только свой код.

Для Python замените действие:

$action = New-ScheduledTaskAction -Execute 'C:\Program Files\Python312\python.exe' -Argument 'C:\bots\bot.py' -WorkingDirectory 'C:\bots'

Что мы проверили

Падение. Мы убили процесс бота через Stop-Process -Force. Через 46 секунд ежеминутный триггер запустил новый, и копия была ровно одна.

Перезагрузка. Мы перезагрузили сервер и не входили в систему. Через 40 секунд после загрузки бот уже писал в журнал, а пользовательских сеансов на сервере не было.

Что не сработало. Две вещи выглядят правильно, но не работают:

  1. «При сбое выполнения перезапускать через…» Мы её включили, убили процесс, и задача несколько минут стояла в состоянии «Готово». Настройка реагирует на то, что задача не смогла запуститься, а не на падение вашей программы потом.
  2. Обёртка с циклом (while ($true) { & bot; sleep 5 }). Она перезапускает упавшего бота, но если умирает сама обёртка, её дочерний процесс продолжает работать, а следующий запуск стартует второго бота. У нас оказалось две копии, а для Telegram-бота это два процесса, которые дерутся за одни и те же обновления.

Как проверить

Get-ScheduledTask -TaskName mybot | Select-Object TaskName, State
Get-ScheduledTaskInfo -TaskName mybot | Select-Object LastRunTime, NextRunTime
Get-Content C:\bots\heartbeat.log -Tail 5

Чтобы остановить бота на обслуживание, сначала выполните Disable-ScheduledTask mybot (иначе ежеминутный триггер запустит его снова), потом остановите процесс. Enable-ScheduledTask mybot возвращает всё обратно.

Журналы

Задача не сохраняет вывод. Пусть бот сам пишет журнал в файл, как в примере, или перенаправьте вывод в маленьком файле .cmd и укажите его в -Execute.

Какой тариф

Бот на PowerShell или Python почти не нагружает процессор, но сама Windows в простое занимает на нашем образе от 1 до 1,3 ГБ памяти. Windows-Small (4 vCPU, 4 ГБ памяти, 50 ГБ, $11/мес) — подходящий размер для одного-двух ботов. На тарифе с NAT исходящие подключения (Telegram, API бирж, вызовы веб-хуков) работают как обычно. Если бот должен принимать веб-хуки или другой входящий трафик, берите Windows Small-IP ($28/мес) с собственным IPv4.

Дальше: защитите RDP, а если удобнее управлять ботом по SSH, установите OpenSSH. Оплата в USDT, USDC или ETH, Windows — это Server 2022 с вашей лицензией (BYOL). Все тарифы: Windows VPS.

Частые вопросы

Почему бы просто не оставить скрипт в сеансе RDP?

Это работает, пока сеанс что-нибудь не завершит: выход из системы, перезагрузка после обновления или сбой. Если закрыть окно RDP (отключиться), сеанс живёт, но выход из системы убивает всё, что в нём запущено, а после перезагрузки никто не вошёл, чтобы запустить скрипт снова. Задача Планировщика от сеанса не зависит.

Разве в Планировщике нет настройки «перезапускать при сбое»?

Есть, но в нашей проверке она не помогла. Когда мы убили процесс скрипта, задача просто перешла в состояние «Готово» и осталась в нём. Эта настройка срабатывает, если задача не смогла запуститься, а не если ваша программа упала потом. Возвращает её повторяющийся триггер из этой инструкции.

Не окажется ли запущено две копии бота?

С этой схемой — нет. Задача запускает самого бота (без обёртки) и использует MultipleInstances IgnoreNew, поэтому ежеминутный триггер ничего не делает, пока бот жив. Дубли создаёт как раз обёртка с циклом: убейте обёртку, её дочерний процесс продолжит работать, а новая обёртка запустит ещё один. Мы видели именно это.

Это работает для Python или Node.js?

Да. Укажите интерпретатор в -Execute, а скрипт в -Argument, с полными путями, например -Execute 'C:\Program Files\Python312\python.exe' -Argument 'C:\bots\bot.py'. Устанавливайте интерпретатор для всех пользователей, чтобы учётная запись SYSTEM его нашла.

Комментарии

Пока нет комментариев. Будьте первым.

Оставить комментарий

Комментарии проходят модерацию перед публикацией.