Работа с Proxmox в стиле IaC
2026-03-10 02:21 Diff

Интерфейс 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.

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

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

yum install cloud-init -y

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

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

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

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

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

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

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

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

Таким образом, если вам необходимо установить какое-либо дополнительное ПО, сделать специфические настройки, всё это нужно сделать до(!) этапа конвертации в 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

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

  • Загрузка переменных
  • Клонирование 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-а:

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

Заключение

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

Справедливости ради, нужно сказать, что есть и другие инструменты декларативного подхода. Например, плагин Terraform. Но он, к сожалению, не документирован и официально не сертифицирован.

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

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)