Когда на сервер попадает уже собранный билд веб-приложения, то со временем они накапливаются, что приводит к некоторым проблемам. Рассмотрим следующий случай и его решение.
На продакшен сервер попадает уже собранное веб-приложение. Новый билд всегда "монтируется" по адресу ./www/current
и перезапускает Nginx. Со временем билдов ставилось всё больше, кончалась память и свободные индексные дескрипторы доходили до критичного значения. Чтобы такое не происходило - старые билды нужно удалять. В этом случае не все, т.к. может потребоваться откат на предыдущий билд в аварийных случаях (а такое, к сожалению, случается).
Структура директории с билдами имела следующий вид:
- www
- - build_202203011010300
- - build_202203051536856
- - current
www
- директория с билдами;current
- симлинк на актуальный билд, который указан как точка входа для Nginx. Когда появляется свежий билд, то симлинк пересоздается, а Nginx перезапускается;build_*
- наши билды.
Задача предельно простая: нужно удалить всё ненужное, и оставить всё нужное. Для этого был использован командный язык Bash для написания скрипта зачистки и настроен вызов этого скрипта через Cron по удобному графику. Скрипт оставляет N-последних билдов, билд используемый для симлинка current и сам симлинк. В константе BUILD_DIR
указывается абсолютный путь до директории с билдами, а в константе PRESERVE_BUILDS_COUNT
указывается необходимое количество билдов, которое не будет затронуто. По результату выполнения скрипта будет выведены удаленные имена директорий и имена файлов/директорий, которые остались в директории с билдами после зачистки.
#!/usr/bin/env bash
# Скрипт предназначен для очистки директории с билдами от старых билдов, которые занимают память и
# и не требуются для работы.
# По умолчанию будет не затронуто 7 последних билдов включая билд, который используется для симлинка "current".
set -euo pipefail
# директория с билдами
BUILD_DIR=/home/user/www
# количество последних билдов на хранение
PRESERVE_BUILDS_COUNT=7
# символ новой строки
EOL='\n'
if [ ! -h ${BUILD_DIR}/current ]; then
echo 'ERROR: symlink "current" is not exists'
exit 1
fi
# имя билда используемого в симлинке "current"
CURRENT_BUILD_NAME=$(basename $(readlink ${BUILD_DIR}/current))
echo 'BUILD DIR:' $BUILD_DIR
echo 'CURRENT BUILD: ' $CURRENT_BUILD_NAME
echo ''
filtered_builds=''
# В результирующую выборку на удаление не попадет симлинк "current", т.к. он не является директорией,
# будут отброшены директории не удовлетворяющие шаблону "^build_[\d]+$", и будет отброшена директория используемая
# для симлинка "current".
for build_name in ${BUILD_DIR}/*; do
build_name=$(basename $build_name)
if [ ! -d ${BUILD_DIR}/${build_name} ]; then
continue
fi
if ! echo -n $build_name | grep -qP '^build_[\d]+$'; then
continue
fi
if [ $build_name = $CURRENT_BUILD_NAME ]; then
continue
fi
filtered_builds+=${build_name}${EOL}
done
# оставляем $PRESERVE_BUILDS_COUNT последних билдов
start_from_line=$(expr $PRESERVE_BUILDS_COUNT + 1)
filtered_builds=$(echo -ne $filtered_builds | sort -fr | tail -n +$start_from_line)
# удаление старых билдов
echo 'DELETED BUILDS:'
for build_name in $filtered_builds; do
rm -rf ${BUILD_DIR}/${build_name}
echo "> ${build_name}"
done
if [ -z "${filtered_builds}" ]; then
echo '> NONE'
fi
echo ''
# список сохраненных файлов/директорий после "зачистки"
echo 'PRESERVED FILES/DIRECTORIES:'
for build_name in ${BUILD_DIR}/*; do
build_name=$(basename $build_name)
echo -n "> ${build_name}"
if [ -h ${BUILD_DIR}/${build_name} ]; then
echo -n ' -> ' $(readlink ${BUILD_DIR}/${build_name})
fi
echo ''
done
exit 0