Кілька команд під час SSH всередині сеансу SSH


10

У мене локальна машина, яка повинна зробити сеанс SSH на віддаленій masterмашині, а потім ще один внутрішній сеанс SSH від masterкожного до якогось віддаленого slaves, а потім виконати 2 команди, тобто видалити певний каталог і відтворити його.

Зауважте, що локальна машина має безвізну SSH для ведучого, а майстер має безвільний SSH для рабів. Також всі імена хостів відомі в .ssh/configлокальних / головних машинах, а імена хостів невільників - slaves.txtлокально, і я читаю їх звідти.

Отже, те, що я роблю, і це працює:

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 

Цей кластер знаходиться на Amazon EC2, і я помітив, що на кожній ітерації створюється 6 сеансів SSH, що викликає значну затримку. Я хотів би об'єднати ці 3 команди в 1, щоб отримати менше з'єднань SSH. Тому я спробував поєднати перші 2 команди в

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

Але це не працює, як очікувалося. Здається, виконується перший ( rm -rf Input Output Partition), а потім виходить із сеансу і продовжується. Що я можу зробити?


3
Замість такої непрямості в команді ви можете використовувати -Jопцію, яка б визначала вашого хоста стрибка.
Hauleth

Відповіді:


15

Вважайте, що &&це логічний оператор. Це не означає "також запустити цю команду", це означає "запустити цю команду, якщо інша вдалася".

Це означає, що якщо rmкоманда не вдасться (що станеться, якщо будь-який із трьох каталогів не існує), тоді виконання mkdirне буде виконано. Це не схоже на поведінку, яку ви хочете; якщо каталоги не існують, їх, мабуть, чудово створити.

Використовуйте ;

Точка з комою ;використовується для розділення команд. Команди виконуються послідовно, чекаючи кожної, перш ніж продовжувати переходити до наступної, але їх успіх чи невдача не впливають одна на одну.

Уникнути внутрішніх цитат

Цитати всередині інших лапок слід уникати, інакше ви створюєте додаткову кінцеву точку та початкову точку. Ваша команда:

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

Стає:

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

Ваша поточна команда через відсутність уникнутих лапок повинна виконувати:

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

якщо це вдасться:

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

Ви помітите, що підсвічування синтаксису показує всю команду тут червоною, що означає, що вся команда - це рядок, що передається в ssh. Перевірте свою локальну машину; у вас можуть бути каталоги Input Outputі Partitionде ви це працювали.


Я розумію ваші моменти. Одна частина мене збентежила крапкою з комою, оскільки я думав, що вона виконає більше 1 команди одночасно, тому я її не використовував.
mgus

Точка з комою не призведе до виконання команд одночасно, див. Тут посилання на виконання команд. Команди &викликає запуск у фоновому режимі, це означає, що їх не чекатиме завершення, перш ніж перейти до наступного.
Centimane

10

Ви завжди можете визначити у своєму перемиканні мультиплексування у OpenSSH

Мультиплексування - це можливість надсилати більше одного сигналу по одній лінії або з'єднанню. За допомогою мультиплексування OpenSSH може повторно використовувати існуюче TCP-з'єднання для декількох одночасних сеансів SSH, а не створювати нове кожного разу.

Перевага SSH мультиплексування полягає в тому, що усуваються накладні витрати на створення нових TCP-з'єднань. Загальна кількість з'єднань, яку може прийняти машина, є кінцевим ресурсом, і обмеження помітніше на деяких машинах, ніж на інших, і сильно змінюється залежно від навантаження та використання. Існує також значна затримка при відкритті нового з'єднання. Діяльність, яка повторно відкриває нові з'єднання, може бути значно прискорена за допомогою мультиплексування.

Для цього виконайте /etc/ssh/ssh_config:

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

Таким чином, будь-які послідовні з'єднання, виконані з тим же сервером протягом наступних 30 хвилин, будуть здійснені повторно з використанням попереднього ssh-з'єднання.

Ви також можете визначити його для машини або групи машин. Взяте з наданого посилання.

Host machine1
    HostName machine1.example.org
    ControlPath ~/.ssh/controlmasters/%r@%h:%p
    ControlMaster auto
    ControlPersist 10m

Це цікаво! Чи є спосіб явно вмикати та вимикати певне з'єднання? Здається, що надмірне вбивство настільки різко змінило всі з'єднання SSH для цього випадку використання. Чи можна його використовувати більш точно? Щоб розпочати мультиплексування лише певного з'єднання?
Centimane

@Centimane так, оновив відповідь
Rui F Ribeiro

1
Я б запропонував поставити розетку в будинок користувача, а не у світовий режим для читання /tmp/.
heemayl

@heemayl Добре. Я відредагую його в комп’ютері.
Rui F Ribeiro

@RuiFRibeiro також виглядає, по man ssh, ControlPath, ControlMasterі ControlPersistє дійсними варіанти , щоб передати sshкоманду , використовуючи -o. Це може бути ще більш точним випадком використання: налаштувати мультиплексування в першому sshсценарії та переробити його для інших, але в іншому випадку уникнути покарання за продуктивність. Цікаво, який орієнтир мультиплексування VS не для 3-х з'єднань SSH, враховуючи, що "Є також значна затримка при відкритті нового з'єднання"
Centimane

4

Ви можете помістити всі свої команди в окремий скрипт на своєму "майстер" сервері.

Головний сценарій

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

Тоді у своєму скрипті ssh називайте це так: SSH Script

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 

АБО якщо всі файли мають бути на початковій машині, ви можете зробити щось подібне:

сценарій1

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

сценарій2

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

скрипт ssh

script1="/path/to/script1"
username="ubuntu"
masterHostname="myMaster"
cat $script1 | ssh -n $username@$masterHostname

1

Деякий час тому я мав нагоду використовувати контрольні сокети, як рекомендують інші відповіді (ця відповідь, по суті, є комбінацією використання керуючих розеток, таких як ця відповідь, та сценаріїв, як ця відповідь ).

Випадок використання був злом: authorized_keysцільового користувача періодично переписували плановим завданням, і я хотів швидко перевірити речі, не проходячи червону стрічку, необхідну для додання чогось до цього файлу. Тому я налаштував цикл, який додав ключ до цього файлу, якщо потрібно, запустив тест і скасував цикл. Однак було б невелике вікно, де заплановане завдання перезаписало б файл, і мій цикл все ще буде sleeping. Отже, встановлення керуючого сокета на початку дозволить пізніше моєму сценарію SSH без проблем:

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

Де setup-ssh.sh:

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

І .ssh-config:

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

І run-test.sh:

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

Послідовність йде так:

  • Основні сценарії (показані перші) джерела setup-ssh.sh.
  • setup-ssh.shзайнято петлями серверів, поки всі вони не встановлять керуючий сокет. hostsФайл просто перераховує сервер імен хостів по одному в кожному рядку.
  • Оскільки конфігурація із зазначенням керуючого сокета знаходиться лише у ${CONFIG_DIR}/scripts/.ssh-config, якщо я не вказав цей файл за допомогою -F, з'єднання SSH не використовуватимуть його. Тож це дозволяє мені використовувати розетку управління лише там, де мені потрібні, використовуючи Fпараметр.
  • Сценарій настройки також копіює тестовий сценарій виконання на сервери. Сам сценарій виконання містить купу команд, і оскільки я скопіював сценарій виконання, мені не доведеться турбуватися про додатковий рівень цитування SSH (та додатковий когнітивний наклад, щоб з'ясувати, що розширюється, коли).
  • Тоді основний сценарій використовує xargsдля розподілу навантаження на сервери, запускаючи нові завдання, як тільки закінчуються запущені.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.