Небольшой скрипт, который установит terraform и модуль telmate/proxmox
актуальной версии.
vim install.sh
#!/bin/bash apt-get update && apt-get install unzip go git ansible make sshpass -y mkdir /tmp/tfinstall cd /tmp/tfinstall wget https://hashicorp-releases.yandexcloud.net/terraform/1.8.0/terraform_1.8.0_linux_amd64.zip unzip terraform_1.8.0_linux_amd64.zip cp terraform /usr/bin/ terraform -install-autocomplete echo "provider_installation { dev_overrides { \"registry.terraform.io/telmate/proxmox\" = \"/root/.terraform.d/plugins\" } }" > /root/.terraformrc git clone https://github.com/Telmate/terraform-provider-proxmox cd terraform-provider-proxmox make mkdir -p /root/.terraform.d/plugins cp bin/terraform-provider* /root/.terraform.d/plugins/ cd - rm -rf /tmp/tfinstall echo "Reboot system for terraform autocomplete"
Подтянуть из репозитория данный модуль в рабочем состоянии, на тот момент когда я устанавливал всё это, было невозможно. Версия была старой, даже в офф hashicorp репо. А при этом старом модуле всегда было ошибка 400
Скрипт создания tmp машинки:
vim CreateTmp.sh
#!/bin/bash if [ $# -ge 4 ]; then scp $2 $1:/var/lib/vz/template/iso/$4 ssh $1 "qm create $3 --memory 2048 --net0 virtio,bridge=vmbr0 qm importdisk $3 /var/lib/vz/template/iso/$4 local-lvm --format raw qm set $3 --scsihw virtio-scsi-pci --scsi0 local-lvm:vm-$3-disk-0 qm set $3 --name $4 qm set $3 --ide2 local-lvm:cloudinit qm set $3 --boot c --bootdisk scsi0 qm template $3" else echo "use: ./CreateTmp.sh user@host ./local/path/to/image tmpid tmpname" fi
./CreateTmp.sh user@host ./local/path/to/image tmpid tmpname".
Подходит далеко не в каждом случае, возможно прийдется создавать tmp вручную.
Создание tmp машинки вручную, с установкой quemu-guest-agent в образ:
wget http://ftp.altlinux.org/pub/distributions/ALTLinux/images/Sisyphus/cloud/x86_64/alt-sisyphus-cloud-x86_64.qcow2
cp ./alt-sisyphus-cloud-x86_64.qcow2 /var/lib/vz/templates/iso/
cd /var/lib/vz/templates/iso/
#Установка агента в образ
virt-customize -a alt-sisyphus-cloud-x86_64.qcow2 --install qemu-guest-agent
#Создание новой ВМ
qm create 9001 --memory 2048 --net0 virtio,bridge=vmbr0
qm importdisk 9001 /var/lib/vz/template/iso/alt-sisyphus-cloud-x86_64.qcow2 local-lvm --format qcow2
qm set 9001 --scsihw virtio-scsi-pci --scsi0 local-lvm:vm-9001-disk-0
qm set 9001 --agent enabled=1,fstrim_cloned_disks=1
qm set 9001 --name alt-server
#Создание диска cloud-init
qm set 9001 --ide2 local-lvm:cloudinit
qm set 9001 --boot c --bootdisk scsi0
qm set 9001 --serial0 socket --vga serial0
#Преобразование ВМ в template
qm template 9001
Добавление зеркала репозитория, если нужно (в РФ hashicorp репо не работает просто так):
vim /root/.terraformrc
provider_installation { network_mirror { url = "https://terraform-mirror.yandexcloud.net/" include = ["registry.terraform.io/*/*"] } direct { exclude = ["registry.terraform.io/*/*"] } }
Добавление провайдера proxmox:
mkdir /terraform
cd /terraform
vim main.tf
terraform { required_providers { proxmox = { source = "telmate/proxmox" } } } provider "proxmox" { pm_api_url = "https://192.168.100.200:8006/api2/json" pm_user = "root@pam" pm_pass = "password" pm_tls_insecure = true #Если необходимо }
Возможно потребуется указать версию модуля, что бы он не брал его с репозитория. version = "> "
Настройка tfvars:
cd /terraform
vim vars.tf
variable "pm_api_url" { type = string } variable "pm_user" { type = string default = "root@pam" } variable "pm_password" { type = string default = "P@ssw0rd" } variable "vmid" { type = string default = 1000 } variable "vm_name" { type = string default = "test" } variable "vm_count" { type = string default = "1" } variable "vm_user" { type = string default = "user" } variable "vm_pass" { type = string default = "P@ssw0rd" } variable "target_node" { type = string default = "pve" } variable "tmp_name" { type = string default = "alt-cloud" } variable "vm_cores" { type = string default = "2" } variable "vm_memory" { type = string default = "2048" } variable "vm_storage" { type = string default = "local-lvm" } variable "vm_size" { type = string default = "32G" } variable "vm_bridge" { type = string default = "vmbr1" } #variable "dns" { # type = string #} variable "subnet" { type = string } #variable "mask" { # type = string #} #variable "domain" { # type = string #}
И уже в файле terraform.tfvars определяем значения переменным:
cd /terraform
vim terraform.tfvars
pm_api_url = "https://<pve_ip_address>:8006/api2/json" pm_user = "root@pam" pm_password = "P@ssw0rd" vmid = "100" vm_name = "alt-1-" vm_count = "4" vm_user = "user" vm_pass = "P@ssw0rd" target_node = "pve" tmp_name = "alt-cloud" vm_cores = "4" vm_memory = "4096" vm_storage = "local-lvm" vm_size = "15G" vm_bridge = "vmbr878" subnet = "192.168.183"
Так как terraform никак сам не может создавать vmbr`ы, то используем ansible. Небольшой playbook, создающий vmbr:
vim vmbr.yml
--- - name: add vmbr to pve hosts: pve become: false vars_files: - ./terraform_vars.yaml tasks: - name: install nft yum: name: nftables state: latest update_cache: yes notify: enable_nft - name: add vmbr to pve blockinfile: path: /etc/network/interfaces marker: '# {mark} ansible add {{ vm_bridge }}' block: | auto {{ vm_bridge }} iface {{ vm_bridge }} inet manual address {{ subnet }}.1/24 bridge-ports none bridge-stp off bridge-fd 0 notify: up_interface - name: add nft ruleset blockinfile: path: /etc/nftables.conf block: | #!/usr/sbin/nft -f table inet nat { chain prerouting { type nat hook prerouting priority 0; } chain postgouting { type nat hook postrouting priority 0; oifname "vmbr0" masquerade } } notify: restart_nft handlers: - name: up_interface shell: "ifup {{ vm_bridge }}" ignore_errors: true - name: restart_nft service: name: nftables state: restarted - name: enable_nft service: name: nftables state: started enabled: yes
На стандартный vmbr0, с помощью nftables, накидывается masquerade. Таким образом из любого vmbr есть доступ в сеть
Что бы ansible работал - необходимо настроить под себя ansible.cfg и hosts(инвентарь). Hosts должен содержать:
vim hosts
[pve] pve ansible_host=<pve_ip_address> ansible_user=root ansible_password=password
Что бы 10 раз не писать все переменные, конвертируем их из tf формата в yaml с помощью .py файла:
vim convert.py
import yaml #Чтение переменных Terraform из файла terraform_vars = {} with open('terraform.tfvars', 'r') as f: for line in f: if '=' in line: key, value = line.strip().split('=', 1) terraform_vars[key.strip()] = value.strip().strip('"') #Преобразование переменных в формат YAML yaml_vars = yaml.dump(terraform_vars) #Запись преобразованных переменных в файл YAML with open('terraform_vars.yaml', 'w') as f: f.write(yaml_vars) print("Variables converted to YAML format successfully.")
Сам tf, для создания ВМ:
vim vms.tf
resource "proxmox_vm_qemu" "vm1" { vmid = "${var.vmid}${count.index}" count = var.vm_count ciuser = var.vm_user cipassword = var.vm_pass name = "${var.vm_name}${count.index+1}" target_node = var.target_node clone = var.tmp_name agent = 0 os_type = "cloud-init" cores = var.vm_cores sockets = 1 vcpus = 0 memory = var.vm_memory scsihw = "virtio-scsi-pci" disks { scsi { scsi0 { disk { size = var.vm_size storage = var.vm_storage iothread = false discard = true } } } } network { model = "virtio" bridge = var.vm_bridge } cloudinit_cdrom_storage = "local-lvm" ipconfig0 = "ip=${var.subnet}.${count.index+10}/24,gw=${var.subnet}.1" }
Обратите внимание: Для cloud-init дисков используется storage local-lvm
Ну и финальный скрипт, немного упрощающий жизнь:
vim CreateVMs.sh
#!/bin/bash python3 ./convert.py && \ ansible-playbook ./vmbr.yml && \ terraform apply -auto-approve && \
Итого:
Складываем все файлики (кроме install.sh) в папочку и заполняем файл переменными, далее запускаем CreateVMs.sh.
Если необходимо сделать ещё ВМ другого типа, например другая ОС, то копируем файлы из папочки (terraform state не берем, он будет только мешать) в новую, там меняем переменные и всё запускаем. Не забываем менять resource "proxmox_vm_qemu" "vm1" на новое в файле vms.tf
.