Executando código de função local em um servidor remoto

1

Estou atualizando cerca de 20 scripts bash que executam várias tarefas operacionais do lado do servidor, verificando status / enviando relatórios etc. Em alguns ambientes, esses scripts são executados localmente, em outros, precisam ser executados remotamente. Os scripts devem detectar se precisam ser executados remotamente ou localmente e "fazer a coisa certa"

#!/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

Alguns dos scripts usam funções compartilhadas e variáveis de ambiente. Eu posso passar através de variáveis de ambiente usando a opção ssh SendEnv, mas não consigo descobrir uma boa maneira de disponibilizar funções compartilhadas ao executar remotamente.

./ shared.sh

# Shared functions and variables

export report_host="appdev7"

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

./ example

#!/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
  • Eu posso colocar shared.sh em todos os nossos hosts, mas tenho que manter os arquivos em sincronia de alguma forma e isso inevitavelmente vai dar errado em algum momento.
  • Eu posso colocar shared.sh em um compartilhamento NFS, mas se o NFS cair, perderemos a capacidade de usar nossos scripts
  • Eu poderia scp shared.sh para o servidor antes de executar o script. Isso funcionaria, mas pode ser um pouco lento, e se shared.sh depender de algum outro script, teríamos que copiar esse arquivo também
  • Posso usar declare -f para extrair o código de minhas funções, mas não consigo descobrir uma maneira legal de misturá-las para o servidor remoto. Dependências de função também podem causar problemas.

A solução mais simples que posso encontrar é executar a biblioteca compartilhada em linha usando Substituição de processo :

#!/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

Minhas perguntas então:

  1. Há algum problema com essa abordagem?
  2. Consegue ver uma maneira de evitar fazer referência à biblioteca compartilhada em vários lugares?
  3. Qualquer maneira de resolver dependências em shared.sh ? (por exemplo, se shared.sh depende de shared2.sh )
  4. Existe alguma maneira melhor de resolver este problema?
por dwurf 05.12.2014 / 05:12

2 respostas

0

Respondendo minha própria pergunta aqui.

Agora temos uma função como esta:

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"
}

Isso nos permite fazer coisas como (exemplo inventado):

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

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

Está longe de ser perfeito, mas é menos feio do que o que tínhamos:)

Resposta anterior:

Foi difícil acompanhar o fluxo de controle nos scripts acima, acho que estou resolvendo algo assim:

#!/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

Não estou entusiasmado com o código remoto estar em um heredoc. Desde que permaneça como algumas declarações seguidas por uma simples chamada de função, acho que está tudo bem.

    
por 08.12.2014 / 00:55
2

Então, o que tenho usado para realizar essa tarefa é uma abordagem diferente. Primeiro eu criei um script de execução chamado remotely 1 :

#!/bin/bash

# Usage:
# Put this thing as a shebang into your scripts
# i.e.:
# 
#   #!/bin/remotely [email protected]
#   
#   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"

E, em seguida, use-o em meus scripts que devem ser executados remotamente:

#!/bin/remotely [email protected]

echo "hello world from ${hostname}"

Ele também pode ser ajustado para suportar vários hosts usando algo como gnu-parallel .

por 28.04.2015 / 10:57