Como Construir una Imagen de Ubuntu 22.04 para Proxmox con Packer y Subiquity

Tabla de contenido
Packer es una herramienta de la empresa Hashicorp que nos permite crear imágenes de maquinas virtuales homogéneas, para multiples plataformas destino a partir de un template único de configuración, ayudando a crear nuevos hosts a partir de estos templates.
Packer tiene soporte para crear imágenes de Amazon EC2, CloudStack, DigitalOcean, Docker, Google Compute Engine, Microsoft Azure, Proxmox, VirtualBox, VMware y más.
En este articulo, veremos como construir una imagen de Ubuntu 22.04 LTS para mas adelante poder desplegar maquinas virtuales en Proxmox.
PROMO DigitalOcean #
Antes de comenzar, quería contarles que hay una promoción en DigitalOcean donde te dan un crédito de USD 100.00 durante 60 días para que puedas probar los servicios que este Proveedor Cloud ofrece. Lo único que tienes que hacer es suscribirte a DigitalOcean con el siguiente botón:
O a través del siguiente enlace: https://bit.ly/digitalocean-itsm
Instalando Packer #
Ubuntu / Debian #
Agregamos la llave GPG del repositorio de Hashicorp:
wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
Agregamos el repositorio:
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
Actualizamos los repositorios e instalamos Packer:
sudo apt update && sudo apt install packer
RHEL/ Rocky/ Almalinux / Fedora #
Instalamos el paquete dnf-plugins-core:
sudo dnf install -y dnf-plugins-core
Agregamos el repositorio:
sudo dnf config-manager --add-repo https://rpm.releases.hashicorp.com/fedora/hashicorp.repo
Actualizamos los repositorios e instalamos Packer:
sudo dnf -y install packer
MacOS #
Con brew es muy facil instalarlo:
brew tap hashicorp/tap
brew install hashicorp/tap/packer
Comprobamos la versión:
packer version
Packer v1.8.7
Creando Usuario de Servicio en Proxmox #
Para poder generar la imagen con Packer, es necesario que creemos una cuenta de servicio en nuestro nodo de Proxmox que permita realizar tareas en la API. Crearemos entonces una cuenta llamada packer@pve
con los permisos necesarios.
En una terminal del nodo Proxmox, creamos el usuario:
pveum useradd packer@pve
Vamos a crear el Rol con los mínimos privilegios requeridos posibles, para crear la maquina virtual que servirá para crear la imagen, lo llamaremos Packer
:
pveum roleadd Packer -privs "VM.Config.Disk VM.Config.CPU VM.Config.Memory Datastore.AllocateSpace Sys.Modify VM.Config.Options VM.Allocate VM.Audit VM.Console VM.Config.CDROM VM.Config.Network VM.PowerMgmt VM.Config.HWType VM.Monitor"
Y ahora vamos a asignar estos permisos al usuario packer@pve:
pveum aclmod / -user packer@pve -role Packer
Para autenticarnos a nuestro nodo Proxmox tenemos dos opciones, por usuario/Contraseña o con un Token. Si decidimos autenticarnos por contraseña, debemos asignar una al usuario packer@pve:
pveum passwd packer@pve
Enter new password: ****************
Retype new password: ****************
Por Token tendremos que ir a la GUI de Proxmox, en el Menu Datacenter > API Tokens y creamos un token con el nombre packer:
Una vez que hagamos click en Add, nos mostrará por una unica vez el token, el cual deberemos reservar:
Descargamos la imagen ISO de Ubuntu desde su repositorio oficial cuya última versión disponible es ubuntu-22.04.2-live-server-amd64.iso y la subimos a Proxmox en el almacenamiento local
bajo la categoria ISO Image
:
Creando el Template #
Configurando el Builder #
El ciclo de trabajo de Packer consiste en crear un template de maquina virtual que puede ser usado luego en desplegar un nuevo host rapidamente aplicando una configuración previa (disco, os, etc)
Tecnicamente, Packer iniciará una MV en Proxmox, ejecutará el instalador de la imagen de Ubuntu (en este caso pero puede ser cualquier distro Linux) referenciando al archivo de configuración que le vamos a indicar y luego convertirá esa MV resultante en un template cuando haya terminado la configuración.
Packer soporta dos formatos de archivos: JSON en versiones anteriores a 1.6.0 y HCL, en nuestro caso vamos a utilizar este ultimo para configurar nuestro template de VM, lo llamaremos ubuntu-2204.pkr.hcl
:
# Primero declaramos algunas variables.
# URL de nuestro host Proxmox
variable "proxmox_api_url" {
type = string
default = "https://direccionip:8006/api2/json"
sensitive = true
}
# El usuario de servicio de Proxmox
variable "proxmox_api_token_id" {
type = string
default = "packer@pve!packer"
sensitive = true
}
# El Token del Usuario de Servicio
variable "proxmox_api_token_secret" {
type = string
default = "TOKEN:ID"
sensitive = true
}
# Aqui comienza la definición de recursos del Template
source "proxmox" "ubuntu-server-jammy" {
# Configuración de conexión a Proxmox
proxmox_url = "${var.proxmox_api_url}"
username = "${var.proxmox_api_token_id}"
token = "${var.proxmox_api_token_secret}"
# (Opcional) Ignorar Validación TLS
insecure_skip_tls_verify = true
# Configuración General de la VM
# Nombre del nodo
node = "mamba"
# Id de la maquina virtual resultante para el template
vm_id = "9000"
# Nombre de la VM
vm_name = "ubuntu-2204-cloudinit-template"
# Descripcion del Template
template_description = "Ubuntu Server 22.04 Image"
# Configuración del SO de la MV
# (Opcion 1) Archivo ISO local
iso_file = "local:iso/ubuntu-22.04.2-live-server-amd64.iso"
# - or -
# (Opcion 2) Descargar archivo ISO
# iso_url = "https://releases.ubuntu.com/22.04.2/ubuntu-22.04.2-live-server-amd64.iso"
# Checksum de la imagen (en caso que queramos comprobar)
# iso_checksum = "10f19c5b2b8d6db711582e0e27f5116296c34fe4b313ba45f9b201a5007056cb"
# Almacenamiento donde se encuentra la imagen ISO
iso_storage_pool = "local"
unmount_iso = true
# Instalar qemu_agent (recomendado)
qemu_agent = true
# Configuración del Disco Duro de la VM
scsi_controller = "virtio-scsi-pci"
disks {
disk_size = "20G"
format = "raw"
storage_pool = "local-lvm"
storage_pool_type = "lvm"
type = "virtio"
}
# CPU de la VM
cores = "2"
# Memoria de la VM
memory = "2048"
# Configuración de Red de la VM
network_adapters {
model = "virtio"
bridge = "vmbr0"
firewall = "false"
}
# Configuración de Cloud-Init
cloud_init = true
cloud_init_storage_pool = "local-lvm"
# Comandos que Packer le inyecta al iniciar la imagen ISO
boot_command = [
"<esc><wait>",
"e<wait>",
"<down><down><down><end>",
"<bs><bs><bs><bs><wait>",
"autoinstall ds=nocloud-net\\;s=http://{{ .HTTPIP }}:{{ .HTTPPort }}/ ---<wait>",
"<f10><wait>"
]
boot = "c"
boot_wait = "5s"
# Configuración de Autoinstalación
http_directory = "http"
# (Opcional) Dirección IP y Puerto en caso que tengamos la configuración en un servidor remoto.
# http_bind_address = "0.0.0.0"
# http_port_min = 8802
# http_port_max = 8802
# Usuario SSH
ssh_username = "ubuntu"
# (Opcion 1) Agregar la password aqui
# ssh_password = ""
# - o -
# (Opcion 2: Recomendada) Añadir una llave SSH Privada here
ssh_private_key_file = "~/.ssh/packer"
# Si la instalación demora, podemos aumentar el tiempo de espera
ssh_timeout = "60m"
}
# Definición del Build para crear el Template de VM
build {
name = "ubuntu-server-jammy"
sources = ["source.proxmox.ubuntu-server-jammy"]
# Aprovisiona el Template de la VM para que Cloud-Init se integre con Proxmox #1
provisioner "shell" {
inline = [
"while [ ! -f /var/lib/cloud/instance/boot-finished ]; do echo 'Esperando por cloud-init...'; sleep 1; done",
"sudo rm /etc/ssh/ssh_host_*",
"sudo truncate -s 0 /etc/machine-id",
"sudo apt -y autoremove --purge",
"sudo apt -y clean",
"sudo apt -y autoclean",
"sudo cloud-init clean",
"sudo rm -f /etc/cloud/cloud.cfg.d/subiquity-disable-cloudinit-networking.cfg",
"sudo sync"
]
}
# Aprovisiona el Template de la VM para que Cloud-Init se integre con Proxmox #2
provisioner "file" {
source = "files/99-pve.cfg"
destination = "/tmp/99-pve.cfg"
}
# Aprovisiona el Template de la VM para que Cloud-Init se integre con Proxmox #3
provisioner "shell" {
inline = ["sudo cp /tmp/99-pve.cfg /etc/cloud/cloud.cfg.d/99-pve.cfg"]
}
# Añade scripts de aprovisionamiento adicionales aqui
# ...
}
Creamos una carpeta que se llame files
y en un archivo 99-pve.cfg colocamos lo siguiente:
datasource_list: [ConfigDrive, NoCloud]
La mayoría de los parámetros son bien fáciles de entender. La opción ssh_timeout
nos permitirá darle mas tiempo al instalador para descargar las actualizaciones de seguridad mientras se dure la configuración.
La sección Variables
contienen una lista de parámetros que vamos a necesitar para conectarnos al host de Proxmox y opcionalmente configurar algunas opciones de la MV.
Los valores sensibles de las variables (como usuario y token de Proxmox), los podemos guardar en un archivo aparte y luego llamarlos desde el build cuidando en borrar los valores de las variables en el campo default
. A este archivo lo podemos llamar ubuntu-2204.pkr.vars.hcl:
proxmox_api_url="https://direccionip:8006/api2/json"
proxmox_api_token_id="packer@pve!packer"
proxmox_api_token_secret="TOKEN_ID"
En la sección Source
definimos los parámetros de la MV como CPU, Red, Memoria, Disco, etc. como un todo para que después pueda ser tomado por el Builder.
En la sección Build
definimos que imagen vamos a crear para cual tecnologia/plataforma (AWS, Proxmox en nuestro caso, GCP, VMWare, etc) con los parámetros provenientes de la sección Source.
Ejecutamos entonces Packer agregando como argumento el archivo de variables con el siguiente comando:
packer build -var-file=ubuntu-2204.pkr.vars.hcl .
Packer iniciará un servidor HTTP cargando el directorio http
(obtenido desde el parámetro http_directory
) con el contenido de los archivos que cloud-init utilizará para configurar la imagen en el primer inicio.
El comando boot
le indicará a cloud-init que utilice el origen de datos nocloud-net para que sea capaz de cargar los archivos de user-data y meta-data desde el endpoint HTP. El parámetro adicional autoinstall
forzará a Packer a realizar tareas sin confirmación por parte del usuario.
Provisioner #
Nos permite configurar la imagen (instalar software, configurar servicios, etc.) aprovechando herramientas de automatización como Ansible, Puppet; o shell scripts. Cloud-init se encargará de todo esto. Sin embargo, Packer asumirá que el aprovisionamiento ha sido completado cuando sea capaz de conectarse a la VM via SSH. Pero para este momento, el proceso de configuración no está totalmente completado, por lo que Packer tendría que esperar hasta que cloud-init termine correctamente.
Tecnicamente hablando, la mejor solución es es validar que el archivo /var/lib/cloud/instance/boot-finished
esté presente en la VM, ya que es lo ultimo que cloud-init hace. Con un simple script bash podemos validar esto:
provisioner "shell" {
inline = [
"while [ ! -f /var/lib/cloud/instance/boot-finished ]; do echo 'Esperando por cloud-init...'; sleep 1; done",
Cloud-Init #
Subiquity usa cloud-init por lo que en otro directorio llamado http
creamos el archivo user-data
que nos aprovisionará la VM cuando clonemos el template y meta-data
en la cual contiene una serie de metadatos principalmente del servicio EC2 de AWS. En este caso lo podemos crear vacío ya que de no estarlo, cloud-init no iniciará:
#cloud-config
autoinstall:
version: 1
locale: es_ES
keyboard:
layout: es
ssh:
install-server: true
allow-pw: true
disable_root: true
ssh_quiet_keygen: true
allow_public_ssh_keys: true
packages:
- qemu-guest-agent
- sudo
storage:
layout:
name: direct
swap:
size: 0
user-data:
package_upgrade: false
timezone: UTC/utc
users:
- name: ubuntu
groups: [adm, sudo]
lock-passwd: false
sudo: ALL=(ALL) NOPASSWD:ALL
shell: /bin/bash
# passwd: password
# - or -
ssh_authorized_keys:
- llave ssh publica que se le inyectará a cada VM creada a partir del template para poder acceder a ella
http
├── meta-data
└── user-data
La documentación oficial contiene todos los parámetros disponibles que pueden ser configurados. El número es reducido en comparación con debian-installer pero Subiquity nos da la posibilidad de utilizar cualquier otro módulo de cloud-init para compensar.
Todos los módulos “nativos” de cloud-init deben estar bajo la clave user-data. Por ejemplo, si querríamos usar el modulo write_files
, se puede usar la siguiente configuración:
#cloud-config
autoinstall:
...
user-data:
write_files:
- path: /etc/crontab
content: 15 * * * * root ship_logs
append: true
...
Autoinstall #
Autoinstall es el responsable de configurar todos aquellos parámetros donde en una instalación tradicional nos haría en forma de pregunta (como el layout del teclado, idioma, paquetes de software, etc.)
#cloud-config
autoinstall:
version: 1
locale: es_ES
keyboard:
layout: es
ssh:
install-server: true
allow-pw: true
packages:
- qemu-guest-agent
El paquete qemu-guest-agent
es necesario para que Packer detecte la dirección IP de la MV para poder realizar la conexión SSH. Esto permite también a Proxmox de mostrar los recursos de la MV en la interfaz de usuario.
Identity #
Esta sección crea la cuenta de usuario durante el aprovisionamiento. También es responsable de configurar el nombre del host.
#cloud-config
autoinstall:
identity:
hostname: ubuntu-host
username: ubuntu
password: $6$xyz$1D0kz5pThgRWqxWw6JaZy.6FdkUCSRndc/PMtDr7hMK5mSw7ysChRdlbhkX83PBbNBpqXqef3sBkqGw3Rahs..
Para tener mayor flexibilidad sobre como al cuenta es creada, es posible utilizar el modulo users
:
#cloud-config
autoinstall:
...
user-data:
users:
- name: ubuntu
passwd: $6$xyz$1D0kz5pThgRWqxWw6JaZy.6FdkUCSRndc/PMtDr7hMK5mSw7ysChRdlbhkX83PBbNBpqXqef3sBkqGw3Rahs..
groups: [adm, cdrom, dip, plugdev, lxd, sudo]
lock-passwd: false
sudo: ALL=(ALL) NOPASSWD:ALL
shell: /bin/bash
ssh_authorized_keys:
- ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJEXrziuUOCpWPvwOsGuF4K+aq1ufToGMi4ra/1omOZb
Este módulo nos da más parámetros a configurar como: grupos, shell binary, SSH authorized keys… El parámetro sudo nos habilitaria a usar comandos administrativos sin necesidad de digitar una contraseña. Esto es super util después cuando, por ejemplo, Ansible complete la instalación de software desde el template.
Almacenamiento #
Para configurar el almacenamiento del template, se pueden utilizar dos parámetros: lvm
y direct
. Por defecto, Packer utiliza lvm con un volumen lógico de 4GB. El instalador no extiende la partición para usarla en toda la capacidad del volumen de grupo. También es posible de configurar un tamaño de swap en el sistema de archivos (si deseamos deshabilitarla con el parámetro 0
)
#cloud-config
autoinstall:
...
storage:
layout:
name: direct
swap:
size: 0
Si usamos direct
, podríamos crear una sola partición /dev/sda2 ocupando todo el disco:
Filesystem Size Used Avail Use% Mounted on
udev 1.9G 0 1.9G 0% /dev
tmpfs 394M 696K 393M 1% /run
/dev/sda2 20G 3.6G 15G 20% /
tmpfs 2.0G 0 2.0G 0% /dev/shm
tmpfs 5.0M 0 5.0M 0% /run/lock
tmpfs 2.0G 0 2.0G 0% /sys/fs/cgroup
Conclusión #
Packer es una excelente herramienta que nos brinda flexibilidad para crear nuestras propias imágenes base en nuestro Proxmox y de esta manera desplegar maquinas virtuales de manera uniforme y con una configuración preestablecida.
Espero les haya gustado y nos vemos en la próxima!