Обновление скрипта резервного копирования

Обновил скрипт для резервного копирования. Убрал вызов bc, вынес всё в отдельные функции, причесал код, чтобы ShellCheck был счастлив:

#!/usr/local/bin/bash
 
BACKUP_ROOT=/var/backups/manual/
BACKUP_DIR="$BACKUP_ROOT"$(date +"%Y-%m-%d/%H-%M")
PREFIX=$(date +"%Y%m%d_%H%M")
LIMIT=$((4*1024*1024))
 
exit_on_error() {
    exit_code=$?
    if [ $exit_code -ne 0 ]; then
        >&2 echo -e "FAIL"
        exit $exit_code
    fi
}
 
print_used_space() {
    printf "Used space: "
    SIZE=$(du -sB 1 $BACKUP_ROOT | cut -f 1)
    printf "%sMb(%s%%)\n" "$((SIZE / 1024))" "$((SIZE * 100 / LIMIT))"
}
 
backup_etc() {
    FILENAME="$PREFIX"_etc.tar.bz2
    printf "Creating %s .. " "$FILENAME"
    if /usr/bin/tar -Pjcf "$BACKUP_DIR"/"$FILENAME" /etc /usr/local/etc /home /var/db/ports /var/named/etc/namedb /root/scripts > /dev/null 2>&1; then
        printf "OK\n"
    else
        exit_on_error
    fi
}
 
backup_site() {
    FILENAME="$PREFIX"_www.tar.bz2
    printf "Creating %s .. " "$FILENAME"
    if /usr/bin/tar -Pjcf "$BACKUP_DIR"/"$FILENAME" /usr/local/www > /dev/null; then
        printf "OK\n"
    else
        exit_on_error
    fi
}
 
backup_db() {
    FILENAME="$PREFIX"_db.sql.bz2
    printf "Creating %s .. " "$FILENAME"
    if /usr/local/bin/mysqldump --login-path=backup --opt --all-databases --triggers --routines --events | bzip2 -c > "$BACKUP_DIR"/"$FILENAME"; then
        printf "OK\n"
    else
        exit_on_error
    fi
}
 
remove_old_files() {
    removed=0
    for days in $(seq 100 -1 0)
    do
        SIZE=$(du -sB 1 $BACKUP_ROOT | cut -f 1)
        if [[ $SIZE -le $LIMIT ]]; then
            break
        fi
        if [[ -z "$(find $BACKUP_ROOT -type f -mtime +"$days")" ]]; then
            continue
        fi
        printf "Removing files older than %d days:\n" "$days"
        find $BACKUP_ROOT -type f -mtime +"$days"d -delete -print
        find $BACKUP_ROOT -empty -type d -delete -print
        removed=1
    done
 
    if [ $removed -ne 0 ]; then
        true
        return
    fi
 
    false
}
 
if [ ! -d "$BACKUP_DIR" ]; then
        mkdir -p "$BACKUP_DIR"
fi
 
backup_etc
backup_site
backup_db
print_used_space
remove_old_files && print_used_space
true
Краткая инструкция по обновлению порта для сопровождающих

Примерно с 2008 года совершенно случайно являюсь сопровождающим порта sysutils/archivemount в ОС FreeBSD и недавно стал получать сообщения от их багтрекера. Их Bugzilla с упорством достойным поощрения настойчиво раз в неделю присылала мне письма об ошибках. Так что ничего не оставалось делать, как обновить соответствующий порт и в результате родилась эта инструкция.

# Делаем копию оригинала
cp -r /usr/ports/sysutils/archivemount/ /usr/ports/sysutils/archivemount.bak
# Обновляем Makefile (PORTVERSION=new_version, PORTREVISION=0)
vim /usr/ports/sysutils/archivemount/Makefile
# Обновляем файл distinfo командой make makesum
cd /usr/ports/sysutils/archivemount && make makesum
# Генерируем diff (рекомендуется использовать git diff)
diff -ruN /usr/ports/sysutils/archivemount.bak /usr/ports/sysutils/archivemount > archivemount.diff

Заливаем этот diff через форму багтрекера с припиской в начале [maintainer update]. В качестве примера можно посмотреть обновление archivemount. Также приложите CHANGELOG со списком изменений и, разумеется, проверьте работу порта и новой версии приложения перед отправкой.
Что ещё почитать:
* FreeBSD Porters Handbook
* Руководство FreeBSD по созданию портов

Скрипт для резервного копирования

Всем привет, обновил скрипт для резервного копирования с учётом того, что я вообще-то программист, а не просто айтишник! Он стал более интеллектуально определять и удалять старые файлы, учитывать ограничение в 4.5Gb (на FTP для бекапа доступно 5Gb) и успешность операций, добавлены сообщения об ошибках. И бонусом сообщения раскрашены в разные цвета, нейтральные выделены жирным, хорошие зелёным, а плохие красным цветом — красота! Скрипт проверен на сайте ShellCheck, который я настоятельно рекомендую к использованию. Если найдёте ошибку или вам есть что добавить, смело пишите комментарий.

#!/usr/local/bin/bash
 
# Основная папка для архивов
BACKUP_ROOT=/var/backups/manual/
# Папка, куда непоредственно пишем файлы, выглядит как BACKUP_ROOT/ГОД/МЕСЯЦ/ДЕНЬ
BACKUP_DIR="$BACKUP_ROOT"$(date +"%Y/%m/%d")
# Время запуска скрипта. Все файлы имеют вид имя_файла_%ЧАС%%МИНУТА%.tag.bz2
TIME=$(date +"%H%M")
# Ограничение хранилища в килобайтах для проверки
LIMIT=$(echo "4096*2^10"|bc)
# Ограничение хранилища в мегабайтах для вывода в сообщениях
HLIMIT=$(echo "$LIMIT/1024"|bc)"Mb"
 
# Функция выход в случае ошибки
exit_on_error() {
    # Получаем код ошибки
    exit_code=$?
    # Входное сообщение
    message=$1
    if [ $exit_code -ne 0 ]; then
        # Выводим сообщение в stderr
        >&2 echo -e "\e[31m${message}\e[0m"
        # Выходим к кодом exit_code
        exit $exit_code
    fi
}
 
# Если папки нет
if [ ! -d "$BACKUP_DIR" ]; then
    # Создаём папку со всеми промежуточными подпапками
    mkdir -p "$BACKUP_DIR"
fi
 
printf "\e[1mSearching space for backup\e[0m .. "
# Цикл от 100 дней до нуля
for days in $(seq 100 -1 0)
do
    SIZE=$(du -sB 1 $BACKUP_ROOT | cut -f 1)
    HSZ=$(echo "$SIZE/1024"|bc)"Mb"
    # Если размер хранилища не превышает лимита
    if [[ $SIZE -le $LIMIT ]]; then
        # Выводим сообщение и прерываем цикл
        printf "[%s/%s] .. " "$HSZ" "$HLIMIT"
        break
    fi
    # Если нет ни одного файла старше $days дней
    if [[ -z "$(find $BACKUP_ROOT -type f -mtime +"$days")" ]]; then
        # Продолжаем цикл
        continue
    fi
    printf "\e[31m%s/%s]\e[0m .. " "$HSZ" "$HLIMIT"
    # Удаление файлов старше $days прошло успешно
    if find $BACKUP_ROOT -type f -mtime +"$days"d -delete > /dev/null; then
        # Выводим сообщение 
        printf "CLEAN (older than %s days) .. " "$days"
    else
        # Выходим с сообщением FAIL
        exit_on_error "FAIL"
    fi
    # Удаляем пустые папки, чтоб не мусорить
    find $BACKUP_ROOT -empty -type d -delete
done
echo -e "\e[32mOK\e[0m"
 
printf "\e[1mBackuping etc files\e[0m .. "
/usr/bin/tar -Pjcf "$BACKUP_DIR"/etc_"$TIME".tar.bz2 /etc /usr/local/etc /home /var/db/ports /var/named/etc/namedb /root/scripts > /dev/null 2>&1 && echo -e "\e[32mOK\e[0m" || exit_on_error "FAIL"
 
printf "\e[1mBackuping www folder\e[0m .. "
/usr/bin/tar -Pjcf "$BACKUP_DIR"/www_"$TIME".tar.bz2 /usr/local/www > /dev/null && echo -e "\e[32mOK\e[0m" || exit_on_error "FAIL"
 
printf "\e[1mBackuping SQL databases\e[0m .. "
/usr/local/bin/mysqldump --login-path=backup --opt --all-databases --triggers --routines --events | bzip2 -c > "$BACKUP_DIR"/sql_"$TIME".sql.bz2 && echo -e "\e[32mOK\e[0m" || exit_on_error "FAIL"

Откровенно говоря, не до конца понимаю, в каких единицах считает занятое место команда du. Предполагаю, что в килобайтах.

P.S. Для того, чтобы получить вывод без цвета, использую утилиту textproc/ansifilter:

script.sh | ansifilter --text
Последние новости сайта

SSL Сертификат

В связи с событиями на Украине контора Sectigo объявила санкции и перестала выпускать ssl сертификаты для .ru и .рф доменов. За выпуск оного я платил 820 рублей в год.

Благодаря сcанкциям, я перешёл на использование бесплатного сертификата от Let’s Encrypt (с помощью certbot — он есть в портах FreeBSD). Поначалу certbot капризничал и не хотел работать без AAAA DNS записи, но это вылечилось, когда я прописал www как CNAME основного домена. Так что теперь я использую сертификат от Let’s Encrypt, а конторе Sectigo вместе с Comodo могу сказать следующее:

Fuck you asshole!

Кстати, в связи с подобными запретами Минцифры РФ подсуетилось и начало выпускать отечественные SSL сертификаты через госуслуги, которые никем не признаны, доступны только юридическим лицам и принесут больше проблем, чем пользы. Досадно, что настройкой чебурнета занимаются кретины.

IPv6

Решая проблемы с certbot, неожиданно вспомнил, что хостер выдал мне сеть /64 IPv6 адресов. Я не сильно в этом разбираюсь, но судя по калькулятору, могу выдать каждому жителю планеты Земля по два миллиарда адресов в моём личном адресном пространстве. Мне много не надо, поэтому взял циферку один и теперь у моего сайта есть AAAA DNS запись и он открывается по IPv6 адресу.

Дела админские на сервере

От безделья решил обновить операционную систему на сервере. Сайт находится на VDS, поэтому обновить операционку можно только удалив всё нахрен и поставить чистую систему.

Раньше стояла FreeBSD 9.3. Всем прекрасна, консольна и стабильна, но поддержка закончилась пару лет назад и все приложения были старенькие совсем. Обновить их нельзя, потому что система портов больше не поддерживает старые версии FreeBSD — компилятор поменяли с GCC на модный Clang.

Теперь стоит FreeBSD 10.3. Стало совсем круто: все приложения свежие, новенькие с патчами безопасности и всё такое.

На сервере приложений всего ничего, вот список:

  • nginx (веб-сервер)
  • php (язык программирования)
  • mysql (база данных)
  • bind (ДНС)

Почта ходит через гугл.

Вот эти четыре приложения и обеспечивают работу сайта. Круто, да? )

Заодно проверил систему резервного копирования. Состоит из трёх частей:

  • сохранение базы данных с помощью mysqldump
  • архивирование папок /etc, /usr/local/etc, /var/db/pkg
  • архивирование папки с wordpress

Работает архивирование и восстановление прекрасно, как и планировалось.

Блог Евгения Жирнова