Когда лабораторное окружение готово, пора автоматизировать реальные процедуры обслуживания. Часть 3 серии проведёт тебя через проектирование bash-скрипта, который бэкапит конфигурационные данные, очищает устаревшие артефакты и записывает каждое действие в лог с меткой времени. К концу этого урока ты поймёшь, как условия, циклы и аргументы работают вместе, чтобы поддерживать здоровье Linux-серверов.
Цели обучения
Собирать bash-скрипт, который проверяет предварительные условия, создаёт сжатые бэкапы и корректно обрабатывает ошибки.
Использовать переиспользуемые хелперы логирования, чтобы каждый шаг выдавал структурированные сообщения для журналов аудита.
Перебирать коллекции с помощью циклов, чтобы находить и удалять устаревшие файлы, не затрагивая свежие данные.
Параметризовать скрипт позиционными аргументами, чтобы адаптировать его под разные окружения.
Осмыслять стратегии работы с путями, портируемость и меры безопасности, используемые в автоматизации обслуживания.
Сценарий: автоматизация превентивного обслуживания в Acme
Security-команда в Acme недавно запустила программу превентивного мониторинга. Вместо ручного запуска разовых команд они хотят надёжный скрипт, который operations-команда сможет планировать через cron. Скрипт должен:
Подготовить или переиспользовать директорию для бэкапов.
Архивировать /etc (или другую цель) с именем файла, содержащим метку времени.
Удалять файлы .bak старше 30 дней.
Логировать прогресс и ошибки для последующего просмотра.
Тебе поручено подготовить первую версию, проверить её поведение и задокументировать, почему каждое проектное решение поддерживает безопасность и портируемость.
Разбор скрипта — mantenimiento.sh
Реализация ниже следует требованиям и добавляет защитные механизмы, которые мы изучили в предыдущих частях серии.
#!/usr/bin/env bash# Automate maintenance/auditing workflow with validations and logging.# Args (optional):# 1) SOURCE_DIR - directory to back up (default: /etc)# 2) DEST_FOLDER - destination folder name (default: backups)# ===== Defaults & Args =====SOURCE_DIR="${1:-/etc}"DEST_FOLDER="${2:-backups}"# Validate source directory before doing anything elseif[[!-d"$SOURCE_DIR"]];thenprintf'Error: source directory does not exist: %s\n'"$SOURCE_DIR">&2exit1fi# Resolve an absolute path to avoid surprises with relative inputif!SOURCE_ABS=$(cd"$SOURCE_DIR"2>/dev/null &&pwd-P);thenprintf'Error: unable to access source directory: %s\n'"$SOURCE_DIR">&2exit1fi# Date stampsSTAMP="$(date'+%Y%m%d_%H%M%S')"# PathsSCRIPT_DIR="$(pwd)"DEST_PATH="${SCRIPT_DIR}/${DEST_FOLDER}"if!mkdir-p"$DEST_PATH";thenprintf'Error: failed to create destination folder: %s\n'"$DEST_PATH">&2exit1fiLOG_FILE="${DEST_PATH}/mantenimiento_${STAMP}.log"if!touch"$LOG_FILE"2>/dev/null;thenprintf'Error: cannot write log file: %s\n'"$LOG_FILE">&2exit1fitimestamp(){date'+%Y-%m-%d %H:%M:%S';}log_line(){locallevel="$1"shiftlocaltarget="$1"shiftlocalmessage="$*"local formatted
formatted=$(printf'[%s] %s | %s\n'"$level""$(timestamp)""$message")if[["$target"=="stderr"]];thenprintf'%s'"$formatted"|tee-a"$LOG_FILE">&2elseprintf'%s'"$formatted"|tee-a"$LOG_FILE"fi}log_info(){ log_line "INFO""stdout""$@";}log_warn(){ log_line "WARN""stderr""$@";}log_error(){ log_line "ERROR""stderr""$@";}log_info "=== Maintenance run started ==="log_info "Source: ${SOURCE_ABS}"log_info "Destination: ${DEST_PATH}"# ===== A) Ensure backup folder exists =====log_info "Ensuring backup folder exists: ${DEST_PATH}"if[[-d"$DEST_PATH"]];then log_info "Backup folder ready."else log_error "Backup folder is not available after creation attempt."exit1fi# ===== B) Compressed backup of SOURCE_DIR with date in name =====SOURCE_LABEL="$(basename"$SOURCE_ABS")"if[[-z"$SOURCE_LABEL"||"$SOURCE_LABEL"=="/"]];thenSOURCE_LABEL="root"fiBACKUP_FILE="${DEST_PATH}/${SOURCE_LABEL}_backup_${STAMP}.tar.gz"log_info "Creating compressed backup: ${BACKUP_FILE}"if[["$SOURCE_ABS"=="/"]];thenTAR_BASE="/"TAR_TARGET="."elseTAR_BASE="$(dirname"$SOURCE_ABS")"TAR_TARGET="$(basename"$SOURCE_ABS")"fiiftar-czf"$BACKUP_FILE"-C"$TAR_BASE""$TAR_TARGET">>"$LOG_FILE"2>&1;thenif[[-s"$BACKUP_FILE"]];then log_info "Backup created successfully: ${BACKUP_FILE}"else log_error "Backup file is empty. Aborting."exit1fielse log_error "Backup failed. Aborting."exit1fi# ===== C) Delete .bak files older than 30 days inside SOURCE_DIR =====log_info "Searching .bak files older than 30 days in: ${SOURCE_ABS}"CANDIDATES_RAW=$(find"$SOURCE_ABS"-type f -name'*.bak'-mtime +30 -print2>>"$LOG_FILE"||true)if[[-n"$CANDIDATES_RAW"]];thenCANDIDATE_COUNT=$(printf'%s\n'"$CANDIDATES_RAW"|wc-l|tr-d' ') log_info "${CANDIDATE_COUNT} .bak files scheduled for deletion."printf'%s\n'"$CANDIDATES_RAW"|whileIFS=read-rfile;do[[-z"$file"]]&&continue log_info "Deleting: ${file}"doneiffind"$SOURCE_ABS"-type f -name'*.bak'-mtime +30 -delete>>"$LOG_FILE"2>&1;then log_info ".bak cleanup completed."else log_warn "Cleanup encountered issues. Review log: ${LOG_FILE}"fielse log_info "No .bak files older than 30 days found."fi# ===== D) Log bookkeeping =====log_info "All steps completed."log_info "Log file: ${LOG_FILE}"log_info "Backup file: ${BACKUP_FILE}"log_info "=== Maintenance run finished ==="
Ключевые концепции
Валидации и ранние выходы
Скрипт сразу проверяет, существует ли SOURCE_DIR. Раннее завершение предотвращает каскад вводящих в заблуждение ошибок.
Мы избегаем cd "$SOURCE_DIR"; вместо этого получаем абсолютный путь (SOURCE_ABS) через pwd -P, чтобы безопасно обрабатывать символические ссылки.
Каждая файловая операция (mkdir, touch, tar) проверяет свой результат и прерывается при ошибке, сохраняя достоверность логов.
Стратегия логирования
timestamp() предоставляет метки времени в формате ISO, благодаря чему логи можно сортировать в таблицах или SIEM-инструментах.
log_info, log_warn и log_error централизуют форматирование сообщений, уменьшая дублирование и проясняя уровни серьёзности.
Сообщения проходят через tee, поэтому оператор видит прогресс на экране, пока лог-файл фиксирует то же самое содержимое.
Перебор целей для очистки
Команда find собирает файлы-кандидаты .bak старше 30 дней. Скрипт подсчитывает их и логирует каждое удаление для прослеживаемости.
Поскольку удаления потенциально деструктивны, скрипт выводит имена директорий перед вызовом find … -delete и сохраняет stdout/stderr для последующего анализа.
Параметризация и портируемость
Аргументы по умолчанию (/etc, backups) поддерживают использование «из коробки», а опциональные параметры позволяют другим командам переиспользовать скрипт под свои задачи.
Выходные пути формируются от текущей рабочей директории (SCRIPT_DIR), что полезно, когда operations монтируют скрипт в контейнеризированные задачи.
Скрипт использует POSIX-совместимые конструкции (с минимумом расширений Bash), чтобы оставаться портируемым между распространёнными дистрибутивами Linux.
Запуск скрипта и сбор доказательств
После сохранения файла как mantenimiento.sh запусти:
chmod +x mantenimiento.sh
./mantenimiento.sh
Чтобы передать аргументы (например, бэкапить /var/www в nightly_backups):
./mantenimiento.sh /var/www nightly_backups
Проверь архив бэкапа и лог, выведя список целевой директории и просмотрев заголовок лога:
Скриншоты этих команд служат артефактами успешного выполнения.
Размышление и теоретическое обоснование
Условия контролируют каждое критичное действие, гарантируя, что последующие шаги выполняются только при успешном выполнении предпосылок. Это предотвращает частично выполненное состояние и сохраняет точность логов.
Аргументы делают скрипт переиспользуемым на серверах с разной структурой директорий, способствуя единообразию внутри operations-команды.
Абсолютные и относительные пути. Скрипт полагается на абсолютные пути для исходных директорий и мест хранения логов, чтобы устранить неоднозначность. Внутри директории бэкапов относительные ссылки (например, ./mantenimiento_${STAMP}.log) можно использовать безопасно.
Портируемость и меры безопасности. Использование set -euo pipefail (которое можно добавить при запуске на Bash 4+) и проверка существования директорий защищают от ошибок конфигурации. Логирование в отдельный файл поддерживает аудит, а описанные ранее опциональные стратегии ротации или хеширования усиливают обнаружение подмены.
Следующие шаги
Чтобы продолжить харденинг воркфлоу обслуживания, рассмотри:
Добавление email- или webhook-уведомлений при сбое бэкапа.
Внедрение генерации и проверки контрольных сумм для архивированных файлов.
Расширение скрипта флагами командной строки (с помощью getopts) для подробного режима или dry run.
Планирование скрипта через cron с безопасными для окружения обёртками (/usr/bin/env bash).
Эти итерации опираются на навыки, представленные здесь, готовя тебя к управлению более крупными проектами автоматизации в безопасных operations-окружениях.