Если заглянуть назад в прошлое когда еще не было Ansible или других систем удаленного администрирования linux мы пользовались только своими подручными скриптами, позволяли им подключаться к системам по ssh с помощью ключей. Думаю и по сей день многие использую свои скрипты взамен системам централизованного управления.

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

Логика работы выстроилась сразу. Зайти на хост по ssh, выполнить некоторые команды для подготовки бекапов и забрать готовые файлы с помощью scp.

Первым делом нужно создать пользователя который будет иметь доступ к необходимым данным с необходимыми правами. Тут я думаю каждый сам сможет разобраться в какую группу определить пользователя. Главное помнить:
  1. Имя пользователя для работы входа без пароля должно быть одинаковое на всех хостах или придется для каждого хоста указывать свой логин что не есть удобно;
  2. Пользователь должен иметь доступ на хост без пароля (ssh-keygen в помощь) ;
  3. Пользователь должен иметь доступ к файлам которые необходимо забрать.

тут можно пойти несколькими путями 1) выполнить команду в удаленном shell для сбора необходимых данных подлежащих резервированию и потом их забрать 2) настроить cron на хосте и выполнять команду сбора данных а уже потом, когда нибудь, заходить на хост и забирать готовые пирожки бекапы. Конечно мы пойдем по первому пути

Имеем такую конфигурацию —
10 хостов (Mikrotik) с которых необходимо получить два типа бекапов — бинарный (для восстановления с нуля) и конфигурацию без паролей и сертификатов для заливки на работающий конфиг. Так же в полном нашем распоряжении имеется машина с debian 8 на борту назовем ее сервер (и не важно что это контейнер, важно что это дебиан) ну и конечно куда ж без него — zabbix-server.

  • IP Mikrotik — 10.10.0.1, 10.10.1.1, 10.10.2.1, 10.10.3.1, 10.10.4.1, 10.10.5.1, 10.10.6.1, 10.10.7.1, 10.10.8.1, 10.10.9.1;
  • IP Zabbix-server — 10.10.10.10;


Для упрощения задачи zabbix-hostname будет в формате mik(третий октет в десятичном формате).host таким образом получаем —
  • Zabbix hostnames — mik0.host, mik1.host, mik2.host, mik3.host, mik4.host, mik5.host, mik6.host, mik7.host, mik8.host, mik9.host


Если кто не помнит — zabbix hostname мы указываем в файле настроек zabbix-agent(агент тут не нужен, но все же) и на сервере zabbix в web-ui.


Первым делом создаем на нашем сервере ключ RSA. Почему RSA — да вообщем то по привычке, кстати, старые версии RB поддерживают только DSA, а все что свежее 6.35 уже работает с RSA и DSA, потому смотрите по обстановке, можно и обновиться, как сделал я :), если у вас уже есть готовый ключ — пропускайте этот шаг.

ssh-keygen -t RSA

Переносим содержимое файла $HOME/.ssh/id_rsa.pub с сервера на наши хосты. Я ленивый и для Mikrotik использовал winbox.

Для линукс можно сделать проще создаем скрипт sh и запускаем его от имени пользователя которым мы будем ходить на хост за бекапами (на хостах пользователь уже должен быть) такого содержания —
Если у вас ключ DSA то измените id_rsa.pub на id_dsa.pub
#!/usr/bin/env bash

hosts=(10.10.0.1 10.10.1.1 10.10.2.1 10.10.3.1 10.10.4.1 10.10.5.1 10.10.6.1 10.10.7.1 10.10.8.1 10.10.9.1)
username='user'
for host in ${hosts[*]} 
        do
                cat $HOME/.ssh/id_rsa.pub | ssh -o "StrictHostKeyChecking no" ${user}@${host} 'cat >> ~/.ssh/authorized_keys'
        done

Запускаем его и вводим поочередно пароли для всех 10 серверов.

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

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

Создаем на сервере вот такой вот скрипт —
#!/usr/bin/env bash

hosts=(10.10.0.1_mik0.host_22        10.10.1.1_mik1.host_22        10.10.2.1_mik2.host_22        10.10.3.1_mik3.host_22        10.10.4.1_mik4.host_22        10.10.5.1_mik5.host_22        10.10.6.1_mik6.host_22        10.10.7.1_mik7.host_22        10.10.8.1_mik8.host_22        10.10.9.1_mik9.host_22 )
# bash array of values. All values are arrays too, after remove splitter "_".
# Sub array content IP_ZABBIX-HOSTNAME_SSH-DAEMON-PORT
cdate=`date +%d-%m-%Y` # System date =) Hi Max
dir="/mik_backup/" # Storage for backups
cmd="/system backup save name=backup; export file=backup.rsc hide-sensitive" # command that do the preparation of backup
username="user" # SSH user
zabbix_hp=(10.10.10.10 10051) # IP then PORT
age="30" # remove all backups older then 30 days
itemname="backup" # zabbix item
error_value="1" # error value for trigger
value="0" # good value =)

for host in ${hosts[*]} # Get values from main list
do
hostname=($(echo ${host} | tr "_" " ")) # Get values from sub list
ssh ${username}@${hostname[0]} -o "StrictHostKeyChecking no" -p${hostname[2]} "${cmd}"
new_dir="${HOME}${dir}${hostname[1]}/${cdate}"
mkdir -p ${new_dir}
scp -P${hostname[2]} ${username}@${hostname[0]}:backup.backup  ${new_dir}
scp -P${hostname[2]} ${username}@${hostname[0]}:backup.rsc ${new_dir}
check=`find ${new_dir} -type f -name backup.*`
if [ "${check}" == "" ]
then
zabbix_sender -z ${zabbix_hp[0]} -p ${zabbix_hp[1]} -s ${hostname[1]} -k ${itemname} -o ${error_value}
else
zabbix_sender -z ${zabbix_hp[0]} -p ${zabbix_hp[1]} -s ${hostname[1]} -k ${itemname} -o ${value}
fi
done
find ${HOME}${dir} -mindepth 2 -mtime ${age} -type d -exec rm -rf {} \; #clear dirs


Постарался максимально прокомментировать все что происходит в скрипте. Давайте разберем, что же тут такое делается.

Тут мы говорим каким интерпретатором будем выполнять код
#!/usr/bin/env bash

Тут создаем массив с необходимыми нам данными для подключения к хостам и отправки данных в zabbix
hosts=(10.10.0.1_mik0.host_22        10.10.1.1_mik1.host_22        10.10.2.1_mik2.host_22        10.10.3.1_mik3.host_22        10.10.4.1_mik4.host_22        10.10.5.1_mik5.host_22        10.10.6.1_mik6.host_22        10.10.7.1_mik7.host_22        10.10.8.1_mik8.host_22        10.10.9.1_mik9.host_22 )

Тут думаю только одна переменная нуждается в объяснении — $cmd. Это две команды которые выполняются на Mikrotik последовательно. Первая создает бинарный бекап
вторая создает скрипт с настройками без выгрузки паролей и ключей шифрования.

cdate=`date +%d-%m-%Y` # System date =) Hi Max
dir="/mik_backup/" # Storage for backups
cmd="/system backup save name=backup; export file=backup.rsc hide-sensitive" # command to do the preparation of backup
username="user" # SSH user
zabbix_hp=(10.10.10.10 10051) # IP then PORT
age="30" # remove all backups older then 30 days
itemname="backup" # zabbix item
error_value="1" # error value for trigger
value="0" # good value =)

Основное тело программы. На входе в цикл мы имеем массив содержащийся в переменной $hosts. Цикл работает так — берем первый элемент массива, у нас он равен 10.10.0.1_mik0.host_22 и начинаем с ним работать. Первым же действием мы заносим в переменную $hostname массив созданный из первого элемента массива $hosts. Делаем мы это с помощью команды tr по сути как в python эмитируем действие метода строки .split(). Получается вполне сносно. Мы получаем 3 элемента в массиве $hostname. Первый элемент — ip хоста. Второй элемент — zabbix-hostname. Третий элемент — ssh-port.
Дальше мы обращаемся к этим элементам с помощью индекса, опять же python.
Далее формируем древо каталогов для хранения файлов и указываем scp какие файлы забирать. Прошу не пинать — если кто подскажет как с помощью scp в такой конструкции обращаться к файлам по маске + в карму.
После того как мы получили файлы — отправляем в zabbix сообщение об успехе.
Проверка создания конфига производится простым поиском файла в каталоге назначения. Можно было сделать сравнение md5 на Mikrotik и в каталоге назначения, но это уже другая история, хотя я так делал.

for host in ${hosts[*]} # Get values from main list
do
hostname=($(echo ${host} | tr "_" " ")) # Get values from sub list
ssh ${username}@${hostname[0]} -o "StrictHostKeyChecking no" -p${hostname[2]} "${cmd}"
new_dir="${HOME}${dir}${hostname[1]}/${cdate}"
mkdir -p ${new_dir}
scp -P${hostname[2]} ${username}@${hostname[0]}:backup.backup  ${new_dir}
scp -P${hostname[2]} ${username}@${hostname[0]}:backup.rsc ${new_dir}
check=`find ${new_dir} -type f -name backup.*`
if [ "${check}" == "" ]
then
zabbix_sender -z ${zabbix_hp[0]} -p ${zabbix_hp[1]} -s ${hostname[1]} -k ${itemname} -o ${error_value}
else
zabbix_sender -z ${zabbix_hp[0]} -p ${zabbix_hp[1]} -s ${hostname[1]} -k ${itemname} -o ${value}
fi
done

Тут чистим место. Переменная $age поможет нам сохранить бекапы столько сколько нам этого надо
find ${HOME}${dir} -mindepth 2 -mtime ${age} -type d -exec rm -rf {} \; #clear dirs


Теперь самое тривиальное.
Создаем на сервере zabbix шаблон или просто элемент данных типа zabbix_trapper на наших узлах которые мы заблаговременно добавили на мониторинг в zabbix.
Не буду выкладывать шаблон из одного элемента данных и одного триггера. Думаю сделать его сможет каждый. Главное помнить, если хосты мониторятся через zabbix-proxy, данные вы должны отправлять на zabbix-proxy. В ином случае отправляем все на zabbix-server.
Не важно даже какой будет IP у этих хостов в zabbix web ui. Важно чтобы совпадали hostname с данными в скрипте.

PS. На все скрипты нужно кинуть chmod +x так их можно будет запускать без вызова интерпретатора.
P.S.S Чтобы передавать scp список файлов для резервного копирования на linux можно сделать еще одни массив и вложить его в цикл for. Можно сделать все в виде получаемых параметров. Ну вообщем можно развлечься.

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


  1. divaka
    28.12.2017 21:51

    Зачем так усложнять. Можно же использовать старый добрый rancid. Он прекрасно работает с микротиками так ещё и diff делает.


    1. Gnom4ik Автор
      28.12.2017 21:59

      Век живи век учись, с cisco гонял этого зверя. С микротом не доводилось. Можно попробовать. Спасибо за наводку.


      1. Nengchak
        29.12.2017 04:40

        можете подсказать, почему бэкап с помощью rsc вдруг перестал бэкапить все
        system export file=1.rsc
        Бэкапит только название и скрипты кажется, а все остальные настройки игнорирует.


        1. Gnom4ik Автор
          29.12.2017 09:05

          Сложно ответить на такой вопрос. Можно попробовать посмотреть версию RoS и если она имеет возможность к обновлению — обновить. На RoS 6.35 + все работает. Еще хочу заметить что system не нужно указывать export file=1 будет достаточно.


          1. Gnom4ik Автор
            29.12.2017 09:28

            С утра надо проснуться и потом уже садиться за компьютер… Вы выгружаете данные из пространства system а там и содержаться только ваши скрипты. Все настройки нужно выгружать от корня. Почему так произошло? Вы видимо обновились… Не знаю как было раньше на старых версиях.


            1. Nengchak
              29.12.2017 20:09

              на старых версиях все работает как надо, а на новых нет. И да, спасибо за подсказку, экспорт с корня работает как надо.


  1. borisovEvg
    28.12.2017 22:56

    у cisco есть замечательная директива archive для бекапов


  1. baxxter
    29.12.2017 08:56

    Использую oxidized для этих целей и сохраняю на приватный gitlab — всегда можно посмотреть историю изменений. Работает и с Cisco, и с Mikrotik и еще бог знает с чем.
    github.com/ytti/oxidized


    1. romeo_82
      29.12.2017 13:45

      Мне oxidized показался еще сыроватым…


  1. tenhi_shadow
    29.12.2017 14:02

    дратути. Надысь, писал я про подобное(честно говоря вообще не понял к чему тут zabbix, поясните пожалуйста)
    Критика: если адреса микротиков в цикле — не будет работать. Я по опыту скажу, я делал список в отдельном файле, в том же скрипте, через параметры, используя for, until — хренушки.
    Всегда будет трабла одна — если в середине цикла косячный микротик — рвётся весь цикл. т.е. если недоступен, если поменялась публичная часть ключа и т.п. Я долго это дело костылил, но дошёл только до одного — делать скоп микротик в цикле категорически неправильно. Поэтому в своём решении я разделил сам скрипт и конфиги в отдельные файлы. Т.е. скрипт как хорошая программа имеет какие-то дефолты, которые потом можно перебить в конфигурационном файле для определённого микротика. Выгода в том, что если сломается бэкап одного — не пострадают остальные(как у вас). Так же я добавил логгирование, вполне себе нормальное(которое даже при неожиданном отключении нормально залогирует, что не было бэкапа)
    Почитайте пожалуйста, мою статью, взгялните критическим взгядом. Незачем плодить 100500 проектов, мне кажется. Лучше туда добавить что-то(к примеру заббикс, только я не пойму зачем?)
    habrahabr.ru/post/342060

    PS — не в обиду, по существу.


    1. mickvav
      29.12.2017 15:05

      От глючного микротика в середине цикла меня спасала кострукция примерно следующего вида — запускаем скрипт, общающийся с одним девайсом в фоне, инкрементим счетчик. Как только счетчик доходит до некоторого порога — делаем wait — ждем, пока кто-нибудь (!) освободится, декрементим счетчик, запускаем следующего. Так, пока в списке что-то осталось. Потом делаем waitall. Итого — обходим овер-дофига железок, каждая может подвиснуть и/или затормозить, а всё вместе отработает за вполне разумное время.


      1. tenhi_shadow
        29.12.2017 15:14

        я делал не только так, — костыляки бессмысленные. Я думаю, что проще и лучше вынести конфиги отдельно, скрипт отдельно, тогда не будет проблем и можно будет задавать очень много кастомных параметров, при этом же сохраняя то, что можно оставить для других 100500 микротиков дефолтные, которые определены в сприпте.
        Про заббикс так и не понял. В моём понимании система… мониторит, а система резервного копирования — правильно, бэкапит.
        Так же не вижу очистки бэкапов, которые так и накапливаются на устройстве. Через определённое количество времени(когда тупо память забьётся на микротике), такие бэкапы встанут в единственно возможную позу — перестанут делаться. Произойдёт фиктивное срабатывание скрипта, далее произойдёт тот самый wait непонятно чего, а потом скрипт в конце завершится с exitcode=0. Всё вроде и работает, а бэкапа-то нет…


        1. Gnom4ik Автор
          29.12.2017 21:22

          Ух, да вы говорите верно, но к сожалению практика уже показала что если я не могу достучаться до хоста по ssh, например no route to host, все прекрасненько отрабатывает на последующих микротах. По заббиксу скажу так — он уже есть, уже работает и костылить велосипед рядом — смысла не вижу. Отправлять можно хоть в socket.


          1. tenhi_shadow
            29.12.2017 22:23

            у меня обрывался луп и не было продолжения, когда, к примеру, был Routeros 5(там тупо всё по-другому). Не вижу универсальности в решении. Если просто поделиться, как может быть — ок, но как решение, ну так себе. Мониторинг он мониторит, а не бэкапит(ну я так полагаю)


      1. Gnom4ik Автор
        29.12.2017 21:48

        Да, тут полностью согласен, если сессия зависнет и не завершиться — цикл остановится и не будет продолжаться. Будет ждать сначала ssh потом scp и еще раз scp тут зарыта собака и если что то пойдет не так — будет боль и страдание, но для этого существует мониторинг zabbix — в котором не получение данных в течении n количества минут от скрипта гласит — а ну поднимай свой тощий зад и тащи его сюда скорей посмотри что там с бекапами


    1. mihmig
      29.12.2017 17:23

      бэкапы старше чем ST_RTN дней
      А как вы обрабатываете ситуацию, когда бекапы не далались 30 дней — существующие не удалятся?


      1. tenhi_shadow
        29.12.2017 19:11

        на микротике удаляются только те, которые делаются скриптом.
        RTN только для архивации, за это время они лежат в папке ../hostname, а старше этой даты в ../hostname/archive
        Удалять они вообще не удаляются с хоста, где выполняется скрипт(очень малый размер)

        По итогу: если SN_RTN=30, то при работе скрипта все бэкапы переместятся в archive на 31 день, если вы таки делаете бэкап.


    1. Gnom4ik Автор
      29.12.2017 21:40

      Честно, увидел ваш проект после того как написал свой скрипт. Посмотрел — честно говоря это уже что-то из разряда сервисов с логированием и возможностью подключения модулей. Мой же скрипт прост как дважды два, но затрагивает одну интересную проблему bash — отсутствие вложенных массивов.Я, честно говоря, люблю больше python и мне было бы даже проще и быстрее написать программу которая будет вам не только по ssh ходить, но еще и чаю нальет лог запишет. К слову сказать есть много библиотек для работы ssh в python. Так вот мне захотелось сделать это безобразие на bash в стиле python — получилось сносно. Считаю что из этого кода кто то может почерпнуть для себя что то интересное. Еще раз повторюсь моей целью не было просто взять и написать штуку которая будет лучше всех собирать бекапы =)


  1. mihmig
    29.12.2017 17:00

    Не знало про zabbix_sender, спасибо.
    А есть что-то для заббикса но работающее исключительно в песочнице javascript-браузера?
    Нужно контролировать доступность веб-ресурса именно из браузера инфокиоска.
    Пинги могут идти, сервер работать, а веб-приложение именно с этого киоска — нет.


    1. Gnom4ik Автор
      29.12.2017 21:54

      я думаю что вы сможете отсылать на заббикс сервер данные в формате JSON, другой вопрос как код внедрить в js. Ну тут я уже к сожалению не помогу. Формат JSON который принимает заббикс можно подсмотреть в том же zabbix-sender'e поснифать пакеты и потом посмотреть содержимое, не думаю что там есть что то сверхестественное, возможно вы сможете найти описание формата на просторах www.zabbix.com но не путайте с форматом LLD — это всего лишь кусочек того что отправляет sender.


  1. maxx_s
    29.12.2017 21:23

    Немножко паранойи и предостережений
    1 — вы оставляете бекап на самом микротике, если вашу железку унесут плохие люди, то получат доступ к вашим данным(snmp, users, etc.) В общем то можно не заходить на микротики, а выполнить команду удалённо, бекап может остаться там, а export можно через '>' отправить в файл.
    2 — вы не проверяете содержимое файла .rsc, бывают ситуации(баги) когда скрипт не отрабатывает до конца, можно смотреть что скрипт содержит хотя бы 50 строчек, например.
    3 — есть нюансы в зависимости от версий RouterOS и от платформы, я бы порекомендовал делать ещё export verbose, это поможет при замене микротика другой платформой и версией софта, могут меняться разные умолчания и синтаксис, без export verbose этого вы не увидите.


    1. Gnom4ik Автор
      29.12.2017 21:30

      По первому пункту согласен полностью.
      По второму — rsc делается исключительно как запаска + есть еще 29 бекапов до… Ну если и они битые — тут уже не приятно. Да можно добавить проверку на содержимое думаю стоит доделать.
      По третьему — спасибо, буду знать. Это уже добавляю.