Запуск фоновой задачи на удаленном хосте через ssh приводит к зависанию вызывающего скрипта.

Вопрос или проблема

У меня есть следующий код:

for i in $ipb $ipc $ipd; do 
    ssh -i ~/.ssh/'key' 'name'@${i} /bin/bash << EOF
    nohup ${install_dir}/redis-stable/src/redis-server ${install_dir}/${port}/node.conf &
EOF
    let "port++"
done

который должен запустить серверы Redis на всех трех IP-адресах.
Однако, в настоящее время выполняется только первая команда (на $ipb).
Замечание: если команда nohup вводится вручную,
когда сервер Redis запущен с помощью команды,
пользователь должен нажать Enter, чтобы можно было ввести другие команды.
Проблема не связана с Redis, а скорее связана с тем, как написан скрипт, так что для этого случая не нужно знание Redis.
Ssh также работает нормально.

Что я должен изменить, чтобы запускать все три команды (по одной на каждый IP-адрес)?

Объяснение

Вы указали команду (/bin/bash) для ssh без -t, поэтому на удаленной стороне не выделяется tty. Затем происходят две вещи:

  • nohup не перенаправляет стандартные потоки (это произошло бы, если бы был tty);

  • SSH-сервер ждет eof перед отключением, локальный ssh продолжает работать.

    А eof происходит, когда нет файлового дескриптора, открытого каким-либо процессом на записи в конце канала, что обычно случается, когда все процессы, stdout которых не был перенаправлен в другое место, завершены.

Перенаправление стандартных потоков, как вы сделали в вашем ответе, заставляет SSH-сервер отключиться рано.

Нашел проблему, добавив это в конце строки nohup (прямо перед &)

> foo.out 2> foo.err < /dev/null

спасибо @berndbausch за указание правильного направления!

Ответ или решение

Для устранения проблемы, связанной с зависанием скрипта при запуске фоновой задачи на удалённом хосте через SSH, необходимо прежде всего понять, почему это происходит. Основная причина заключается в том, как именно работают ssh и nohup в данном контексте.

Проблема

При выполнении команды ssh, когда не используется опция -t, удалённая сторона не создает псевдотерминал. В результате этого возникают следующие проблемы:

  1. Отсутствие перенаправления стандартных потоков с помощью nohup: Если бы псевдотерминал был доступен, nohup автоматически перенаправил бы стандартные потоки; однако без него этого не происходит.

  2. Зависание SSH-сеанса: SSH сервер ожидает достижения конца файла (EOF) для разрыва соединения. Процесс SSH на локальной стороне продолжает работать до тех пор, пока есть открытые дескрипторы файлов для записи в поток, что зачастую случается, если выходной поток процесса не перенаправлён.

Решение

Решение вашей проблемы заключается в явном управлении стандартными потоками, что было замечено вами после внесения следующих изменений в код:

for i in $ipb $ipc $ipd; do 
    ssh -i ~/.ssh/'key' 'name'@${i} /bin/bash << EOF
    nohup ${install_dir}/redis-stable/src/redis-server ${install_dir}/${port}/node.conf > foo.out 2> foo.err < /dev/null &
EOF
    let "port++"
done

Добавление > foo.out 2> foo.err < /dev/null перед & позволяет:

  • Перенаправить стандартный вывод в файл foo.out, сохраняя информацию о запуске сервера.
  • Перенаправить стандартный поток ошибок в файл foo.err, что помогает в случае диагностики.
  • Отключить стандартный ввод, используя /dev/null, чтобы избежать блокировки ввода и, соответственно, разрыва SSH-соединения по EOF.

Таким образом, перенаправление потоков гарантирует, что все связанные с этим процессы завершаются, и SSH-соединение разрывается корректно после запуска команд на всех указанных IP-адресах.

Заключение

Правильное управление потоками ввода-вывода является критически важным аспектом при удалённом выполнении команд через SSH, особенно когда речь идёт о запуске фоновых процессов. Корректная настройка перенаправления стандартных потоков позволит предотвратить потенциальные блокировки и обеспечить стабильную работу вашего сценария.

Если у вас появятся дополнительные вопросы, обращайтесь – я всегда готов помочь.

Оцените материал
Добавить комментарий

Капча загружается...