Linux

在遠端伺服器上執行本地功能程式碼

  • April 29, 2015

我正在更新大約 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
  • 我可以放置shared.sh在我們所有的主機上,但我必須以某種方式保持文件同步,這在某些時候不可避免地會出錯。
  • 我可以放置shared.sh在 NFS 共享上,但如果 NFS 出現故障,我們將無法使用我們的腳本
  • 我可以在執行腳本之前 scpshared.sh到伺服器。這會起作用,但可能會有點慢,如果shared.sh依賴於其他腳本,我們也必須複製該文件
  • 我可以用它declare -f來提取我的函式的程式碼,但我想不出一個很好的方法來將它們改組到遠端伺服器。函式依賴也可能導致問題。

關於我能找到的最乾淨的解決方案是使用Process Substitution內聯執行共享庫:

#!/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. 有沒有更好的方法來解決這個問題?

在這裡回答我自己的問題。

我們現在有一個這樣的函式:

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中並不感到興奮。只要它保留幾個聲明,然後是一個簡單的函式呼叫,我認為就可以了。

所以我用來完成這項任務的是一種不同的方法。首先,我創建了一個名為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.

引用自:https://serverfault.com/questions/649359