Tenho um cluster de Raspberry Pis em casa rodando Docker Swarm. Para manter tudo em ordem, todos usam a mesma configuração – usuários, versões do Docker, etc.

Às vezes, algumas atualizações exigem rodar a mesma coisa em todos os nós do cluster – por exemplo – criar uma nova rede macvlan do Docker.

Para fazer isso, é preciso rodar os mesmos comandos em todos os nós manualmente, entrando via ssh em cada um deles, executando os comandos e passando para o próximo.

Como estou tentando reduzir meu trabalho a longo prazo e deixar tudo o mais organizado possível, comecei a procurar maneiras de rodar comandos em todos os nós ao mesmo tempo.

Iteração 1: Script Python customizado

A primeira iteração foi criar um script Python customizado. Por um lado, era para aprender um pouco melhor Python; por outro, eu não tinha encontrado uma forma boa de rodar os comandos de um jeito mais nativo.

Como era de se esperar (ei, ainda tenho pouca experiência com a linguagem 🙂 ), ele virou uma bagunça inchada muito rápido e quebrava com facilidade.

Eu precisava achar outro jeito de fazer isso, sem ter que reaprender o que eu fiz 3 semanas atrás e o que quebrou da última vez.

Iteração 2: pssh

Pesquisando por aí, esbarrei no [pssh](https://linux.die.net/man/1/pssh), que consegue rodar coisas em paralelo via SSH.

Considerando tudo, foi bem fácil colocá-lo para funcionar.

O problema era que, para rodar algumas das coisas, eu precisava executá-las via sudo. Ficar passando senhas por aí não era o ideal, então tive que pesquisar de novo para descobrir como enviar a senha do sudo quando o servidor remoto a pedisse.

Acontece que é bem fácil se você topar com o comentário certo no StackOverflow.

A versão resumida (TL;DR;) é:

stty -echo; printf "Password: "; read PASS; stty echo; echo "${PASS}" | \
pssh -h <HOSTFILE> -o /tmp/output -t 0 -I "`cat ./commands.sh`"

O -I "`cat ./commands.sh`" precisa ser feito exatamente assim (entre aspas e crases desse jeito); caso contrário, ele não executa os comandos direito. A senha vai ser enviada como um comando separado, e não captada como entrada do sudo.

A outra pegadinha é que, por causa de como isso está montado, é preciso rodar o sudo com o parâmetro -S, para que ele leia a senha corretamente.