Капстоун удалённых операций
Свяжи ssh, rsync и tmux в полный рабочий процесс удалённых операций: синхронизируй каталог с удалённым сервером, запусти долгую задачу в отдельной сессии tmux и переподключись к ней позже — пережив разрывы соединения без потери работы.
Есть задача обработки данных, занимающая четыре часа. Ты подключаешься по ssh, запускаешь её, и через сорок минут WiFi ноутбука падает. Задача исчезла. Или: копируешь датасет в 3 ГБ через scp, он падает на 2.8 ГБ, и начинаешь заново с нуля. Это две самые распространённые боли при удалённой работе — и у обеих есть чистые решения, уже установленные на каждом Linux-сервере. Этот урок связывает ssh, rsync и tmux в повторяемый процесс деплоя и запуска, переживающий разрывы, возобновляющий частичные передачи и позволяющий проверять долгие задачи без удержания открытого терминала.
После этого урока ты сможешь синхронизировать локальный каталог с удалённым сервером через rsync -avz --delete (предварительно с --dry-run), запускать долгую задачу в именованной сессии tmux чтобы она пережила разрыв SSH, переподключаться к этой сессии с любого терминала, и связывать весь рабочий процесс в скрипт деплоя с ~/.ssh/config для эргономичных псевдонимов хостов.
Настрой ~/.ssh/config для эргономичных псевдонимов хостов. Сырые команды ssh многословны и легко ошибиться при повторном вводе. Запись в конфиге даёт короткий псевдоним, фиксирует имя пользователя, файл ключа и порт один раз, и включает ProxyJump для паттернов с бастионом.
# ~/.ssh/config
Host prod
HostName server.example
User deploy
IdentityFile ~/.ssh/id_ed25519
ServerAliveInterval 60
ServerAliveCountMax 3
Host staging
HostName 192.0.2.100
User deploy
IdentityFile ~/.ssh/id_ed25519
ProxyJump bastion.exampleServerAliveInterval 60 / ServerAliveCountMax 3 отправляет keepalive каждые 60 секунд и сдаётся после 3 пропущенных ответов — предотвращает тихое зависание SSH-соединения при падении сетевого пути. ProxyJump (OpenSSH 7.3+) заменяет устаревший паттерн -o ProxyCommand для доступа через бастион.
После сохранения ssh prod подключается к deploy@server.example с правильным ключом — без флагов.
Синхронизируй файлы с rsync -avz --dry-run сначала, затем без него. rsync передаёт только изменившиеся байты (дельта-кодирование через скользящие контрольные суммы). Это делает повторный запуск синхронизации после частичной передачи быстрым — он возобновляется с того места, где остановился.
# Пробный запуск: посмотреть что ИЗМЕНИТСЯ, ничего не передаётся
rsync -avz --dry-run --delete ./dist/ prod:/var/app/current/
# Реальный запуск: передать разницу
rsync -avz --delete ./dist/ prod:/var/app/current/Флаги:
-a— режим архива: рекурсивно, сохранять права, временные метки, симлинки, владельца, группу.-v— подробно: выводить каждый переданный файл.-z— сжимать данные при передаче (полезно на медленных соединениях; пропускай для LAN или уже сжатых файлов вроде.gz).--delete— удалять файлы на удалённом хосте, которых нет локально. Это деструктивно. Всегда запускай--dry-runсначала.
Завершающий слеш в ./dist/ означает «скопировать содержимое dist», а не саму директорию. prod:/var/app/current/ — удалённый путь rsync, разрешённый через псевдоним ~/.ssh/config.
Запусти долгую задачу в именованной сессии tmux. tmux мультиплексирует терминал: сессия продолжает работать на сервере после отключения. Используй именованные сессии для надёжного переподключения.
# На удалённом: запустить именованную сессию
ssh prod tmux new-session -d -s deploy -x 220 -y 50
# Отправить команду в сессию (неинтерактивно)
ssh prod tmux send-keys -t deploy 'cd /var/app/current && ./run-job.sh 2>&1 | tee /var/log/job.log' Enter
# Подключиться для просмотра вывода в реальном времени (интерактивно)
ssh prod
tmux attach -t deploy-d запускает сессию в отключённом режиме (без привязанного терминала). -s deploy даёт ей имя. -x 220 -y 50 задаёт размер виртуального терминала во избежание проблем с макетом при подключении с терминалов разных размеров.
send-keys -t deploy '...' Enter вводит нажатия клавиш в сессию — это позволяет запускать задачу неинтерактивно одной командой с локальной машины.
Управляй сессиями tmux: список, подключение, завершение. Три команды покрывают 90% ежедневных операций tmux:
# Список всех сессий на удалённом
ssh prod tmux ls
# Подключиться к сессии deploy
ssh prod -t tmux attach -t deploy
# Завершить сессию когда закончено
ssh prod tmux kill-session -t deployssh -t принудительно выделяет псевдо-TTY — обязательно для интерактивных программ вроде tmux attach, которым нужен настоящий терминал. Без -t tmux attach завершается с ошибкой «not a terminal», потому что режим SSH по умолчанию не выделяет TTY для команд.
Если сессия умерла (задача завершилась или упала), tmux attach -t deploy вернёт «no sessions». Проверь лог: ssh prod tail -50 /var/log/job.log.
Обработай паттерн бастиона (jump host). Многие продакшн-окружения требуют SSH через бастион. С ProxyJump в ~/.ssh/config rsync и ssh оба прозрачно туннелируются через него — отдельные флаги не нужны.
# Это прозрачно работает через бастион, определённый в ~/.ssh/config
rsync -avz --delete ./dist/ staging:/var/app/current/
ssh staging tmux new-session -d -s deployДля разовых подключений без записи в конфиге используй -J:
ssh -J deploy@bastion.example deploy@192.0.2.100
rsync -avz -e 'ssh -J deploy@bastion.example' ./dist/ deploy@192.0.2.100:/var/app/Флаг -e для rsync переопределяет команду SSH. Полезно в CI-окружениях, где нельзя писать ~/.ssh/config.
Полный скрипт деплоя: синхронизация, запуск задачи, слежение за логом.
#!/usr/bin/env bash
set -euo pipefail
REMOTE="prod"
REMOTE_DIR="/var/app/current"
SESSION="deploy"
LOG="/var/log/app/job.log"
echo "==> пробный запуск (проверь перед передачей)"
rsync -avz --dry-run --delete ./dist/ "${REMOTE}:${REMOTE_DIR}/"
read -r -p "Продолжить реальную синхронизацию? [y/N] " answer
[[ "${answer,,}" != "y" ]] && { echo "отменено"; exit 0; }
echo "==> синхронизация"
rsync -avz --delete ./dist/ "${REMOTE}:${REMOTE_DIR}/"
echo "==> запуск задачи в tmux-сессии '${SESSION}'"
ssh "${REMOTE}" "tmux new-session -d -s ${SESSION} -x 220 -y 50 2>/dev/null || true"
ssh "${REMOTE}" "tmux send-keys -t ${SESSION} \
'cd ${REMOTE_DIR} && ./run-job.sh 2>&1 | tee ${LOG}' Enter"
echo "==> слежение за логом (Ctrl-C для отключения — задача продолжает работу)"
ssh "${REMOTE}" "tail -f ${LOG}"|| true после tmux new-session означает «создать сессию, если её нет; если уже есть — всё в порядке». Это делает скрипт идемпотентным — повторный запуск подключается к существующей сессии, а не завершается с ошибкой.
Для переподключения позже с любой машины: ssh prod -t tmux attach -t deploy.
▸Почему это работает
На macOS системные ssh и rsync — старые BSD-версии. Для поддержки ProxyJump убедись, что OpenSSH >= 7.3 (ssh -V). macOS поставляется с очень старым rsync (2.x); установи актуальную версию через Homebrew (brew install rsync) для --info=progress2 и других современных флагов. Поведение tmux идентично на macOS и Linux — установи через brew install tmux, если не установлен.
▸Частая ошибка
rsync --delete без предварительного --dry-run — распространённый источник потери данных. Если локальный dist/ пуст или неверен, --delete очистит удалённую директорию до совпадения. Всегда запускай пробный прогон, читай вывод, подтверждай. Вторая ошибка: забытый -t при запуске tmux attach по SSH. Без него SSH не выделяет псевдо-TTY и tmux не может занять терминал — получаешь «not a terminal» и ничего не подключается. Всегда ssh -t host tmux attach -t session.
Ты запускаешь: ssh prod tmux attach -t deploy и получаешь ошибку 'not a terminal'. Как исправить?
Рабочий процесс удалённых операций связывает три инструмента: ~/.ssh/config устраняет повторяющиеся флаги и включает прозрачный ProxyJump для хостов-бастионов; rsync -avz --delete синхронизирует только изменённые байты и поддерживает точное соответствие удалённого локальному — всегда перед этим с --dry-run; tmux new-session -d -s ИМЯ запускает отдельную сессию, переживающую SSH-соединение, а tmux attach -t ИМЯ (через ssh -t) переподключается с любого терминала в любое время. Флаг -t для ssh обязателен для интерактивных программ, которым нужен настоящий TTY. Вместе эти три инструмента позволяют задеплоить, запустить долгую задачу, отключиться и переподключиться для проверки прогресса — полный цикл удалённых операций без риска потери данных.
Практика
Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.
Что-то непонятно?
Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.