Pull to refresh

Автоматическое клонирование серверов на виртуальные машины по крону

Reading time6 min
Views5K

Постановка задачи


Описание проблемы


В работе используется большое количество физических серверов на базе Debian GNU/Linux. Разработчикам часто бывает нужно предоставить на растерзание клоны этих серверов, каждый раз клонировать руками неэффективно. Примечание: конкретный дистрибутив при описываемом методе не важен, метод очень легко адаптируется под любой дистрибутив.

картинка для красоты


Задача


Сделать автоматическую систему клонирования боевых серверов в виртуальные машины по крону.

Что получилось


virt_server> p2v.py foo full
WORKING WITH SERVER: 'foo'
READING CONFIG FOR 'foo'
CHECKING LOCAL CONFIG
CHECKING LOCAL CONFIG FOR 'foo' COMPLETED SUCCESSFULLY
CHECKING REMOTE CONFIG
CHECKING NODUMP FLAG: "lsattr -d /home/backupman/dumps | egrep '[\\w-]+d[\\w-]+[ ]/data/dumps'"
CHECKING REMOTE DUMP: 'sudo /sbin/dump a0f /dev/null /dev/null'
CHECKING IF WE ARE ABLE TO SSH TO: "ssh -T backupman@foo 'if [ -d /data/dumps ] ; then exit 0 ; else exit 1 ; fi'"
CHECKING REMOTE CONFIG FOR 'foo' COMPLETED SUCCESSFULLY
DUMPING FILESYSTEMS
GETTING THE DUMPS
STOPPING VM: foo2 
MAKING FS TYPE: ext3 ON PARTITION: /dev/mapper/foo
RESTORING DUMPS FOR: foo
INSTALLING BOOTLOADER FOR: foo
RESTORING CONFIG FOR: foo
STARTING VM: /etc/xen/foo.xm

Как получилось


Процесс клонирования боевого сервера


Наиболее простым и одновременно быстрым способом клонирования боевого сервера без его остановки я считаю следующее:
  • Создание на самом сервере его дампа с помощью утилиты dump. Соображения такие:
    • Возможные проблемы при дампе живой ФС для виртуальной машины не страшны, это не бэкап. По опыту, на практике я пока что проблем с битыми файлами не встречал
    • Дамп сервера делается быстро, что немаловажно
  • Копирование дампа на сервер с виртуальными машинами. Я использую scp, т.к. при наших скоростях узким местом все равно является сеть, и поэтому затраты на шифрование файлов при передачи не страшны
  • Разворачивание дампа на заранее выделенный LVM-раздел с помощью утилиты restore
  • Установка загрузчика
  • Копирование конфигов, актуальных для виртуальной машины (/etc/network/interfaces, /etc/hostname, /etc/mailname, /etc/aliases и т.д.)
  • Запуск свежеприготовленной виртуальной машины.

Используемая система виртуализации при этом совершенно не важна, единственное требование к ней — уметь предоставлять виртуальной машине раздел LVM в качестве виртуального диска. Я лично использую XEN.

Для наглядости приведу пример. Предполжим, что у нас есть сервер виртуальных машин virt_server, а также физический сервер который нужно клонировать, server1.
  • XENовский конфиг для клона server1 лежит на virt_server в /etc/xen/server1.xm
  • LVM раздел виртуальной машины server1 такой: /dev/mapper/server1
  • Дампы server1 будем делать в server1:/dumps/server1/, и копировать в virt_server:/dumps/server1
  • Измененные конфиги server1 будем держать в virt_server:/dumps/server1/cfg/

Для того чтобы все заработало, на server1 необходимо добавить пользователя backupman, и разрешить ему sudo dump без пароля. На virt_server нужно настроить аутентификацию по ключам на backupman@server1. На virt_server будем делать все из-под root.

При такой настройке из консоли процедура клонирования выглядит так:

root@virt_server> xm destroy server1
root@virt_server> ssh backupman@server1
backupman@server1$ sudo dump -a0f /dumps/server1.root /
backupman@server1$ for i in var usr home ; do sudo dump a0f /dumps/server1.$i /$i ; done
backupman@server1$ exit
root@virt_server> scp backupman@server1:/dumps/server1.* /dumps/server1/
root@virt_server> mkfs.ext4 /dev/mapper/server1
root@virt_server> mount /dev/mapper/server1 /mnt/server1
root@virt_server> cd /mnt/server1
root@virt_server> restore -rf /dumps/server1.root
root@virt_server> for i in var usr home ; do cd /mnt/$i && restore /dumps/server1.$i ; done
root@virt_server> dd if=/usr/share/syslinux/mbr.bin of=/dev/mapper/server1
root@virt_server> extlinux --install /dev/mapper/server1
root@virt_server> scp -R /dumps/server1/cfg/* /mnt/server
root@virt_server> xm start server1.xm

Автоматизация процесса


Теперь, когда алгоритм клонирования сервера ясен, можно приступить к написанию скрипта. Работа со скриптом будет производиться следующим образом:
  • В конфиге скрипта описывается порядок клонирования каждого сервера
  • В аргументах скрипта указывается имя сервера и требуемое действие
  • Скрпит читает раздел конфига названный по имени сервера, затем делает автоматическое клонирование сервера и запуск виртуальной машины
  • Запуск скрипта происходит по крону по ночам
  • ????
  • Выгода!

Настройка сервера виртуальных машин и клонируемого сервера


На сервере виртуальных машин нужно сделать следующее:
  • Создать дисковый раздел для виртуальной машины
  • Создать конфигурационный файл виртуальной машины
  • Создать конфигурационный файл скрипта

На клонируемом сервере нужно сделать следующее:
  • Установить утилиту dump
  • Создать пользователя backupman
  • Разрешить этому пользователю запускать дамп, добавив в /etc/sudoers следующее: backupman ALL=NOPASSWD: /sbin/dump
  • Создать директорию для дампов
  • Сделать на эту директорию chattr +d чтобы не сдампило дампы
  • Разрешить заход по ssh с севера виртуальных машин на клониреумый сервер по ключам

Конфиг скрипта


Конфиг в стандарном .ini формате кладется в директорию со скриптом. Выглядит он так:
[server1]
vm_config = /etc/xen/server1.xm
vm_name = server1
ssh = backupman@server1.site
scp = backupman@server1.site
dumps_list = /,/var,/usr,/home
remote_dumps_dir = /home/bakupman/dumps
local_dumps_dir = /dumps/server1
mount_dir = /mnt/server1
partition = /dev/mapper/server1
fs = ext3

Следует дать небольшие пояснения по параметрам ssh и scp. Эти параметры определяют формат вызова ssh и scp соответственно, и они оба нужны т.к. у ssh порт задатся ключем -p, а у scp — ключем -P.

Сам скрипт


Приводить скрипт полностью я не вижу смысла, т.к. я выложил его на github, поэтому только опишу работу скрипта в целом.

Скрипт принимает на вход два параметра, а именно имя сервера, которое является именем раздела в конфигурационном файле, и требуемое действие.

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

def main():
    """Main function, calls all other ones"""

    if len(sys.argv) < 3:
        sys.exit('Usage: %s <servername> <action> (check|full|dump|get|restore)' % sys.argv[0])

    server = sys.argv[1]
    action = sys.argv[2]

    print "\nWORKING WITH SERVER: %r\n" % server
    
    if action == "check":
        try:
            conf=read_config(server)
            check_config_local(conf)
            check_config_remote(conf
        except Exception, e:
            sys.exit("\nERROR: %r\n" % e)
    elif action == "full":
        try:
            conf=read_config(server)
            check_config_local(conf)
            check_config_remote(conf)
            stop_vm(conf)
            dump_physical(conf)
            get_dumps(conf)
            restore_vm(conf)
            install_bootloader(conf)
            restore_config(conf)
            start_vm(conf)
        except Exception, e:
            cleanup(conf)
            sys.exit("\nERROR: %r\n" % e)
    <...>

Вся работа скрипта построена таким образом, что если некоторые действие не удается совершить, то выбрасывется исключение и скрипт завершается с сообщением об ошибке.

Конфигурация указанная в конфигурационном файле проверяется, и если что-то не так, то скрипт завершается. Если же все в порядке, то скрпит просто исполняет все те команды, которые можно ввести из консоли, проверяя статус их завершения.

Самой «сложной» частью скрипта являются проверки. Я хотел бы обратить внимание на этот фрагмент:

def check_config_local(conf):
    """Checks config by trying to ssh, checking existence of local and remote folders,
    destination partition, mount point, and remote dump utility""" 

    print "\nCHECKING LOCAL CONFIG\n"

    # check for ensuring we won't delete accidentaly specified host os parittion
    if conf['partition'] == "/dev/sda"  or conf['partition'] == "/dev/sda1" or \
       conf['partition'] == "/dev/sda2" or conf['partition'] == "/dev/sda3" or \
       conf['partition'] == "/dev/sda4" or conf['partition'] == "/dev/sda5":
        raise Exception, "DON'T KILL THE SERVER! %S IS A SYSTEM PARTITION!!" % conf['partition']

У меня операционная система на сервере виртуальных машин установлена именно на эти разделы, для самих виртуальных машин сделан LVM. Т.к. при разворачивании виртуальной машины на предназнеченном для нее разделе создается новая файловая системы, то для защиты от случайного уничтожения сервера виртуальных машин впишите сюда ваши системные разделы.

github


Вот и все, скрипт можно взять тут: github.com/sistemshik/p2v
Tags:
Hubs:
Total votes 14: ↑12 and ↓2+10
Comments7

Articles