Однажды в одно прекрасное утро перед нами встал вопрос архивирования индексов Elasticsearch. Захотелось увидеть в хранилище стройные ряды сжатых файлов, по одному на каждый индекс.

«Из коробки» Elastic такого решения не предлагает, по крайней мере, в версии 5.х. Немного поспрашивав у Гугла Всемогущего, мы решили создать собственный велосипед. Пусть немного неуклюжий, зато родной.




Итак, дано:
Сервер с установленной на нём ОС Linux. На сервере установлен, настроен и работает Elasticsearch версии 5.х, в Elasticsearch хранятся индексы с именами вида project_name-yyyy.mm.dd

Задача:
Архивировать каждый индекс старше N дней, сжав его в отдельный файл для возможности восстановления данного конкретного индекса за нужную дату. По завершению процесса архивирования индекс удалить из Elasticsearch.

Решение


Архивирование индексов


Для работы с индексами нам потребуется curator версии старше 5.0.
Вытаскивать индексы из Elasticsearch будем через снапшоты. Поэтому для начала убедимся, что в файле конфигурации elasticsearch, обычно это файл /etc/elasticsearch/elasticsearch.yml, прописан путь до файлового репозитория:

path.repo: /opt/elasticsearch/snapshots

Если такой строки нет, пропишем и перезагрузим elasticsearch.

Далее создадим репозиторий, в котором мы будем размещать снапшоты:

mkdir -p /opt/elasticsearch/snapshots/repository
curl -XPUT 'http://localhost:9200/_snapshot/repository' -H 'Content-Type: application/json' -d '{
    "type": "fs",
    "settings": {
        "location": "repository",
        "compress": true
    }
}'

И сразу же создадим ещё один репозиторий с именем «recovery», он нам понадобится для восстановления индексов:

mkdir -p /opt/elasticsearch/snapshots/recovery
curl -XPUT 'http://localhost:9200/_snapshot/recovery' -H 'Content-Type: application/json' -d '{
    "type": "fs",
    "settings": {
        "location": "recovery",
        "compress": true
    }
}'

Ну и сам скрипт архивирования индексов. Из списка индексов, подлежащих архивированию, исключаем индексы .kibana и elastalert_status. Значения переменных, заданных в шапке скрипта, естественно, можно вытащить наружу и передавать скрипту в качестве аргументов, дело вкуса.

В качестве хранилища архивов в скрипте указана локальная папка, но это, конечно же, не очень хорошая практика, и лучше архивы складывать куда-нибудь подальше в надёжное место.

Процесс архивирования логируется, файл лога задаётся переменной $LOG. Не забывайте настроить ротацию для файла лога.

Логика работы скрипта описана в комментариях. Не забудьте поправить значения переменных, если ваши настройки будут отличаться от дефолтных.

#!/bin/bash
 
DAYS=31 #Количество дней, от текущей даты, старше которого индексы будут архивироваться
SNAPSHOT_DIRECTORY="/opt/elasticsearch/snapshots"
BACKUP_DIR="/opt/elasticsearch/elasticsearch_backup"
REPOSITORY="repository"
LOG="/var/log/elasticsearch/elasticsearch_backup.log"
DATE=`date`
  
#Проверим существование папки для архивов и если нет, создадим её
if ! [ -d $BACKUP_DIR ]; then
  mkdir -p $BACKUP_DIR
fi
  
#Получаем массив индексов, которые старше $DAYS
INDICES=`curator_cli --config /etc/elasticsearch/curator-config.yml --host localhost --port 9200 show_indices --filter_list "[{\"filtertype\":\"age\",\"source\":\"creation_date\",\"direction\":\"older\",\"unit\":\"days\",\"unit_count\":\"$DAYS\"},{\"filtertype\":\"kibana\",\"exclude\":\"True\"},{\"filtertype\":\"pattern\",\"kind\":\"regex\",\"value\":\"elastalert_status\",\"exclude\":\"True\"}]"`
 
#Проверим, не пустой ли список
TEST_INDICES=`echo $INDICES | grep -q -i "error" && echo 1 || echo 0`
 
if [ $TEST_INDICES == 1 ]
then
  echo "$DATE Не найдено индексов для обработки" >> $LOG
  exit
else
# Составляем цикл для каждого индекса в массиве $INDICES
for i in $INDICES
  do
    # Создаём снапшот для индекса $i
    curator_cli --config /etc/elasticsearch/curator-config.yml --timeout 600 --host localhost --port 9200 snapshot --repository $REPOSITORY --filter_list "{\"filtertype\":\"pattern\",\"kind\":\"regex\",\"value\":\"$i\"}"
 
    # Заносим в переменную имя снапшота для индекса $i
    SNAPSHOT=`curator_cli --config /etc/elasticsearch/curator-config.yml --host localhost --port 9200 show_snapshots --repository $REPOSITORY`
 
    # Архивируем папку репозитория и складываем архив в хранилище
    cd $SNAPSHOT_DIRECTORY/$REPOSITORY && tar cjf $BACKUP_DIR"/"$i".tar.bz" ./*
 
    # Удаляем snapshot
    curator_cli --config /etc/elasticsearch/curator-config.yml --host localhost --port 9200 delete_snapshots --repository $REPOSITORY --filter_list "{\"filtertype\":\"pattern\",\"kind\":\"regex\",\"value\":\"$SNAPSHOT\"}"
 
    # Удаляем индекс
    curator_cli --config /etc/elasticsearch/curator-config.yml --host localhost --port 9200 delete_indices --filter_list "{\"filtertype\":\"pattern\",\"kind\":\"regex\",\"value\":\"$i\"}"
 
    # Очищаем папку репозитория
    rm -rf $SNAPSHOT_DIRECTORY/$REPOSITORY/*
  done
fi

Удаление устаревших архивов


Отлично! Мы получили архивы индексов в хранилище, но их-то тоже нужно периодически чистить. Сделаем для этого отдельный скрипт /opt/elasticsearch/delete_archives.sh

#!/bin/bash
 
# Удаление бекапов старше $DAYS дней
# ВАЖНО! В имени файла архива может быть только один знак "-" перед датой. Дата должна быть в формате "yyyy.mm.dd".
# Например: aaa_bbb.ccc-yyyy.mm.dd.tar.bz
 
DAYS=91
BACKUP_DIR="/opt/elasticsearch/elasticsearch_backup"
  
#Определяем пороговую дату для удаления архивов
THRESHOLD=$(date -d "$DAYS days ago" +%Y%m%d)
 
#echo "THRESHOLD=$THRESHOLD"
 
FILES=`ls -1 $BACKUP_DIR`
 
TODELETE=`for i in $FILES; do echo $i | awk -F- '{printf "%s\n",$2 ;}' | awk -F. '{printf "%s%s%s \n",$1,$2,$3 ;}' | sed "s/$/$i/"; done`
 
echo -e "$TODELETE" |while read DATE FILE
  do
     [[ $DATE -le $THRESHOLD ]] && rm -rf $BACKUP_DIR/$FILE
  done

Ну вот, теперь осталось установить скрипты в крон. У нас они выполняются раз в сутки по ночам. Вывод скриптов перенаправим в файл лога

0   1   *   *   *   /bin/bash /opt/elasticsearch/backup_snapshot.sh >> /var/log/elasticsearch/elasticsearch_backup.log
0   3   *   *   *   /bin/bash /opt/elasticsearch/delete_archive.sh >> /var/log/elasticsearch/elasticsearch_backup.log

Восстановление индексов из архива


Процесс восстановления индексов тоже несложный. Для удобства обернём процесс восстановления индекса из архива в скрипт, которому на вход в качестве аргумента будем передавать имя файла архива. Для работы скрипта необходимо установить утилиту jq. Папку репозитория /opt/elasticsearch/snapshots/recovery для восстановления индекса и сам репозиторий в Elasticsearch мы создали ранее.

#!/bin/bash

#Зададим переменные
ARCHIVE=$1
BACKUP_DIR="/opt/elasticsearch/elasticsearch_backup"
RECOVERY_DIR="/opt/elasticsearch/snapshots/recovery/"

# На всякий случай очищаем папку репозитория
rm -rf $RECOVERY_DIR/*

# Разархивируем индекс в папку репозитория
tar xjf $BACKUP_DIR/$ARCHIVE -C $RECOVERY_DIR

# Заносим в переменную $SNAPSHOT имя снапшота в репозиториии
SNAPSHOT=`curl -s -XGET "localhost:9200/_snapshot/recovery/_all?pretty" | jq '.snapshots[0].snapshot' | sed 's/\"//g'`

# Восстанавливаем индекс из снапшота
curl -XPOST "localhost:9200/_snapshot/recovery/$SNAPSHOT/_restore?pretty"

# Нужно выставить небольшую задержку, чтобы Elasticsearch не ругался на удаление восстанавливаемого снапшота
sleep 30

# Удалим снапшот из репозитория
curl -XDELETE "localhost:9200/_snapshot/recovery/$SNAPSHOT?pretty"

# Очистим папку репозитория
rm -rf $RECOVERY_DIR/*

Подводим итог

Мы сделали архивирование каждого индекса в отдельный файл, настроили удаление устаревших архивов и научились восстанавливать индекс за нужную дату из архива.
Можно позволить себе взять с полки пирожок.

Комментарии (11)


  1. Sovigod
    15.02.2018 14:27

    А как же дедупликация данных, которая в классическом бекапе из коробки?


  1. Darklord
    15.02.2018 20:32

    Просто оставлю это здесь — elasticsearch-curator


    1. nightvich
      15.02.2018 23:13

      Если всмотреться в скрипты, то автор использует именно curator_cli.


    1. BuddyGlass Автор
      16.02.2018 10:27

      Добрый день!
      вы хотите сказать, что в elasticsearch-curator есть описанный функционал из коробки и затея со скриптами — лишние танцы с бубном? Т.е. куратор может не только сделать выборку нужных мне индексов по фильтрам, но и сжать их? Может я что-то пропустил, но если так, буду вам очень благодарен, если укажете в документации к куратору, как это можно сделать. Спасибо.


      1. Darklord
        16.02.2018 10:57

        фильтрация есть, для сжатия сможно shrink использовать


        1. BuddyGlass Автор
          17.02.2018 16:41

          Насколько я понимаю, shrink не сжимает данные как таковые. Он лишь позволяет снизить количество шардов, на которые растащен индекс по вашему кластеру. Проще говоря, если в вашем кластере 3 ноды и ваш индекс шардится на все три ноды, а вам нужно на одной из нод освободить место, то shrink вам в помощь.


  1. surkov_nsk
    15.02.2018 20:32

    Здорово, так часто бывает, что надо что-то быстро сделать, нет времени задумываться, и так не хватает понятной инструкции. А тут — вот она)


  1. nightvich
    15.02.2018 23:23

    Некоторое время назад писал скрипт, который позволяет снимать/удалять/восстанавливать снапшоты и индексы за указанный период времени, n последних, разность между снятыми снапшотами и текущими индексами и т.д. Привязка была к формату хранения индексов за период времени в формате ISO week.


    1. chemtech
      16.02.2018 07:24

      Можно ли Вас попросить выложить его на github или куда-нибудь чтобы люди могли пользоваться


  1. olku
    17.02.2018 12:26

    Бесплатная ГУИ-тула, умеет бакап и рестор, может кому пригодится kaizen.artcom-venture.de


    1. BuddyGlass Автор
      17.02.2018 16:24

      Спасибо большое, ранее не попадался этот инструмент.