У тебя в углу уже давно стоит старый списанный сервер? Горячий как чайник, медленный как улитка и с памятью, как у рыбки. Но это не повод вешать на него табличку «выведен из эксплуатации». Это повод превратить его в ночного "качка", который тихо, аккуратно и с минимальным стрессом для железа будет качать браузеры, ISO-образы Debian/Ubuntu и всё, что может понадобиться утром человеку, у которого есть руки и желание поставить ОС.
Идея в двух словах (и одной кружке чая)
Ночной план:
- Включаем загрузчик в 2–4 утра, когда сеть свободна и соседи не ругаются.
- Запускаем скрипт, который по расписанию качает нужные файлы (wget/aria2c).
- Ограничиваем I/O и CPU (ionice/nice, лимиты), чтобы сервер не умер от стресса.
- Проверяем контрольные суммы, записываем лог и удаляем старьё через 30 дней (для серверов у которых жим-жим с местом).
- Утром — свежие ISO и браузеры у тебя уже на районе.
Что нужно на сервере (минимум)
- Linux (подойдёт любой лёгкий дистрибутив; примеры команд для Debian/Ubuntu ниже).
- Достаточно свободного диска (средний размер 1 файла ISO ≈ 700 МБ–4 ГБ).
- Утилиты: wget, aria2 (или только wget), screen/tmux (опционально), mailx (оповещения).
- Права root для установки и cron/systemd.
Установка (Debian/Ubuntu):
sudo apt update
sudo apt install -y wget aria2 coreutils mailutils
Как экономно использовать ресурсы (важно для дохлика)
- nice: уменьшает приоритет CPU.
- ionice -c3: — background best effort с наименьшим приоритетом.
- --limit-rate (wget) или --max-overall-download-limit (aria2) — ограничь пропускную способность.
- --max-concurrent-downloads / --max-connection-per-server в aria2 — поставь малые значения (1–2).
- Используй rsync для синхронизации только новых/обновлённых файлов (для зеркал).
- Запускай ночью через cron или systemd-таймер.
Пример запуска с низким приоритетом:
nice -n 19 ionice -c3 wget -c --limit-rate=500k "https://example.com/file.iso"
Здесь 500k — максимум 500 KB/s, чтобы не забирать весь канал.
Структура каталогов (как пример)
/srv/nightfetch/
├─ downloads/ # сюда качаем
├─ logs/ # лог выполнения
├─ tmp/ # временные файлы
└─ hooks/ # скрипты postprocess (опц.)
Создать:
sudo mkdir -p /srv/nightfetch/{downloads,logs,tmp,hooks}
sudo chown -R $(whoami):$(whoami) /srv/nightfetch
chmod +x /srv/nightfetch/nightfetch-debian.sh
Сам скрипт nightfetch-debian.sh
#!/usr/bin/env bash
set -euo pipefail
# CONFIG
BASE="/srv/nightfetch"
DL="$BASE/downloads"
LOG="$BASE/logs/nightfetch-$(date +%F).log"
TMP="$BASE/tmp"
LOCKDIR="/var/lock/nightfetch-debian.lock"
MAX_AGE_DAYS=30
CDIMAGE_URL="https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/"
# wget options: докачка, терпимость к сети, ограничение скорости
WGET_OPTS="--continue --tries=5 --waitretry=10 --timeout=30 --limit-rate=500k --no-verbose"
# Logging to file (append)
mkdir -p "$BASE/logs" "$BASE/tmp" "$BASE/downloads"
exec >>"$LOG" 2>&1
echo "[$(date +'%F %T')] START"
# простая блокировка (директория)
if ! mkdir "$LOCKDIR" 2>/dev/null; then
echo "Another run is active, exiting."
exit 0
fi
trap 'rmdir "$LOCKDIR"; echo "Lock removed";' EXIT
cd "$TMP"
# 1) Попытка скачать SHA256SUMS прямо (несколько вариантов)
sha_candidates=( "SHA256SUMS" "SHA256SUMS.asc" "SHA256SUMS.sign" "SHA256SUMS.txt" )
fetched_sha=""
for name in "${sha_candidates[@]}"; do
url="${CDIMAGE_URL}${name}"
echo "Trying to fetch $url"
if wget $WGET_OPTS -O "$TMP/$name" "$url" 2>/dev/null; then
# проверим, есть ли в файле ссылки на .iso (чтобы не принять HTML-страницу)
if grep -q '\.iso' "$TMP/$name"; then
fetched_sha="$TMP/$name"
echo "Fetched checksum file: $name"
break
else
echo "Downloaded $name but it doesn't contain .iso entries — removing"
rm -f "$TMP/$name"
fi
else
echo "No $name at $url"
fi
done
# 2) fallback: если SHA-файла нет — спарсить index.html и собрать список .iso
iso_list=""
if [ -z "$fetched_sha" ]; then
echo "No SHA256SUMS found — falling back to scraping index HTML"
wget $WGET_OPTS -O "$TMP/index.html" "$CDIMAGE_URL" || true
if grep -q '\.iso' "$TMP/index.html"; then
# вытащим href-ы и оставим только имена файлов
iso_list=$(grep -oP 'href="[^"]+\.iso"' "$TMP/index.html" \
| sed -E 's/href="([^"]+)"/\1/' \
| sed 's|.*/||' \
| sort -u)
if [ -z "$iso_list" ]; then
echo "No .iso links parsed from index.html, aborting."
exit 1
fi
echo "Parsed $(echo "$iso_list" | wc -l) iso entries from index.html"
else
echo "Index HTML doesn't contain .iso links — aborting."
exit 1
fi
fi
# 3) Если у нас есть SHA-файл — парсим имена iso
if [ -n "$fetched_sha" ]; then
# Преобразуем возможные форматы (удалим ведущие * или ./)
iso_list=$(awk '/\.iso/ {print $2}' "$fetched_sha" \
| sed 's/^[*]//' | sed 's|^\./||' | sort -u)
echo "Found $(echo "$iso_list" | wc -w) iso entries in $fetched_sha"
fi
# 4) Скачиваем: только отсутствующие или с неверной sha
cd "$DL"
for iso in $iso_list; do
[ -z "$iso" ] && continue
# если существует — проверим sha (только если есть файл SHA в tmp)
need_download=1
if [ -f "$DL/$iso" ] && [ -n "$fetched_sha" ]; then
expected=$(awk -v fn="$iso" '$2==fn {print $1}' "$fetched_sha" || true)
if [ -n "$expected" ]; then
actual=$(sha256sum "$DL/$iso" 2>/dev/null | awk '{print $1}' || true)
if [ "$expected" = "$actual" ]; then
echo "$iso already present and checksum OK, skipping"
need_download=0
else
echo "$iso exists but checksum mismatch (will re-download)"
mv "$DL/$iso" "$DL/$iso.part.$(date +%s)" || rm -f "$DL/$iso"
need_download=1
fi
else
echo "No checksum entry for $iso in fetched SHA file (will re-download to be safe)"
need_download=1
fi
elif [ -f "$DL/$iso" ]; then
echo "$iso already exists (no checksum available) — skipping"
need_download=0
fi
if [ "$need_download" -eq 1 ]; then
url="${CDIMAGE_URL}${iso}"
echo "Downloading $iso from $url"
# низкий приоритет CPU/I/O
nice -n 19 ionice -c3 wget $WGET_OPTS -O "$DL/$iso" "$url" || echo "wget failed for $iso" >&2
fi
done
# 5) Проверка контрольных сумм (если мы скачали SHA-файл) — запустим в каталоге download
if [ -n "$fetched_sha" ]; then
cp -f "$fetched_sha" "$DL/SHA256SUMS"
echo "Verifying checksums:"
sha256sum -c "$DL/SHA256SUMS" 2>&1 | tee -a "$LOG" || echo "Some checks failed or files missing (see output above)."
fi
# 6) Ротация (удаляем старые файлы) — опционально
if [ "$MAX_AGE_DAYS" -gt 0 ]; then
echo "Rotating files older than ${MAX_AGE_DAYS} days..."
find "$DL" -type f -mtime +"$MAX_AGE_DAYS" -print -delete || true
fi
echo "[$(date +'%F %T')] END"
Пара заметок / советы
- Если тебе нужно всё архивное (весь archive/), лучше не парсить HTML — используй rsync://cdimage.debian.org/cdimage/archive/ с rsync. Это быстрее и надёжнее для больших зеркал.
- Если у тебя мало диска и ты хочешь все старые и новые ISO — учитывай, что это может быть сотни гигабайт. Установи MAX_AGE_DAYS=0 чтобы не удалять (или убери блок ротации).
- Подкорректируй скрипт и качай другие образы Linux (какие нужны) или любой другой софт, который часто обновляется и экономь свое время.
- Ограничения nice и ionice — чтобы не портить жизнь серверу.
- --limit-rate=500k можно уменьшать для очень медленных машин.
Планирование: cron vs systemd-timer
Cron (очень просто). Добавь в crontab -e:
0 3 * * * /srv/nightfetch/nightfetch-debian.sh
Это запустит в 03:00 каждую ночь.
Systemd-timer (чуть аккуратнее). Если хочешь логирование в journal и единичные таймеры — лучше systemd. Но cron проще для дохлика — ставь cron.
Как качать браузеры и другие файлы
- Для браузеров часто достаточно
wget -c URL или aria2c -x1 URL
- Если сайт даёт API/JSON с релизами (GitHub, Mozilla), можно написать небольшой curl/jq-скрипт, который парсит последнюю версию и добавляет ссылку в очередь загрузки.
- Для нескольких файлов используй
aria2c --input-file=urls.txt --max-concurrent-downloads=2 --max-connection-per-server=1
Пример urls.txt:
https://ftp.mozilla.org/pub/firefox/releases/143.0/linux-x86_64/en-US/firefox-143.0.deb
https://releases.chromium.org/...
И запуск:
nice -n 19 ionice -c3 aria2c --max-concurrent-downloads=2 --max-connection-per-server=1 --input-file=/srv/nightfetch/urls.txt --dir=/srv/nightfetch/downloads
Мониторинг и уведомления (минимум)
- Пиши лог в /srv/nightfetch/logs. Настрой logrotate для них.
- Для простого оповещения при ошибке можно в конце скрипта отправлять письмо:
if grep -i "failed" "$LOG"; then
mail -s "nightfetch: errors on $(hostname) $(date +%F)" you@example.com < "$LOG"
fi
- Или отправляй простое уведомление в Telegram через webhook (хотя нафиг он тебе нужен в 3 утра).
Полезные советы и мелкие фишки
- Если диск маленький — монтируй внешний NAS/USB и качай туда напрямую.
- Для контроля версий: сохраняй список скачанных URL и контрольные суммы в отдельном файле (audit).
- Используй flock или mkdir /var/lock/... чтобы не запустить скрипт дважды.
- Если сеть нестабильна — wget --tries=0 --waitretry=10 (бесконечные повторы с паузами).
- Для зеркал лучше rsync (он скачивает только изменения).
- Не качай всё сразу — дроби очереди, чтобы не перегружать диск.
Тонкая настройка под «старый и очень слабый» сервер
- Ограничь количество одновременных загрузок до 1.
- Ограничь пропускную способность до 100–300 KB/s (если интернет полная лажа).
- Уменьши время ожидания и увеличь паузу между попытками (--wait=3).
- Запускай в период, когда система обычно простаивает — 2–4 утра.
- Проверь температуру корпуса: если сервер греется — раздели задания на несколько ночей.
Твой старый сервер — это не музейный экспонат и не обогреватель для сторожа. Это ночной робот, который тихо собирает ISO, браузеры и прочие штуки, чтобы утром ты мог спокойно ставить систему или отдавать коллегам ссылки. Немного nice, пара ionice, умная ротация и проверка SHA256 — и даже самый древний железяка будет полезен.
А как Вы используете списанное железо? Напишите в комментариях.