Назад | Перейти на главную страницу

Выполнение кода локальной функции на удаленном сервере

Я обновляю около 20 сценариев bash, которые выполняют различные операционные задачи на стороне сервера, проверяют статус / отправляют отчеты и т. Д. В некоторых средах эти сценарии будут запускаться локально, в других им необходимо запускать удаленно. Скрипты должны определять, нужно ли им запускать удаленно или локально, и «делать правильные вещи».

#!/bin/bash

# Generate the foo report

execute_host=appdev7

if [[ "$execute_host" != "$(hostname)" ]]; then
    # ssh-agent will provide passwordless logon
    ssh "$execute_host" < $0
else
    # run report
    echo "Report"
fi

Некоторые скрипты используют общие функции и переменные среды. Я могу передавать переменные среды с помощью опции ssh SendEnv, но я не могу найти хороший способ сделать общие функции доступными при удаленном запуске.

./shared.sh

# Shared functions and variables

export report_host="appdev7"

function run_report() {
    # run report
    echo 'Report'
}

./пример

#!/bin/bash

# Generate the foo report

[[ -f shared ]] && source ./shared.sh

export execute_host="$report_host"

if [[ "$execute_host" != "$(hostname)" ]]; then
    # ssh-agent will provide passwordless logon
    ssh -o SendEnv='report_host' "$execute_host" < $0
else
    # This doesn't work when the script is run remotely
    run_report
fi

Самое чистое решение, которое я могу найти, - это запустить встроенную общую библиотеку с помощью Замена процесса:

#!/bin/bash

# Generate the foo report

# source the shared code/vars if we're running locally
[[ -f shared.sh ]] && source shared.sh

execute_host="$report_host"

if [[ "$execute_host" != "$(hostname)" ]]; then
    # ssh-agent will provide passwordless logon
    # Note that we source the shared library in-line with the script
    ssh -T "$execute_host" < <(cat shared.sh $0)
else
    run_report
fi

Тогда мои вопросы:

  1. Есть ли проблемы с таким подходом?
  2. Можете ли вы найти способ избежать ссылок на общую библиотеку в нескольких местах?
  3. Любой способ разрешить зависимости в shared.sh? (например, если shared.sh зависит от shared2.sh)
  4. Есть ли лучший способ решить эту проблему?

Так что для решения этой задачи я использовал другой подход. Сначала я создал исполняемый скрипт под названием remotely1:

#!/bin/bash

# Usage:
# Put this thing as a shebang into your scripts
# i.e.:
# 
#   #!/bin/remotely user@your-server.com
#   
#   echo "hello world from ${hostname}"
#

login=$1
script=$2

args=${@#$1}
args=${args#$2}

tar cz $script | ssh $login "tar xz && bash --login $script $args"

А потом использовать его в моих скриптах, которые должны выполняться удаленно:

#!/bin/remotely user@your-server.com

echo "hello world from ${hostname}"

Его также можно настроить для поддержки нескольких хостов, используя что-то вроде gnu-parallel.

Отвечая на свой вопрос здесь.

Теперь у нас есть такая функция:

function remote::run() {
  # usage: remote::run "host" "includes" "commands"
  # where "includes" is a list of functions to export to
  # the remote host

  [[ -n "$2" ]] && includes="$(declare -f $2);"
  ssh -T $1 "$includes $3"
}

Это позволяет нам делать такие вещи (надуманный пример):

function status::report() {
  date
  host $(hostname)
}

remote::run $REMOTE_HOSTNAME status::report 'echo status report:; status::report' > out

Это далеко не идеально, но менее уродливо, чем было у нас :)

Предыдущий ответ:

Было сложно проследить за потоком управления в приведенных выше сценариях, я думаю, что остановился на чем-то вроде этого:

#!/bin/bash

# Generate the foo report

# source the shared code/vars if we're running locally
[[ -f shared.sh ]] && source shared.sh

if [[ "$report_host" != "$(hostname)" ]]; then
    # ssh-agent will provide passwordless logon
    runner="ssh -T -o SendEnv=report_host $report_host"
else
    # run report locally
    runner="bash"
fi

report="$($runner << EOF
    $(declare -f run_report)
    run_report
EOF
)"

echo "$report" | mail -s "daily foo report" root

Я не в восторге от того, что удаленный код находится в heredoc. Пока остается несколько объявлений, за которыми следует простой вызов функции, я думаю, что это нормально.