Ir al contenido
  1. Posts/

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

·2156 palabras·11 mins· loading · loading ·
Proxmox Packer Infraestructura proxmox packer infraestructura ubuntu
Autor
Enmanuel Moreira
Ingeniero DevOps de día y aprendiz de Barman en mis tiempos libres, con experiencia en Kubernetes, Cloud, y DevOps. También disfruta de hacer stream de juegos, hablar de CI/CD, desplegar en producción un viernes con Terraform y automatizar tareas aburridas con Ansible.

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 200.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:

DigitalOcean Referral Badge

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:

Creando Token
Creando token en Proxmox

Una vez que hagamos click en Add, nos mostrará por una unica vez el token, el cual deberemos reservar:

Proxmox Token
Proxmox Token

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:

Subiendo ISO de Ubuntu
Subiendo ISO de Ubuntu

ISO de Ubuntu en nuestro Proxmox
ISO de Ubuntu en nuestro Proxmox

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 autoinstallforzará 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!