Работа с Proxmox в стиле IaC
Интерфейс Proxmox-а достаточно удобен для быстрого создания виртуальных машин. Но прогресс не стоит на месте, и парадигма Infrastructure as Code набирает всё больше сторонников. Действительно, очень удобно получить стенд, состоящий из пяти, десяти и более хостов, при помощи одной команды в консоли. Удобный инструмент для этого предоставляет Ansiblе при помощи одного из своих модулей -
Далее я покажу, как создать необходимое кол-во виртуальных машин с указанными вами характеристиками, используя 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 |
(!)Обратите внимание на Наименование. Оно задаётся как полностью определённое имя домена для изменения значения
Характеристики стенда
В своём примере я использую: - выделенный сервер с установлённым 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-ключа.
Вносим правки в конфигурационный файл
ssh_pwauth: true
Если вы в этом не нуждаетесь, то можете не менять значение этого параметра. Так же в этом файле можно отключить модули, которые вы не будете использовать.
Выполним
Перейдём на вкладку Cloud-Init и определим учётную запись с паролем.
(!)Обратите внимание: необходимо задать имя пользователя и пароль! Иначе вы не сможете зайти в созданную из этого шаблона VM. Сетевые настройки в параметрах
Далее кликаем на кнопку Regenerate image и после выполнения этой команды конвертируем виртуальную машину в
Обратите внимание, что после конвертации исчезает возможность запуска -
Таким образом, если вам необходимо установить какое-либо дополнительное ПО, сделать специфические настройки, всё это нужно сделать до(!) этапа конвертации в
Итого, на данном этапе у нас есть шаблон виртуальной машины на основе CentOS 7.
Описание переменных Ansible
Переменные, которые будет использовать
В файле мы определим переменные, необходимые для авторизации в 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 будут точно такие же как и у
- Запуск 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-а (
--- - 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) }}"
Итак, теперь у нас есть не только шаблон виртуальной машины, но и
Запуск плейбука
Запустим плейбук при помощи команды
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 [email protected] [sablin@master ~]$ ssh -i /tmp/id_rsa [email protected] [sablin@slave ~]$
Проверяем характеристики хостов в интерфейсе Proxmox-а:
Всё соответствует ожидаемому.
Заключение
Можно ли применить подход IaC к системе виртуализации Proxmox? Да, только что мы в этом убедились: один
Справедливости ради, нужно сказать, что есть и другие инструменты декларативного подхода. Например, плагин 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)