Packer es un programa utilizado para automatizar la creación de golden images o machine images.
El objetivo de este post es enumerar algunos trucos para facilitar la creación de los scripts utilizados para generar las imagenes.
Casos de ejemplos
Hay veces que conviene comenzar con un template funcionando y ajustarlo que tener que realizar el template de cero.
Como ejemplo de buenos repositorios de template se tiene
- bento: contiene varias distribuciones y versiones de estas, lo que lo convierte en un buen punto de partida.
Hay que notar que varias veces las imagenes de los cd’s de instalación contienen errores y deben utilizarse hacks para poder crear las imagenes con packer. Un ejemplo de esto es el issue 3521: Preseed and Bypass Try/Install screen in ubuntu 14.04, 16.04
Uso de yaml
Por defecto packer utiliza archivos json para los archivos de configuración (templates). La contra más grande del formato json es que no permite realizar comentarios mientras se está creando la plantilla o comentar varias lineas mientras es necesario realizar una depuración en la configuración.
Una alternativa a utilizar json es utilizar el formato yaml,
convirtiendo luego el template a formato json para su uso por packer.
Para ello se puede utilizar el siguiente snippet en debian, instalando
previamente el paquete python3-yaml
(sudo apt-get install -Vy python3-yaml
):
def convert(in_, out):
json.dump(yaml.load(in_), out, sort_keys=True, indent=4)
El script completo se puede encontrar en el archivo yaml2json.
Nota: el soporte de yaml para templates en packer ya fué rechazado.
Separación en etapas
Dado que crear una imagen de una vez no es sencillo, conviene separar la creación en varias etapas.
Creación del sistema base vs Customización
La separación que me ha resultado más útil es entre la instalación del sistema base (mínimo) utilizando el cd de instalación y la posterior customización del sistema. Para ello la primer etapa utiliza un archivo .iso (el instalador) y en la segunda se utiliza la imagen de la vm exportada. En el caso de utilizar VirtualBox:
-
etapa 1:
builders: - type: virtualbox-iso format: ova iso_urls: - ./debian-9.9.0-amd64-netinst.iso - https://cdimage.debian.org/mirror/cdimage/release/9.9.0/amd64/iso-cd/debian-9.9.0-amd64-netinst.iso - https://cdimage.debian.org/mirror/cdimage/archive/9.9.0/amd64/iso-cd/debian-9.9.0-amd64-netinst.iso iso_checksum_type: sha512 iso_checksum: 42d9818abc4a08681dc0638f07e7aeb35d0c44646ab1e5b05a31a71d76c99da52b6192db9a3e852171ac78c2ba6b110b337c0b562c7be3d32e86a105023a6a0c vm_name: debian-base output_directory : output/stage1
NOTA: este paso debe instalar el servidor ssh para que sea posible conectarse luego a la máquina virtual y continuar su configuración.
-
etapa 2:
builders: - type: virtualbox-ovf format: ovf source_path: output/stage1/debian-base.ova vm_name: debian-customized output_directory : output/stage2
Instalación de paquetes vs Customización de paquetes
Otra separación que me ha dado resultado es cuando se debe configurar un paquete cuya instalación posee muchas dependencias que deben ser instaladas. En estos casos la descarga e instalación de paquetes insume un tiempo importante y es posible que ocurran fallas al momento de realizar la configuración mediante scripts, por lo cual conviene separar la instalación del paquete de su configuración. Como ejemplo se puede tener la instalación y configuración del display manager:
-
etapa instalación:
provisioners: - type: shell script: scripts/install-lightdm.sh
-
etapa configuración:
provisioners: - type: shell script: scripts/configure-lightdm.sh
Luego, cuando ya esté funcionando el script de configuración se pueden unir las etapas anteriores:
provisioners:
- type: shell
scripts:
- scripts/install-lightdm.sh
- scripts/configure-lightdm.sh
Uso de makefiles
Para evitar crear la imagen de cero cuando hay algún paso en una etapa
intermedia es útil utilizar make
para calcular las dependencias.
Un ejemplo del contenido del archivo Makefile
es el siguiente:
all: stage2
# aliases
stage1: output/stage1/debian-base.ova
stage2: output/stage2/debian-customized-disk001.vmdk
# dependencies
output/stage1/debian-base.ova: $(shell ./calculate-dependencies stage1.yaml)
output/stage2/debian-customized-disk001.vmdk: $(shell ./calculate-dependencies stage2.yaml)
# rules
output/stage1/debian-base.ova:
output/stage2/debian-customized-disk001.vmdk:
packer build -force $<
%.json: %.yaml
@./yaml2json < $< > $@
clean:
rm -fr stage?.json output
.PHONY: clean
En el archivo anterior la pieza fundamental es el script
calculate-dependencies, el cual obtiene las
dependencias del archivo yaml y las devuelve en la salida estandar, para ser
utilizada por el makefile. (Notar que el script anterior debe devolver como
primer elemento el nombre del archivo pasado por parámetro con extensión
.json
para que se pueda utilizar correctamente la regla packer build -force $<
.)
Dicho script funciona cargando el archivo yaml y obteniendo los archivos listados en los elementos (por ahora): virtualbox-iso, virtualbox-ovf, file, shell. No se tiene en cuenta el borrado de archivos, pero es útil en caso de modificaciones.