Скрипт up в OpenVPN запустился слишком рано или он так и не был готов?
Хук up срабатывает, когда появляется туннельное устройство, раньше маршрутов и готовности приложения. Привяжите скрипт к нужному событию вместо добавления паузы.
Скрипт up выполняется, завершается с нулём, а задача внутри него не достигает приватного сервиса, который из терминала прекрасно работает две секунды спустя. Инстинкт подсказывает, что дело в тайминге, и обычное исправление — пауза, которая скрывает гонку, не понимая её. Более полезное прочтение: «up» называет хук, а не гарантирует готовность. Он срабатывает, когда туннельное устройство настроено, а это раньше маршрутов, DNS и уж точно раньше любого приложения за туннелем.
Тест в оболочке прошёл, потому что выполнялся после того, как всё устоялось. Хук выполнялся в один конкретный момент.
Где up находится в порядке запуска
OpenVPN настраивает туннельное устройство и затем запускает хук up. Установка маршрутов — более поздний этап, и у него свой хук, поэтому задача, зависящая от маршрутов и помещённая в up, может увидеть устройство без полезного пути. Поместите работу с маршрутами в route-up, и проблема тайминга обычно исчезает.
Руководство документирует этот порядок и окружение, которое получает каждый хук. Этот порядок — всё объяснение классического отчёта «работает из моей оболочки», и он же причина, по которой один и тот же скрипт может пройти на одной машине и не пройти на другой с более медленным этапом маршрутов. Ни один из результатов ничего не говорит о качестве скрипта.
DNS и готовность приложения наступают ещё позже. Даже установленные маршруты не доказывают, что удалённая конечная точка слушает, что имя разрешается или что существует сессия рабочего стола для задачи в масштабе пользователя.
Что хук на самом деле наследует
Хук выполняется под учётной записью и в окружении процесса OpenVPN, что не то же самое, что ваша интерактивная оболочка. Ему может не хватать шины сеанса пользователя, окружения рабочего стола или учётных данных, которые разблокируются только при входе. Запишите, что из этого действительно нужно задаче.
Это важно для задач, которые выглядят сетевыми, но зависят от пользовательского контекста: монтирование общего ресурса с пользовательскими учётными данными, настройка прокси для пользователя или настройка принтера. Привилегированный туннельный хук — неправильное место для них, и никакое ожидание не исправит отсутствующую сессию.
Когда задача падает в хуке, запишите учётную запись, окружение и первую операцию, которая завершилась неудачей. Это отделяет преждевременный тайминг от проблемы с разрешениями или контекстом, и у этих двух совершенно разные исправления. Угадывание между ними — это путь, которым пятиминутное исправление превращается в целый день.
Почему добавление паузы не является проверкой готовности
Пауза задерживает скрипт и сдвигает окно гонки. В быстрый день задача удаётся; в медленный день она снова падает, и сбой становится труднее воспроизвести, потому что задержка как будто работает. Задержка, которая работает только под лёгкой нагрузкой, — совпадение, а не исправление.
Если зависимость — маршрут, используйте хук, соответствующий установке маршрутов. Если зависимость — сервис за туннелем, используйте ограниченную проверку, которая ждёт именно это условие и явно падает, когда оно так и не наступает.
Держите сбой явным. Хук, завершающийся с нулём, когда его реальная работа провалилась, хуже хука, который падает, потому что менеджер сервисов записывает успех и больше никто не смотрит. Одна строка лога из самого скрипта — самый дешёвый способ сохранить это честным.
Очистка и переподключение заслуживают такой же заботы
Скрипт, который меняет общее состояние, нуждается в соответствующем пути отмены. Маршруты, записи резолвера и метки брандмауэра, добавленные хуком, должны быть идемпотентными и обратимыми, иначе переподключение наложит вторую копию поверх первой.
Переподключения также могут запускать хуки чаще, чем полный запуск сервиса. Постоянный интерфейс, который заново устанавливает сеанс, может снова запустить цикл up и route-up, поэтому скрипт, предполагающий одно выполнение на загрузку, в конце концов начнёт работать неправильно. Посчитайте выполнения в логе за день мигания, чтобы увидеть, как часто он на самом деле запускается. Затем сделайте скрипт безопасным для такого количества запусков.
Проверяйте тот жизненный цикл, который падал, а не более позднюю команду. Воспроизведите загрузку или переподключение, наблюдайте за выполнением хука и подтвердите, что задача удалась в тот момент, когда он действительно запускается. Успех из интерактивной оболочки потом — это доказательство о зависимости, а не доказательство того, что путь запуска работает.
Назовите событие, от которого зависит каждая задача, и привяжите задачу к этому событию. Всё остальное — задержка, притворяющаяся проверкой.