Como executo um script bash local em máquinas remotas via ssh?

51

Estou procurando uma maneira de enviar a configuração de uma máquina central para várias máquinas remotas sem a necessidade de instalar nada nas máquinas remotas.

O objetivo é fazer algo como você encontraria com ferramentas como cfengine, mas em um conjunto de máquinas que não possuem agentes configurados. Isso pode realmente ser uma boa técnica de configuração cfagentem um conjunto de máquinas remotas existentes.

tremoloqui
fonte
Semelhante no SO: stackoverflow.com/q/305035/435605
AlikElzin-kilaka
11
As perguntas reais têm 23 votos positivos, onde a duplicata no SO tem 55: P
MoJo 2/15

Respostas:

55

Você pode transmitir um script e executá-lo efêmera, canalizando-o e executando um shell.

por exemplo

echo "ls -l; echo 'Hello World'" | ssh me@myserver /bin/bash

Naturalmente, a "ls -l; echo 'Hello World'"peça pode ser substituída por um script bash armazenado em um arquivo na máquina local.

por exemplo

cat script.sh | ssh me@myserver /bin/bash

Felicidades!

tremoloqui
fonte
2
Como posso fazer esta corrida como sudo na system.eg remoto Se eu estava logado no servidor remoto eu normalmente executar este como sudo -u testuser script.sh
Sharjeel
E se o script que estou chamando envolver interações do usuário ???
Abhimanyu Srivastava 18/03/2013
20

Há várias maneiras de fazer isso.

1:

ssh user@remote_server 'bash -s' < localfile

2:

cat localfile  | ssh user@remote_server

3:

ssh user@remote_server "$(< localfile)"

número 3 é o meu caminho preferido, ele permite comandos interativos, por exemplo su -S service nginx restart

(O nº 1 consumirá o restante do script como entrada para a pergunta de senha quando você o usar su -S.)

Paul Verschoor
fonte
4
Em relação à execução de script local na máquina remota - Existe uma maneira de enviar variável como argumento para a máquina remota ..? ou seja, junto com o script, quero enviar uma variável (com várias linhas) como argumento para a máquina remota. O script então pretende usar a variável.
13

Eu recomendaria o Fabric do python para este fim:

#!/usr/bin/python
# ~/fabfile.py

from fabric_api import *

env.hosts = ['host1', 'host2']
def deploy_script():
    put('your_script.sh', 'your_script.sh', mode=0755)
    sudo('./your_script.sh')

# from shell
$ fab deploy_script

Você deve poder usar o descrito acima para começar. Consulte a excelente documentação do Fabric para fazer o resto. Como um adendo, é totalmente possível escrever seu script totalmente no Fabric - não é necessário copiar; no entanto, observe que, para alterar o script em todas as máquinas, você só precisará editar a cópia local e reimplementar. Além disso, com um pouco mais do que o uso básico da API, você pode modificar o script com base em qual host está sendo executado no momento e / ou em outras variáveis. É uma espécie de expectativa pitônica.

Sam Halicke
fonte
Não acho que isso responda exatamente à pergunta, mas gosto da ideia e pude ver o Fabric como uma ferramenta útil.
Tremoloqui 23/12/10
11
O @tremoloqui Fabric é um wrapper python para o ssh - nada precisa ser instalado nas máquinas de destino, salve o script que está sendo enviado. O qual, se reescrito como uma série de comandos do Fabric (usando rune sudo), nem é necessário.
Izkata 9/02/12
5

É exatamente para isso que o Ansible é usado. Não há agente, você apenas precisa criar um arquivo de texto chamado:

/etc/ansible/hosts

com conteúdo parecido com:

[webhosts]
web[1-8]

Isso especificaria que as máquinas "web1, web2 ... web8" estão no grupo "webhosts". Então você pode fazer coisas como:

ansible webhosts -m service -a "name=apache2 state=restarted" --sudo

para reiniciar o serviço apache2 em todas as suas máquinas, usando o sudo.

Você pode executar comandos on the fly como:

ansible webhosts -m shell -a "df -h"

ou você pode executar um script local na máquina remota:

ansible webhosts -m script -a "./script.sh"

ou você pode criar um manual (consulte a documentação para obter detalhes) com uma configuração completa com a qual deseja que seus servidores estejam em conformidade e implantá-la:

ansible-playbook webplaybook.yml

Basicamente, você pode começar a usá-lo como uma ferramenta de linha de comando para executar comandos em vários servidores e expandir seu uso em uma ferramenta de configuração completa, conforme desejar.

seumasmac
fonte
3
I como ansible tanto quanto o próximo cara, mas se ele está perguntando sobre um script, então ansible tem muito legal módulo de script :ansible webhosts -m script script.sh
ptman
11
Todas as outras respostas envolvem um script bash, mas não é o que ele pediu especificamente. Ele disse apenas enviando a configuração para máquinas remotas. Mas boa menção do módulo de script :)
seumasmac
Por favor vote nesta resposta! É assim que se faz!
pintainhos
@ptman Acabei de notar que, embora ele não mencione um script na pergunta, ele faz no título! Desculpa. Eu atualizei.
Seumasmac 22/09/2015
3

Conforme explicado nesta resposta, você pode usar o heredoc :

ssh user@host <<'ENDSSH'
#commands to run on remote host
ENDSSH

Você precisa ter cuidado com o heredoc, porque ele envia apenas texto, mas na verdade não espera a resposta. Isso significa que não esperará que seus comandos sejam executados.

BЈовић
fonte
1

A resposta aqui ( https://stackoverflow.com/a/2732991/4752883 ) funciona muito bem se você estiver tentando executar um script em uma máquina Linux remota usando plinkor ssh. Funcionará se o script tiver várias linhas ativadas linux.

** No entanto, se você estiver tentando executar um script em lote localizado em uma linux/windowsmáquina local e a sua máquina remota estiver Windows, ela consiste em várias linhas usando **

plink root@MachineB -m local_script.bat

não vai funcionar.

Somente a primeira linha do script será executada. Esta é provavelmente uma limitação de plink.

Solução 1:

Para executar um script em lotes com várias linhas (especialmente se for relativamente simples, consistindo em algumas linhas):

Se o script em lote original for o seguinte

cd C:\Users\ipython_user\Desktop 
python filename.py

você pode combinar as linhas usando o separador "&&", como segue no seu local_script.batarquivo, da seguinte maneira : https://stackoverflow.com/a/8055390/4752883 :

cd C:\Users\ipython_user\Desktop && python filename.py

Após essa alteração, você poderá executar o script conforme indicado aqui por @ JasonR.Coombs: https://stackoverflow.com/a/2732991/4752883

Solução 2:

Se o seu script em lote for relativamente complicado, pode ser melhor usar um script em lote que encapsule o comando plink, bem como a seguir, conforme indicado aqui por @Martin https://stackoverflow.com/a/32196999/4752883 :

rem Open tunnel in the background
start plink.exe -ssh [username]@[hostname] -L 3307:127.0.0.1:3306 -i "[SSH
key]" -N

rem Wait a second to let Plink establish the tunnel 
timeout /t 1

rem Run the task using the tunnel
"C:\Program Files\R\R-3.2.1\bin\x64\R.exe" CMD BATCH qidash.R

rem Kill the tunnel
taskkill /im plink.exe
alpha_989
fonte
0

Por que não simplesmente copiar o script primeiro e depois executá-lo?

scp your_script.sh the_server:
ssh the_server "chmod +x your_script.sh; ./your_script.sh"

É claro que você deve tomar cuidado para não carregá-lo em um local gravável pelo mundo, para que ninguém mais possa mexer nele antes de executá-lo (possivelmente como root).

Dave Vogt
fonte
11
O motivo pelo qual não quero fazer upload do script é que ele não precisa ser gerenciado e tem os riscos que você mencionou. Além disso, parece-me que é mais simples que o processo de várias etapas de upload, processamento e (opcionalmente) exclusão.
Tremoloqui 23/12/10
0

Reescreva o script de maneira que cada comando já seja prefixado com ssh e um nome de host / ip ou lista dele seja passado para o script como argumento (assumindo que você tenha a autenticação de chave sem senha / ssh-agent configurada). Pode ser necessário algum trabalho para transmitir corretamente os códigos de erro / retorno dos comandos remotos ....

rackandboneman
fonte
0

Se o script não for muito grande e você estiver usando o bash ou o ksh ...

ssh vm24 -t bash -c "$(printf "%q" "$(< shell-test.sh )")"

Stdin e stdout funcionam corretamente, mas o script é limitado ao tamanho do argumento (geralmente cerca de 100k). Argumentos para o script podem funcionar, no final da linha, possivelmente após um argumento "-" extra. O "-t" para alocar um pty é opcional.

Cuidado: Isso confunde a conclusão do bash, não clique na guia.

user3710044
fonte