Learning Platform
Глоссарий Troubleshooting
Урок 13.05 · 22 мин
Средний
DE workflowshealth checkdata syncSSH tunnelsshfsautomation

Сетевые инструменты в DE-workflow

Урок-сборник. Берём весь арсенал из предыдущих уроков (curl, wget, ssh, rsync, ss, nc, dig) и применяем его к типичным задачам DE-инженера. Готовые рецепты, которые можно копировать в работу.


Сценарий 1: Проверка работоспособности всего стека

DE-команда хочет каждое утро видеть «зелёный/красный» status каждого сервиса. Это первый bash-скрипт, который пишет Junior DE.

#!/bin/bash
# health-check.sh — утренний health check продакшен-стека

set -uo pipefail

# Сервисы для проверки
declare -A SERVICES=(
  ["Airflow UI"]="https://airflow.company.com/health"
  ["Airflow API"]="https://airflow.company.com/api/v1/health"
  ["Postgres metadata"]="pg.internal.company.com:5432"
  ["S3 (data lake)"]="data-lake.s3.amazonaws.com:443"
  ["Kafka brokers"]="kafka-1.internal.company.com:9092"
  ["Grafana"]="https://grafana.company.com/api/health"
)

check_http() {
  local url="$1"
  local code
  code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 "$url")
  [ "$code" = "200" ]
}

check_tcp() {
  local host_port="$1"
  local host="${host_port%:*}"
  local port="${host_port##*:}"
  nc -zv -w 5 "$host" "$port" 2>/dev/null
}

echo "Health check at $(date -Iseconds)"
echo "----------------------------------"

FAIL_COUNT=0
for name in "${!SERVICES[@]}"; do
  target="${SERVICES[$name]}"
  if [[ "$target" == http* ]]; then
    check_http "$target" && echo "OK  : $name" || { echo "FAIL: $name ($target)"; ((FAIL_COUNT++)); }
  else
    check_tcp "$target" && echo "OK  : $name" || { echo "FAIL: $name ($target)"; ((FAIL_COUNT++)); }
  fi
done

echo "----------------------------------"
echo "Total failures: $FAIL_COUNT"

# Notify в Slack при ошибках
if [ "$FAIL_COUNT" -gt 0 ]; then
  curl -s -X POST -H 'Content-type: application/json' \
    --data "{\"text\":\":fire: Health check $FAIL_COUNT failures, see logs.\"}" \
    "$SLACK_WEBHOOK_URL"
fi

exit $FAIL_COUNT

Cron каждое утро в 9:00:

0 9 * * * /opt/scripts/health-check.sh > /var/log/health-$(date +\%Y\%m\%d).log 2>&1
Диагностика сети — ping, traceroute, netstat, lsof

Сценарий 2: Daily data download с rsync resume

Партнёр-компания раз в день кладёт CSV-дампы на FTP. Сеть прерывистая, файлы большие (5-50GB). Нужно качать reliable.

#!/bin/bash
# fetch-partner-data.sh — ежедневный sync data dumps

set -euo pipefail

DATE=$(date +%Y-%m-%d)
SRC="[email protected]:/exports/$DATE/"
DST="/data/imports/partner/$DATE/"
LOG="/var/log/partner-import-$DATE.log"

mkdir -p "$DST"

# rsync с resume и retry
for attempt in 1 2 3; do
  echo "Attempt $attempt at $(date -Iseconds)" >> "$LOG"

  if rsync -avhP --partial \
    --timeout=120 \
    "$SRC" "$DST" >> "$LOG" 2>&1; then
    echo "Success" >> "$LOG"
    break
  fi

  echo "Failed, retry in 60s" >> "$LOG"
  sleep 60
done

# Проверить, что файлы реально скачались
EXPECTED=$(curl -s "https://api.partner.com/exports/$DATE/manifest.json" | jq '.file_count')
ACTUAL=$(ls "$DST" | wc -l)

if [ "$ACTUAL" -ne "$EXPECTED" ]; then
  echo "WARNING: expected $EXPECTED files, got $ACTUAL" | tee -a "$LOG"
  exit 1
fi

# Уведомить о успехе
curl -X POST -H 'Content-type: application/json' \
  --data "{\"text\":\":white_check_mark: Partner data $DATE imported: $ACTUAL files\"}" \
  "$SLACK_WEBHOOK_URL"

Ключевые техники:

  • rsync -avhP --partial — resume прерванных передач.
  • --timeout=120 — таймаут per file.
  • Цикл retry — если первая попытка fail, пробуем ещё.
  • Проверка count через manifest API партнёра.
  • Notify в Slack об успехе.

Сценарий 3: Бэкап data lake на NAS с rotation

Daily incremental backup с использованием --link-dest для эффективности места (hardlinks).

#!/bin/bash
# backup-lake.sh — nightly incremental backup

set -euo pipefail

DATE=$(date +%Y-%m-%d)
SRC="/data/lake/"
BACKUP_HOST="[email protected]"
BACKUP_BASE="/backups/lake"
DST="$BACKUP_BASE/$DATE"
LATEST="$BACKUP_BASE/latest"

# Incremental backup: hardlink файлов которые не изменились
rsync -avh -P \
  --link-dest="$LATEST" \
  --exclude='*.tmp' \
  --exclude='_temp/' \
  --exclude='__staging__/' \
  "$SRC" "$BACKUP_HOST:$DST/"

# Обновить symlink latest
ssh "$BACKUP_HOST" "ln -sfn '$DST' '$LATEST'"

# Rotation: оставить последние 30 дней + по 1 на каждый месяц для последних 12 месяцев
ssh "$BACKUP_HOST" "find $BACKUP_BASE -maxdepth 1 -mtime +30 -type d -name '????-??-??' | head -n -12 | xargs rm -rf"

echo "Backup $DATE complete: $(du -sh $DST | awk '{print $1}') on remote"

--link-dest — каждый день вы видите полную копию, но на диске занимает только размер изменений. 30 daily backups при изменениях 1GB/день — занимают ~30GB вместо 30 × full size.


Сценарий 4: SSH tunnel для доступа к UI

Airflow UI на production — не выставлен в интернет. Нужно зайти из дому.

# В ~/.ssh/config
Host airflow-tunnel
    HostName airflow.internal.company.com
    User ec2-user
    IdentityFile ~/.ssh/work_key
    ProxyJump bastion.company.com
    LocalForward 8080 localhost:8080
    LocalForward 5432 localhost:5432
    ServerAliveInterval 60
    ExitOnForwardFailure yes

# Подключение
ssh airflow-tunnel
# Появится shell на airflow-сервере
# В браузере: http://localhost:8080 — Airflow UI
# psql -h localhost -p 5432 — Postgres metadata

Чтобы tunnel жил без интерактивного shell — -N:

# В фоне, без shell
ssh -N -f airflow-tunnel
# -N = no remote command, -f = fork в background

# Killнуть когда не нужно
pkill -f "ssh -N -f airflow-tunnel"

В системных скриптах — лучше через autossh, который автоматически переподключается:

# Установить autossh
sudo apt install autossh

# Tunnel который сам переподключается
autossh -M 0 -N -f airflow-tunnel

Сценарий 5: Скачать data dump из internal API

#!/bin/bash
# download-dump.sh — скачать вчерашний дамп

set -euo pipefail

API_BASE="https://api.internal.company.com"
TOKEN="${API_TOKEN:?Set API_TOKEN env var}"
YESTERDAY=$(date -d yesterday +%Y-%m-%d)

# 1. Получить presigned URL дампа
PRESIGNED=$(curl -s -X POST \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"date\":\"$YESTERDAY\"}" \
  "$API_BASE/exports/dump/presigned" \
  | jq -r '.url')

if [ -z "$PRESIGNED" ] || [ "$PRESIGNED" = "null" ]; then
  echo "Failed to get presigned URL" >&2
  exit 1
fi

# 2. Скачать через wget с retry
wget --tries=3 --timeout=300 \
  -O "/data/dumps/$YESTERDAY.tar.zst" \
  "$PRESIGNED"

# 3. Проверить checksum
EXPECTED=$(curl -s -H "Authorization: Bearer $TOKEN" \
  "$API_BASE/exports/dump/$YESTERDAY/checksum" \
  | jq -r '.sha256')

ACTUAL=$(sha256sum "/data/dumps/$YESTERDAY.tar.zst" | awk '{print $1}')

if [ "$ACTUAL" != "$EXPECTED" ]; then
  echo "Checksum mismatch! Expected $EXPECTED, got $ACTUAL" >&2
  rm -f "/data/dumps/$YESTERDAY.tar.zst"
  exit 1
fi

echo "Downloaded and verified: $YESTERDAY.tar.zst"

Сценарий 6: Deploy code на сервер через rsync + SSH

#!/bin/bash
# deploy.sh — деплой ETL-проекта

set -euo pipefail

LOCAL="$(pwd)/"
REMOTE="airflow:/opt/etl/"

# Preview
echo "=== Preview ==="
rsync -avhn --delete \
  --exclude='.git' \
  --exclude='__pycache__' \
  --exclude='.env' \
  --exclude='*.pyc' \
  --exclude='node_modules' \
  --exclude='.venv' \
  "$LOCAL" "$REMOTE"

read -p "Continue? (y/n) " yn
[ "$yn" != "y" ] && exit 1

# Real
echo "=== Deploying ==="
rsync -avh --delete \
  --exclude='.git' \
  --exclude='__pycache__' \
  --exclude='.env' \
  --exclude='*.pyc' \
  --exclude='node_modules' \
  --exclude='.venv' \
  "$LOCAL" "$REMOTE"

# Restart scheduler
echo "=== Restarting scheduler ==="
ssh airflow "sudo systemctl restart airflow-scheduler"
ssh airflow "sudo systemctl status airflow-scheduler --no-pager"

Сценарий 7: sshfs для удалённой работы с файлами

Иногда удобно работать с remote директорией как локальной — открывать в VS Code, копировать через Finder.

# Установить
sudo apt install sshfs   # Ubuntu/Debian
brew install --cask macfuse && brew install gromgit/fuse/sshfs-mac   # macOS

# Смонтировать
mkdir -p ~/mounts/airflow
sshfs airflow:/home/ec2-user/etl ~/mounts/airflow

# Работать как с локальной
cd ~/mounts/airflow
ls
vim dag.py    # сохранится прямо на сервер
code .        # открыть в VS Code

# Размонтировать
fusermount -u ~/mounts/airflow         # Linux
umount ~/mounts/airflow                # macOS

Минусы sshfs:

  • Медленнее нативной FS (особенно ls больших директорий).
  • Может «зависнуть» при потере сети.
  • Не для тяжёлой работы (build, индексирование).

Для production-разработки лучше: ssh + tmux + vim/neovim, или VS Code Remote SSH (нативная интеграция).


Сценарий 8: Проверка cron + SSH работают

#!/bin/bash
# self-test.sh — проверить что наша автоматизация жива

set -euo pipefail

# 1. SSH access к ключевым серверам
HOSTS=("airflow" "postgres-prod" "spark-master")
for h in "${HOSTS[@]}"; do
  if ssh -o ConnectTimeout=5 -o BatchMode=yes "$h" "echo ok" > /dev/null 2>&1; then
    echo "SSH $h: OK"
  else
    echo "SSH $h: FAIL"
  fi
done

# 2. Cron сам себя запускает
CRON_HEARTBEAT_FILE="/var/run/cron-heartbeat"
# (этот файл должен обновляться каждые 5 минут другим cron-jobом)
if [ -f "$CRON_HEARTBEAT_FILE" ]; then
  AGE=$(( $(date +%s) - $(stat -c %Y "$CRON_HEARTBEAT_FILE") ))
  if [ "$AGE" -gt 600 ]; then
    echo "Cron heartbeat stale: ${AGE}s old"
  else
    echo "Cron heartbeat OK"
  fi
else
  echo "Cron heartbeat file missing!"
fi

-o BatchMode=yes — не спрашивать пароль интерактивно (если ключ не подходит — сразу fail).


Цепочка инструментов для типичных задач

Сетевые задачи DE и инструменты

Какую задачу каким инструментом

REST API callPOST/GET/DELETE для интеграций
Скачать файлС известного URL, простой случай
Bulk downloadМного файлов, recursive
Sync директорииЛокально или remote, с resume
Backup с истор.Incremental, hardlinks
Deploy кодаС delete и exclude
SSH доступК серверу для shell или команд
Tunnel к UIК Airflow / Grafana UI
Через bastionК internal серверам
Проверка connectivityОткрыт ли TCP порт
DNS вопросРезолвится ли имя
Что слушает на портуСписок listening sockets

Попробуй сам

# 1. Простой health-check для пары публичных сайтов
for url in https://github.com https://google.com https://nonexistent12345.com; do
  CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 "$url" 2>&1)
  echo "$url: $CODE"
done

# 2. Rsync local backup (с --link-dest для incremental)
mkdir -p /tmp/src /tmp/backup/latest
echo "data v1" > /tmp/src/file1.txt
echo "data v1" > /tmp/src/file2.txt

# День 1
rsync -avhP /tmp/src/ /tmp/backup/$(date +%F)/
ln -sfn $(date +%F) /tmp/backup/latest

# Изменить файл
echo "data v2" > /tmp/src/file1.txt

# День 2 (через минуту) — будет hardlink на file2.txt (не изменился)
sleep 60
NEW_DATE="$(date +%F)-2"
rsync -avhP --link-dest=/tmp/backup/latest /tmp/src/ /tmp/backup/$NEW_DATE/

# Проверить — file2 имеет одинаковый inode в двух бэкапах
ls -li /tmp/backup/*/file2.txt
# Будет один и тот же inode — hardlink

# 3. SSH multi-host check
hosts=(localhost)  # или твои реальные сервера
for h in "${hosts[@]}"; do
  ssh -o ConnectTimeout=2 -o BatchMode=yes "$h" "uptime" 2>&1 | head -1
done

# 4. Combined: download + checksum
URL="https://example.com/data.csv"
curl -sLO "$URL"
sha256sum data.csv

# 5. Скачать кучу файлов параллельно
URLS=("https://example.com/2026-04.csv"
      "https://example.com/2026-05.csv"
      "https://example.com/2026-06.csv")

for url in "${URLS[@]}"; do
  wget -q "$url" &
done
wait
echo "All done"

Cross-link: предыдущий урок 04 — debugging. Модуль 12 — архивирование, часто комбинируется с rsync для бэкапов. Модуль 15 — cron для планирования этих скриптов. Модуль 17 — production-bash для надёжных скриптов с error handling.


Проверка знанийKnowledge check
Senior DE говорит: «Каждую ночь нужно: 1) скачать вчерашний dump с partner API (5-20GB, иногда сеть падает), 2) сделать backup нашего data lake на NAS (только изменения), 3) уведомить Slack об успехе/неудаче.» Опиши high-level workflow используя curl, rsync, jq.
ОтветAnswer
High-level workflow ночного скрипта: 1) Скачать partner dump через curl + retry: получить presigned URL через curl -X POST + jq -r '.url' для парсинга JSON; скачать через wget --tries=3 --timeout=300 -c (с resume) или curl -L --retry 3; проверить sha256sum против expected checksum из API; если несовпадает — удалить файл, exit с ошибкой. 2) Backup data lake через rsync с incremental: rsync -avhP --link-dest=/backups/lake/latest --exclude='*.tmp' /data/lake/ backup@nas:/backups/lake/$(date +%F)/; обновить symlink: ssh nas "ln -sfn $(date +%F) /backups/lake/latest". --link-dest даёт incremental backup — неизменённые файлы hardlink на предыдущий бэкап, экономия места. 3) Slack notify: при успехе и неудаче curl -X POST -H 'Content-type: application/json' --data "{\"text\":\"...\"}" $SLACK_WEBHOOK. 4) Скрипт обернуть в set -euo pipefail (модуль 18) для надёжного error handling — любая ошибка падает с ненулевым exit, не молча проглатывается. 5) Запуск через cron (модуль 16): 0 2 * * * /opt/scripts/nightly.sh > /var/log/nightly-$(date +\%F).log 2>&1. Дополнительно — trap для cleanup временных файлов. Это типичный nightly ETL pipeline, который Junior DE напишет на 2-3 месяце работы.

Проверьте понимание

Результат: 0 из 0
Прикладной
Вопрос 1 из 5. Хочешь скачать вчерашний CSV дамп с partner API. Скрипт должен быть надёжным к разрывам сети (5-20GB файл). Какая последовательность инструментов?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 5