Работа с Proxmox в стиле IaC

Введение

Интерфейс Proxmox-а достаточно удобен для быстрого создания виртуальных машин. Но, прогресс не стоит на месте, и парадигма Infrastructure as Code набирает все больше сторонников. Действительно, очень удобно получить стенд, состоящий из пяти, десяти и более хостов, при помощи одной команды в консоли. Удобный инструмент для этого предоставляет Ansiblе при помощи одного из своих модулей - proxmox_kvm.

Далее я покажу как, используя template Proxmox-а и playbook Ansible, создать необходимое кол-во виртуальных машин с указанными вами характеристиками. Playbook мы будем запускать на сервере, где установлен Proxmox, поэтому в качестве хоста указывается localhost. Предполагается, что у вас уже есть опыт работы с Proxmox и Ansible по отдельности. Если понадобится что-то уточнить - пожалуйста, задавайте вопросы в комментариях.

План действий таков: - создать template в Proxmox-е - настроить playbook Ansible - запустить playbook для для создания нескольких виртуалок - убедимся в том, что создание виртуальных машин прошло успешно

В качестве примера будут созданы две виртуальные машины (далее VM):

  • Первая VM
Свойство Значение
Наименование master.example.com
Кол-во сокетов 1
Кол-во ядер 2
Оперативная память (MB) 1024
IP адрес 10.21.21.51/24
Уникальный номер VM 200
  • Вторая VM
Свойство Значение
Наименование slave.example.com
Кол-во сокетов 1
Кол-во ядер 4
Оперативная память (MB) 16384
IP адрес 10.21.21.52/24
Уникальный номер VM 201

(!)Обратите внимание на Наименование. Оно задается как полностью определённое имя домена для изменения значения hostname у VM, которые мы создаем (в данном случае это будут master и slave).

Характеристики стенда

В своем примере я использую: - выделенный сервер с установленным Proxmox VE 5.4 - на сервере установлен Ansible версии 2.9, минимально необходимая версия - 2.3 - Python версии 2.7.13 - DHCP не установлен (поэтому сетевые настройки указываем явным образом)

Предварительная настройка

Установим необходимые для работы модуля proxmox_kvm зависимости

  pip install requests
  pip install proxmoxer

Создание темлейта

Виртуальные машины будут созданы на основе образа CentOS 7. Сохраним ISO-образ со страницы Download CentOS И загрузим его в Proxmox

upload_iso-23186-32b5c4.jpg

Далее создадим виртуальную машину, которую мы потом преобразуем в template. - На вкладке Network я указал Linux bridge соответствующий подсети, из которой я определяю IP - Остальные параметры оставляю без изменений - В итоге настройки будущего template-а выглядят так

create_vm-23186-824225.jpg

После старта и установки операционной системы заходим и устанавливаем пакет cloud-init:

 yum install cloud-init -y

Этот пакет нам понадобится для изменения настроек VM до ее запуска. К примеру, мы сможем задать IP адрес и указать публичную часть ssh-ключа.

Вносим правки в конфигурационный файл /etc/cloud/cloud.cfg - указываем возможность заходить по ssh, используя пароль.

 ssh_pwauth: true

Если вы в этом не нуждаетесь, то можете не менять значение этого параметра. Так же в этом файле можно отключить модули, которые вы не будете использовать.

Выполним Shutdown для виртуальной машины и на вкладке Hardware добавим CloudInit Drive.

add_cloudinit-23186-6828b2.jpg

Перейдем на вкладку Cloud-Init и определим учетную запись с паролем.

add_user-23186-06b40d.jpg

(!)Обратите внимание - задать имя пользователя и пароль необходимо! Иначе вы не сможете зайти в созданную из этого шаблона VM. Сетевые настройки в параметрах cloud-init мы не задаем - они будут определены позже, в playbook-е Ansible.

Далее кликаем на кнопку Regenerate image и после выполнения этой команды конвертируем виртуальную машину в template - вызываем контекстное меню и выбираем пункт "Convert to template"

Обратите внимание, что после конвертации исчезает возможность запуска - template можно использовать только для создания других VM.

converted-23186-0a5cbd.jpg

Таким образом, если вам необходимо установить какое-либо дополнительное ПО, сделать специфические настройки - все это нужно сделать до(!) этапа конвертации в template.

Итого, на данном этапе у нас есть шаблон виртуальной машины на основе CentOS 7.

Описание переменных Ansible

Переменные, которые будет использовать playbook, мы сохраним в отдельном файле. Таким образом, при необходимости, мы сможем настроить несколько независимых конфигураций стендов.

В файле мы определим переменные, необходимые для авторизации в API Proxmox-авторизации (ваши данные будут отличаться, конечно):

 - api_host: m11618.contaboserver.net
 - api_user: root@pam
 - api_password: XXXXXXXXXXXX

И переменные, описывающие характеристики виртуальных машин

 - node: m11618 - идентификатор ноды в Proxmox
 - clone_vm: template-centos7 - имя template, из которого мы будем клонировать VM
 - key_name: id_rsa.pub (имя ключа, который будет скопирован в VM)
 - словарь vms, с определением индивидуальных характеристик VM
   - name: master.example.com - FQDN
   - ipaddress: 10.21.21.51/24 - IP адрес
   - vmid: 200 - уникальный ID VM
   - cores: 2 - количество ядер
   - sockets: 1 - количество сокетов
   - memory: 1024 - размер оперативной памяти (в мегабайтах!)

Итоговый вид файла (vms.yml):

api_host: m11618.contaboserver.net
api_user: root@pam
api_password: XXXXXXXXXXXX
node: m11618
clone_vm: template-centos7
key_name: id_rsa.pub
vms:
  master:
    name: master.example.com
    ipaddress: 10.21.21.51/24
    vmid: 200
    cores: 2
    sockets: 1
    memory: 1024
  slave:
    name: slave.example.com
    ipaddress: 10.21.21.52/24
    vmid: 201
    cores: 4
    sockets: 1
    memory: 16384

Описание playbook Ansible

Определим следующие логические блоки

  • Загрузка переменных
  vars_files:
    - vms.yml
  • Клонирование VM из template
    - name: Clone VMs
      proxmox_kvm:
        node: "{{ node }}"
        name: "{{ item.value.name }}"
        newid: "{{ item.value.vmid }}"
        api_user: "{{ api_user }}"
        api_password: "{{ api_password }}"
        api_host: "{{ api_host }}"
        clone: "{{ clone_vm }}"
        full: yes
      loop: "{{ lookup('dict', vms) }}"

При помощи конструкции loop мы обрабатываем все записи из словаря vms

  • Определение IP адресов
    - name: Set IP addresses
      command: "qm set {{ item.value.vmid }}  --ipconfig0 ip={{ item.value.ipaddress }}"
      loop: "{{ lookup('dict', vms) }}"
  • Загрузка ключей
    - name: Copy SSH key
      command: "qm set {{ item.value.vmid }} --sshkey {{ key_name }}"
      args:
        chdir: /tmp
      loop: "{{ lookup('dict', vms) }}"

Ключи находятся в каталоге /tmp сервера, где запускается playbook.

  • Указываем характеристики VM
    - name: Update VMs
      proxmox_kvm:
        api_host:     "{{ api_host }}"
        api_user:     "{{ api_user }}"
        api_password: "{{ api_password }}"
        cores:        "{{ item.value.cores }}"
        sockets:      "{{ item.value.sockets }}"
        memory:       "{{ item.value.memory }}"
        update:       yes
        vmid:         "{{ item.value.vmid }}"
        node:         "{{ node }}"
        name:         "{{ item.value.name }}"
      loop: "{{ lookup('dict', vms) }}"

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

  • Запуск VM
    - name: Start VMs
      proxmox_kvm:
        api_host:     "{{ api_host }}"
        api_password: "{{ api_password }}"
        api_user:     "{{ api_user }}"
        vmid:         "{{ item.value.vmid }}"
        node:         "{{ node }}"
        state:        started
      loop: "{{ lookup('dict', vms) }}"

Итоговый вид playbook-а (create_vm.yml)

---
- name: Initial setup VM
  hosts: localhost
  vars_files:
    - vms.yml
  tasks:
    - name: Clone VMs
      proxmox_kvm:
        node: "{{ node }}"
        name: "{{ item.value.name }}"
        newid: "{{ item.value.vmid }}"
        api_user: "{{ api_user }}"
        api_password: "{{ api_password }}"
        api_host: "{{ api_host }}"
        clone: "{{ clone_vm }}"
        full: yes
      loop: "{{ lookup('dict', vms) }}"

    - name: Set IP addresses
      command: "qm set {{ item.value.vmid }}  --ipconfig0 ip={{ item.value.ipaddress }}"
      loop: "{{ lookup('dict', vms) }}"

    - name: Copy SSH key
      command: "qm set {{ item.value.vmid }} --sshkey {{ key_name }}"
      args:
        chdir: /tmp
      loop: "{{ lookup('dict', vms) }}"

    - name: Update VMs
      proxmox_kvm:
        api_host:     "{{ api_host }}"
        api_user:     "{{ api_user }}"
        api_password: "{{ api_password }}"
        cores:        "{{ item.value.cores }}"
        sockets:      "{{ item.value.sockets }}"
        memory:       "{{ item.value.memory }}"
        update:       yes
        vmid:         "{{ item.value.vmid }}"
        node:         "{{ node }}"
        name:         "{{ item.value.name }}"
      loop: "{{ lookup('dict', vms) }}"

    - name: Start VMs
      proxmox_kvm:
        api_host:     "{{ api_host }}"
        api_password: "{{ api_password }}"
        api_user:     "{{ api_user }}"
        vmid:         "{{ item.value.vmid }}"
        node:         "{{ node }}"
        state:        started
      loop: "{{ lookup('dict', vms) }}"

Итак, теперь у нас есть не только шаблон виртуальной машины, но и playbook, который из этого шаблона создает необходимое кол-ов хостов с заданными характеристиками.

Запуск плейбука

Запустим плейбук при помощи команды

ansible-playbook create_vm.yaml

Результат выполнения команды должен быть таким:

PLAY [Initial setup VM] ********************************************************

TASK [Gathering Facts] *********************************************************
ok: [localhost]

TASK [Clone VMs] ***************************************************************
changed: [localhost] => (item={'key': u'master', 'value': {u'ipaddress': u'10.21.21.51/24', u'name': u'master.example.com', u'memory': 1024, u'cores': 2, u'vmid': 200, u'sockets': 1}})
changed: [localhost] => (item={'key': u'slave', 'value': {u'ipaddress': u'10.21.21.52/24', u'name': u'slave.example.com', u'memory': 16384, u'cores': 4, u'vmid': 201, u'sockets': 1}})

TASK [Set IP addresses] ********************************************************
changed: [localhost] => (item={'key': u'master', 'value': {u'ipaddress': u'10.21.21.51/24', u'name': u'master.example.com', u'memory': 1024, u'cores': 2, u'vmid': 200, u'sockets': 1}})
changed: [localhost] => (item={'key': u'slave', 'value': {u'ipaddress': u'10.21.21.52/24', u'name': u'slave.example.com', u'memory': 16384, u'cores': 4, u'vmid': 201, u'sockets': 1}})

TASK [Copy SSH key] ************************************************************
changed: [localhost] => (item={'key': u'master', 'value': {u'ipaddress': u'10.21.21.51/24', u'name': u'master.example.com', u'memory': 1024, u'cores': 2, u'vmid': 200, u'sockets': 1}})
changed: [localhost] => (item={'key': u'slave', 'value': {u'ipaddress': u'10.21.21.52/24', u'name': u'slave.example.com', u'memory': 16384, u'cores': 4, u'vmid': 201, u'sockets': 1}})

TASK [Update VMs] **************************************************************
changed: [localhost] => (item={'key': u'master', 'value': {u'ipaddress': u'10.21.21.51/24', u'name': u'master.example.com', u'memory': 1024, u'cores': 2, u'vmid': 200, u'sockets': 1}})
changed: [localhost] => (item={'key': u'slave', 'value': {u'ipaddress': u'10.21.21.52/24', u'name': u'slave.example.com', u'memory': 16384, u'cores': 4, u'vmid': 201, u'sockets': 1}})

TASK [Start VMs] ***************************************************************
changed: [localhost] => (item={'key': u'master', 'value': {u'ipaddress': u'10.21.21.51/24', u'name': u'master.example.com', u'memory': 1024, u'cores': 2, u'vmid': 200, u'sockets': 1}})
changed: [localhost] => (item={'key': u'slave', 'value': {u'ipaddress': u'10.21.21.52/24', u'name': u'slave.example.com', u'memory': 16384, u'cores': 4, u'vmid': 201, u'sockets': 1}})

PLAY RECAP *********************************************************************
localhost                  : ok=6    changed=5    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0  

Проверка результата

Теперь мы можем зайти на созданные хосты по определенным нами IP, пользователю и ключу

ssh -i /tmp/id_rsa sablin@10.21.21.51
[sablin@master ~]$
ssh -i /tmp/id_rsa sablin@10.21.21.52
[sablin@slave ~]$

Проверяем характеристики хостов в интерфейсе Proxmox-а

master-23186-914e77.jpg

slave-23186-22b19a.jpg

Все соответствует ожидаемому.

Заключение

Можно ли применить подход IaC к системе виртуализации Proxmox? Да, только что мы в этом убедились - один playbook, несколько наборов конфигураций. Далее возможна интеграция с пайплайнами CI/CDL - всё ограничивается только вашей фантазией.

Справедливости ради, нужно сказать, что есть и другие инструменты декларативного подхода - к примеру, плагин Terraform - https://github.com/Telmate/terraform-provider-proxmox. Но он, к сожалению, не документирован и официально не сертифицирован.

Материалы к статье

Ansible docs: proxmox_kvm module Cloud-Init documentation Proxmox docs: VM Templates and Clones Creating a template in Proxmox Virtual Environment with cloud-init support (youtube.com)

Автор
0 комментариев
Для комментирования необходимо авторизоваться