Skip to main content

Modelo

No se abordarán en este estudio modelos en JSON, solamente HCL2 que es el recomendado por HashiCorp.

Packer utiliza HashiCorp Configuration Language - HCL - diseñado para permitir descripciones concisas de los pasos necesarios para obtener un archivo de compilación.

Una breve explicación sobre el modelo HCL2 https://developer.hashicorp.com/packer/guides/hcl

Todo puede definirse dentro de un único archivo, pero es interesante separar los archivos para una mejor organización.

  • variables.pkr.hcl
  • packer.pkr.hcl
  • build1.pkr.hcl
  • build2.pkr.hcl

Tipos de bloque

El lenguaje Packer - HCL2 incluye varios bloques integrados que puede usar para configurar compilaciones. Un bloque es un contenedor para configuración.

Los bloques más importantes pueden dividirse en algunos tipos principales:

  • build los bloques contienen configuración para una combinación específica de constructores, provisionadores y post-procesadores usados para crear un artefacto de imagen específico.

  • source los bloques contienen configuración para plugins del constructor. Una vez definidas, las fuentes pueden ser usadas y configuradas posteriormente por el bloque "build".

  • provisioners bloques contienen configuración para plugins del provisionador. Estos bloques están anidados dentro de un bloque de construcción.

  • post-processors los bloques de post-procesadores contienen configuración para plugins de post-procesamiento y secuencias de plugins de post-procesamiento. También están anidados dentro de bloques build.

  • variable los bloques contienen configuración para variables que pueden tener valores por defecto en la configuración o ser definidas por el usuario en tiempo de ejecución.

  • locals los bloques contienen configuración para variables que pueden ser creadas usando funciones HCL o fuentes de datos, o compuestas de variables creadas en los bloques de variables.

  • packer proporciona información al núcleo de Packer sobre qué versión puede ejecutar. El bloque "required_plugins" ayuda al núcleo de Packer

Variable

Un ejemplo del bloque variable podría estar definido dentro de un archivo variables.pkr.hcl.

Como buena práctica siempre tenga la descripción.

variable "project_name" {
type = string
description = "Nombre del Proyecto"
}

variable "region" {
type = string
default = "us-east-1"
description = "Región para crear el source en AWS"
sensitive = false
# Cuando una variable es sensitive todos los valores string de esa variable serán
# ofuscados de la salida de Packer.
}

Locals

Archivo utilizado para manipular variables internamente con interpolación o incluso establecer una variable por función. No es obligatorio, pero es una cuestión de organización.

Particularmente prefiero declarar un archivo separado para locals llamado locals.pkr.hcl

locals {
timestamp = regex_replace(timestamp(), "[- TZ:]", "")
}

# Use el bloque local singular si necesita marcar un local como sensible
local "mylocal" {
expression = "${var.secret_api_key}"
sensitive = true
}

Packer

Este único bloque se utiliza para configurar algunos comportamientos del propio Packer, como la versión mínima necesaria de Packer necesaria para aplicar su configuración.

Es una buena práctica siempre definir una versión mínima.

Me gusta aislar este bloque en packer.pkr.hcl

packer {
required_plugins {
happycloud = {
version = ">= 2.7.0"
source = "github.com/hashicorp/happycloud"
}
}
required_version = ">= 1.8.5"
}

Data

Este bloque define fuente de datos como por ejemplo buscar algún recurso en la nube para ser referenciado durante el proceso de build

Me gusta mantener estos bloques también separados en data.pkr.hcl cuando son necesarios


Los dos bloques siguientes me gusta mantenerlos en el mismo archivo que sería el main.pkr.hcl

Source

Los bloques sources definen la configuración del constructor, pero es en el bloque build donde se instancian. Resumiendo, creará la base para que ocurra el build. Si estamos hablando de crear un AMI basado en otro, este bloque declarará cuál debe ser la configuración de una EC2 cuando el build se instancie.

Cada source debe ser único con el nombre, pero pueden tener los mismos tipos.

## amazon-ebs es el tipo y ec2main es el nombre para ser referenciado más tarde por el bloque build

source "amazon-ebs" "ec2main" {

ami_name = "${var.project_name}-${local.timestamp}"
ami_regions = var.ami_regions
instance_type = var.instance_type
region = var.region
source_ami_filter {
filters = {
name = var.image_name
virtualization-type = var.virtualization
root-device-type = "ebs"
}
owners = ["${var.image_owner}"]
most_recent = true
}
ssh_username = var.ssh_username
ssh_timeout = var.ssh_timeout
ssh_keep_alive_interval = var.ssh_keep_alive_interval
ssh_pty = var.ssh_pty
}

Cada tipo de source tiene su propia configuración. Es necesario estudiar cada uno de ellos cuando sea necesario usarlos.

Este bloque podría estar todo declarado dentro de build, pero no queda organizado. Sin embargo, podríamos solamente reescribir algunos valores si es necesario. La ventaja de esto es inclusive el reaprovechamiento de código. Por eso me gusta mantenerlo en el mismo archivo.

Build

Es el bloque principal y solo con este bloque podría conseguirse una compilación directa en Packer si todas las variables estuvieran establecidas o ningún plugin extra fuera necesario.

Este bloque define qué constructores se inician, cómo hacer el provisionamiento y si es necesario qué hacer con el artefacto generado.


build {
# Name si se establece dará nombre al log, pero no es obligatorio
name = "buildname"

# Lista de sources que será utilizado sin renombrar ningún atributo
sources = [
"source.amazon-ebs.ec2main",
]

# Si hay algún source que se va a utilizar y se quiere renombrar algo, debe separarse este y hacer el cambio
source "source.amazon-ebs.ec2secondary" {
output = "different value"
name = "differentname"
}

# Existen diferentes provisioners que es lo que de hecho ejecutará para personalizar la imagen.

provisioner "shell" {
scripts = fileset(".", "scripts/{install,secure}.sh")
}

# Si se quiere hacer algo con el artefacto utilizar el post-processor
post-processor "shell-local" {
inline = ["echo Hello World from ${source.type}.${source.name}"]
}
}

Pueden definirse varios provisioners y varios post-processors

Lectura extra

Vale la pena una lectura rápida en Expresiones y sintaxis