Wiele poleceń podczas SSH w sesji SSH

10

Mam maszynę lokalną, która ma wykonać sesję SSH na mastermaszynie zdalnej , a następnie kolejną wewnętrzną sesję SSH z masterkażdego do każdego zdalnego slaves, a następnie wykonać 2 polecenia, tj. Usunąć konkretny katalog i odtworzyć go.

Zauważ, że lokalna maszyna ma SSH bez hasła do urządzenia master, a master ma bez hasła hasło do urządzenia slave. Również wszystkie nazwy hostów są znane .ssh/configz maszyn lokalnych / głównych, a nazwy hostów niewolników znajdują się slaves.txtlokalnie i stamtąd je czytam.

Więc to, co robię i pracuję, to:

username="ubuntu"
masterHostname="myMaster"
while read line
do

    #Remove previous folders and create new ones.
    ssh -n $username@$masterHostname "ssh -t -t $username@$line "rm -rf Input Output Partition""
    ssh -n $username@$masterHostname "ssh -t -t $username@$line "mkdir -p EC2_WORKSPACE/$project Input Output Partition""


    #Update changed files...
    ssh -n $username@$masterHostname "ssh -t -t $username@$line "rsync --delete -avzh /EC2_NFS/$project/* EC2_WORKSPACE/$project""

done < slaves.txt 

Ten klaster znajduje się na Amazon EC2 i zauważyłem, że przy każdej iteracji powstaje 6 sesji SSH, co powoduje znaczne opóźnienie. Chciałbym połączyć te 3 polecenia w 1, aby uzyskać mniej połączeń SSH. Próbowałem więc połączyć pierwsze 2 polecenia

ssh -n $username@$masterHostname "ssh -t -t $username@$line "rm -rf Input Output Partition && mkdir -p EC2_WORKSPACE/$project Input Output Partition""

Ale to nie działa zgodnie z oczekiwaniami. Wydaje się, że wykonuje pierwszą ( rm -rf Input Output Partition), a następnie kończy sesję i kontynuuje. Co mogę zrobić?

mgus
źródło
3
Zamiast takiej pośredniczości w poleceniu możesz użyć -Jopcji, która zdefiniowałaby twojego hosta skoku.
Hauleth

Odpowiedzi:

15

Weź pod uwagę, że &&jest to operator logiczny. Czyni nie znaczyć „też uruchomić tę komendę” oznacza „tego polecenia, jeżeli druga udało”.

Oznacza to, że jeśli rmpolecenie się nie powiedzie (co się stanie, jeśli którykolwiek z trzech katalogów nie istnieje), to polecenie mkdirnie zostanie wykonane. To nie brzmi jak pożądane zachowanie; jeśli katalogi nie istnieją, prawdopodobnie można je utworzyć.

Posługiwać się ;

Średnik ;służy do oddzielania poleceń. Polecenia są uruchamiane sekwencyjnie, czekając na każde z nich przed przejściem do następnego, ale ich sukces lub porażka nie mają na siebie wpływu.

Unikaj wewnętrznych cytatów

Cytaty w innych cytatach należy unikać, w przeciwnym razie tworzysz dodatkowy punkt końcowy i punkt początkowy. Twoje polecenie:

ssh -n $username@$masterHostname "ssh -t -t $username@$line "rm -rf Input Output Partition && mkdir -p EC2_WORKSPACE/$project Input Output Partition""

Staje się:

ssh -n $username@$masterHostname "ssh -t -t $username@$line \"rm -rf Input Output Partition && mkdir -p EC2_WORKSPACE/$project Input OutputPartition\""

Twoje bieżące polecenie, z powodu braku cudzysłowów, powinno wykonywać:

ssh -n $username@$masterHostname "ssh -t -t $username@$line "rm -rf Input Output Partition

jeśli to się uda:

mkdir -p EC2_WORKSPACE/$project Input Output Partition"" # runs on your local machine

Zauważysz, że podświetlanie składni pokazuje tutaj całe polecenie na czerwono, co oznacza, że ​​całe polecenie jest ciągiem przekazywanym do ssh. Sprawdź swoją maszynę lokalną; możesz mieć katalogi Input Outputi Partitiongdzie to działało.

Centyman
źródło
Rozumiem twoje punkty. Jedną z części, która mnie myliła, był średnik, ponieważ myślałem, że wykona więcej niż 1 komendę w tym samym czasie, dlatego nie użyłem go.
mg
Średnik nie spowoduje, że polecenia będą wykonywane w tym samym czasie, patrz tutaj jako odniesienie do wykonywania poleceń. Te &polecenia przyczyny uruchomić int on tła, co oznacza, przyzwyczajenie być czekał do końca przed przejściem do następnego.
Centimane
10

Zawsze możesz zdefiniować w swoim jumpboxie Multipleksowanie w OpenSSH

Multipleksowanie to możliwość wysyłania więcej niż jednego sygnału przez jedną linię lub połączenie. Dzięki multipleksowaniu OpenSSH może ponownie wykorzystywać istniejące połączenie TCP do wielu jednoczesnych sesji SSH zamiast tworzyć nowe za każdym razem.

Zaletą multipleksowania SSH jest to, że narzut związany z tworzeniem nowych połączeń TCP jest wyeliminowany. Ogólna liczba połączeń, które komputer może zaakceptować, jest ograniczonym zasobem, a limit jest bardziej zauważalny na niektórych komputerach niż na innych i różni się znacznie w zależności od obciążenia i użycia. Występuje również znaczne opóźnienie przy otwieraniu nowego połączenia. Działania, które wielokrotnie otwierają nowe połączenia, można znacznie przyspieszyć za pomocą multipleksowania.

Aby to zrobić w /etc/ssh/ssh_config:

ControlMaster auto
ControlPath ~/.ssh/controlmasters/ssh_mux_%h_%p_%r
ControlPersist 30m

W ten sposób wszelkie kolejne połączenia wykonane z tym samym serwerem w ciągu następnych 30 minut zostaną wykonane ponownie przy użyciu poprzedniego połączenia ssh.

Możesz także zdefiniować go dla maszyny lub grupy maszyn. Z podanego linku.

Host machine1
    HostName machine1.example.org
    ControlPath ~/.ssh/controlmasters/%r@%h:%p
    ControlMaster auto
    ControlPersist 10m
Rui F. Ribeiro
źródło
To interesujące! Czy istnieje sposób, aby jawnie włączyć lub wyłączyć dla danego połączenia? Wydaje się, że przesadna zmiana tak drastycznie zmienia wszystkie połączenia SSH dla tego jednego przypadku użycia. Czy można go używać w bardziej precyzyjny sposób? Aby rozpocząć multipleksowanie tylko określonego połączenia?
Centimane
@Centimane tak, zaktualizowałem odpowiedź
Rui F Ribeiro
1
Sugerowałbym umieszczenie gniazda w domu użytkownika, a nie w świecie, w którym można czytać /tmp/.
heemayl
@heemayl Dobra uwaga. Będę edytować, gdy będę na komputerze.
Rui F Ribeiro
@RuiFRibeiro również wygląda, zgodnie z man ssh, ControlPath, ControlMasteri ControlPersistsą ważne opcje przekazać sshpolecenie używając -o. Może to być bardziej precyzyjny przypadek użycia, skonfiguruj multipleksowanie w pierwszym sshskrypcie i poddaj go recyklingowi dla innych, ale w przeciwnym razie uniknij obniżenia wydajności. Zastanawiam się, jaki jest punkt odniesienia dla multipleksowania VS nie dla 3 połączeń SSH, biorąc pod uwagę, że „Istnieje również znaczne opóźnienie przy otwieraniu nowego połączenia”
Centimane
4

Możesz umieścić wszystkie swoje polecenia w osobnym skrypcie na serwerze „głównym”.

Skrypt główny

#!/bin/bash
rm -rf "Input Output Partition"
mkdir -p "EC2_WORKSPACE/$project Input Output Partition"

Następnie w skrypcie ssh nazwij to tak: Skrypt SSH

username="ubuntu"
masterHostname="myMaster"
while read line
do
ssh -n $username@$masterHostname "ssh -t -t $username@$line < /path/to/masterscript.sh"
ssh -n $username@$masterHostname "ssh -t -t $username@$line "rsync --delete -avzh /EC2_NFS/$project/* EC2_WORKSPACE/$project""
done < slaves.txt 

LUB jeśli wszystkie pliki muszą znajdować się na komputerze początkowym, możesz zrobić coś takiego:

skrypt1

script2="/path/to/script2"
username="ubuntu"
while read line; do
cat $script2 | ssh -t -t $username@line
done < slaves.txt

skrypt2

#!/bin/bash
rm -rf "Input Output Partition"
mkdir -p "EC2_WORKSPACE/$project Input Output Partition"
rsync --delete -avzh "/EC2_NFS/$project/* EC2_WORKSPACE/$project"

skrypt ssh

script1="/path/to/script1"
username="ubuntu"
masterHostname="myMaster"
cat $script1 | ssh -n $username@$masterHostname
jesse_b
źródło
1

Jakiś czas temu miałem okazję używać gniazd sterujących, tak jak inne odpowiedzi polecają (ta odpowiedź jest zasadniczo połączeniem używania gniazd sterujących takich jak ta odpowiedź i skryptów takich jak ta odpowiedź ).

Przypadek użycia to włamanie: authorized_keysużytkownik docelowy był okresowo nadpisywany przez zaplanowane zadanie i chciałem szybko przetestować rzeczy bez przechodzenia przez biurokrację potrzebną do dodania czegoś do tego pliku. Więc ustawiłem pętlę while, która w razie potrzeby dodała klucz do tego pliku, uruchomiłem test i anulowałem pętlę. Byłoby jednak małe okno, w którym zaplanowane zadanie zastąpiłoby plik, a moja pętla nadal sleepdziałała. Tak więc ustawienie gniazda kontrolnego na początku pozwoliłoby mojemu skryptowi SSH później bez problemów:

#! /bin/bash -xe
. "${CONFIG_DIR}/scripts/setup-ssh.sh"

# Build and test
export TEST_LABEL="${_started_by}-${BUILD_TAG%-BUILD*}"
#...
xargs --arg-file test-list \
    --no-run-if-empty \
    --process-slot-var=NUM \
    --max-procs=${#SERVERS[@]} \
    --max-args="${BATCH_SIZE:-20}" \
    "${CONFIG_DIR}/scripts/run-test.sh"

Gdzie setup-ssh.shjest:

export SSH_CONFIG="${CONFIG_DIR}/scripts/.ssh-config"
mapfile -t SERVERS < "${CONFIG_DIR}/scripts/hosts"

for SERVER in "${SERVERS[@]}"
do
    while ! ssh -F "${SSH_CONFIG}" "${SERVER}" -fnN; do sleep 1; done
    scp -F "${SSH_CONFIG}" "${CONFIG_DIR}/scripts/ssh-script.sh" "${SERVER}":"${TEST_LABEL}.sh"
done

I .ssh-config:

Host test-*
  User test
  StrictHostKeyChecking no
  ControlMaster auto
  ControlPath /tmp/ssh-%h-%p-%r

I run-test.sh:

mapfile -t TEST_SERVERS < "${CONFIG_DIR}/scripts/hosts"
ssh -F "${SSH_CONFIG}" "${TEST_SERVERS[$NUM]}" "./${TEST_LABEL}.sh"

Sekwencja wygląda następująco:

  • Główne źródła skryptu (pokazane jako pierwsze) setup-ssh.sh.
  • setup-ssh.shbusy-loop zapełnia serwery, dopóki wszystkie nie skonfigurują gniazda kontrolnego. hostsPlik po prostu wymienia jedną hostów serwera w wierszu.
  • Ponieważ konfiguracja określająca gniazdo sterujące jest dostępna ${CONFIG_DIR}/scripts/.ssh-config, chyba że określę ten plik za pomocą -F, połączenia SSH nie będą go używać. To pozwala mi używać gniazda kontrolnego tylko tam, gdzie potrzebuję ich, używając Fopcji.
  • Skrypt instalacyjny kopiuje również skrypt wykonania testu na serwery. Sam skrypt wykonawczy zawiera wiele poleceń, a ponieważ skopiowałem skrypt wykonawczy, nie muszę się martwić o dodatkową warstwę cytowania dla SSH (i dodatkowe koszty poznawcze, aby dowiedzieć się, co zostanie rozwinięte, kiedy).
  • Następnie główny skrypt używa xargsdo rozłożenia obciążenia na serwery, rozpoczynając nowe zadania natychmiast po ich zakończeniu.
muru
źródło