Contenido

Vagrant y Puppet

Hay mucha gente que tiene que trabajar a diario con más de un ordenador. En ocasiones, tienen cientos. Las máquinas se estropean y hay que reemplazarlas, así que se pierde mucho tiempo en reinstalar el sistema. Incluso si sólo tienes una máquina, siempre se te olvida instalar algo que necesitarás cuando no tengas red.

Una solución es clonar discos, pero eso es poco flexible. Resulta complejo cambiar la versión de un programa en todas las máquinas.

¿Qué sería lo ideal? Pues poder tener la configuración de tu PC en un repositorio Git o Mercurial. Y poder probar la configuración en una máquina virtual.

Puppet

Antes que nada…

Antes de comenzar, una advertencia: Vamos a hacer cosas peligrosas. Eso significa que puede que se nos reinicie el ordenador o que perdamos datos.

Tras hacer esa advertencia, también os diré que todo será razonablemente seguro y que vamos a trabajar en máquinas virtuales para que no pase, pero no puedo garantizar nada.

Vagrant

Lo primero que necesitamos es un entorno para gestionar máquinas virtuales. Uno de los más utilizados hoy en día es VirtualBox . Pero nosotros no vamos a necesitar un entorno gráfico ni nada de eso.

VirtualBox tiene soporte para máquinas virtuales headless, es decir, máquinas que “están por ahí”, pero que no se ven.

Vagrant no es más que una interfaz a VirtualBox, pero que nos hace la vida más fácil.

Así que vamos a usar Vagrant. Llegada a esta conclusión, tendríamos dos opciones: instalar una máquina virtual desde cero o coger una ya instalada y partir de ahí. Si optáramos por la primera opción, tendríamos que ver VeeWee ; como este artículo va de Puppet, vamos a optar por la segunda opción.

Lo primero: vamos a instalarnos Vagrant:

1
$ sudo gem install vagrant

Sí, es Ruby.

Tras pelearme con algunas máquinas virtuales públicas, algunos reinicios y demás, os recomiendo que utilicéis la siguiente:

1
$ vagrant box add squeeze32 https://mathie-vagrant-boxes.s3.amazonaws.com/debian_squeeze_32.box

Eso tardará un poco, ya que tiene que descargarse 4Gb. Os recomiendo que no utilicéis la wifi. Básicamente, nos descargamos la imagen y la llamamos squeeze32. Eso es importante para referirnos a ella desde el Vagrantfile.

Mientras se descarga, vamos a crearnos una carpeta en alguna parte y a ir configurando Vagrant. El archivo de configuración que yo utilizo tiene el siguiente aspecto (debe llamarse Vagrantfile):

1
2
3
4
# file: Vagrantfile
Vagrant::Config.run do |config|
	config.vm.box = "squeeze32"
end

Difícil, ¿eh?

Vamos con el manejo básico de vagrant:

  • up levanta nuestra máquina virtual. Eso implica construirla si no existe y provisionarla.
  • destroy mata nuestra máquina virtual y la borra.
  • halt sólo mata nuestra máquina virtual. No lo uséis. Ya se morirá sola y, si vais a apagarla, borradla del todo con destroy. ¡Es más divertido!
  • provision Hará que se ejecute Puppet (cuando lo tengamos configurado). Esto es lo que se llama “provisionar”.
  • status Muestra las máquinas virtuales existentes y el estado en el que se encuentran.
  • ssh Os conectará por ssh a la máquina virtual.

Habría que contar algo sobre usar varias máquinas virtuales y tal, pero esto es un artículo introductorio. Para cosas avanzadas, buscad otra cosa o la propia documentación :D

Vamos con Puppet.

Puppet

Este artículo va a ser introductorio, así que me centraré en la estructura básica, la configuración y la instalación de un único paquete sencillo.

Estructura básica de un proyecto

La estructura básica es:

1
2
3
4
5
6
7
8
9
.
├── manifests
│   └── desktop.pp
├── modules
│   └── module
│       ├── files
│       ├── templates
│       └── manifests
└── Vagrantfile

El archivo que se encuentra en @manifests/desktop.pp@ va a ser el que defina nuestra máquina. Aquí hacemos un breve paréntesis.

Puppet está pensado para utilizarse en grandes empresas, con cientos o miles de máquinas. Algunas de estas máquinas serán iguales, pero otras serán distintas. Por eso tiene una estructura cliente-servidor segura, mediante claves ssl.

Yo lo estoy contando para un entorno casero, así que olvidaos de eso. Vamos a tener un archivo distinto por cada máquina y no voy a contar nada de nodes. Tan sólo nuestra máquinita y ya. Como tengo un ordenador de escritorio y el que utilizo para viajar, tengo un archivito “desktop.pp”. Quizá algún día tenga un “laptop.pp” :D

Configuración

La idea es mantener el archivo @desktop.pp@ lo más sencillo posible, de manera que podamos reutilizar su información en otras máquinas. Por eso el mío es así:

1
2
3
4
5
6
# file: manifests/desktop.pp
stage { [apt, pre, post]: }
Stage[apt] -> Stage[pre] -> Stage[main] -> Stage[post]

class { 'apt': stage => apt }
class { 'kernel': }

Lo que hago es definirme 3 etapas: apt, pre y post; después establezco el orden de las etapas, relativas a main. Puppet no tiene orden de ejecución, pero hay cosas que hay que hacer antes que nada. Yo, por ejemplo, quiero que se me actualice APT antes de comenzar a trabajar.

A continuación declaro las clases a utilizar. Queda claro que quiero la clase “apt” que se ejecutará en la etapa “apt” (derrocho imaginación).

Finalmente, incluyo también la clase “kernel”, que me instalará el núcleo. Evidentemente, para usarlo tendré que reiniciar, pero me hace ilusión tener instalada la última versión.

Módulos

Ahora hay que definir los módulos. Comenzaremos por APT. la definición está en @modules/apt/manifests/init.pp@. Siempre, los manifiestos se llamarán init.pp y estarán en la carpeta manifests, dentro de nuestro módulo. Este archivo contendrá:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# file: modules/apt/manifests/init.pp
class apt {
  file { 'sources.list':
    path => '/etc/apt/sources.list',
    source => 'puppet:///modules/apt/sources.list',
    owner => 'root',
    replace => true,
  }

  exec { 'update':
    path => '/usr/bin',
    command => 'apt-get update',
    require => File['sources.list'],
  }
}

Esto es lo más dificil que vamos a hacer y lo más sencillo de explicar. Como véis, el módulo APT necesita un fichero de configuración y ejecutar una orden. El fichero será el @sources.list@, claro :D

No hay mucho que contar, tan solo que sobreescribo el @sources.list@ y después lanzo un update. Estos requisitos se ejecutarán sin orden fijo, así que tengo que forzar un orden mediante la orden require.

Claro, el archivo que quiero instalar tiene que estar en el directorio files (mirad la estructura más arriba). Y tendrá algo como esto:

1
2
# file: modules/apt/files/sources.list
deb https://ftp.es.debian.org/debian unstable main contrib non-free

Ahora vamos a configurar el módulo del kernel:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# file: modules/kernel/manifests/init.pp
class kernel {
  package { 'linux-image-amd64':
    ensure => latest,
  }

  package { 'linux-headers-amd64':
    ensure => latest,
  }
}

Como véis, es una clase que necesitará dos paquetes: el kernel y las headers. Y me aseguro de tener instalada la última versión.

Lanzándolo

Bueno, ya lo tenemos todo configurado, así que hay que decirle a Vagrant que lance a Puppet:

1
2
3
4
5
6
7
8
9
# file: Vagrantfile
Vagrant::Config.run do |config|
	config.vm.box = "squeeze32"
	config.vm.provision :puppet do |puppet|
		puppet.manifests_path = "manifests"
		puppet.module_path = "modules"
		puppet.manifest_file = "desktop.pp"
	end
end

Bueeeeeeeeeeeno. Pues ya está todo. Vamos con la magia:

1
$ vagrant up

Y flipad con los mensajes de pantalla. Nos dejará una máquina perfectamente configurada:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
$ vagrant up
[default] Importing base box 'squeeze32'...
[default] The guest additions on this VM do not match the install version of
VirtualBox! This may cause things such as forwarded ports, shared
folders, and more to not work properly. If any of those things fail on
this machine, please update the guest additions and repackage the
box.

Guest Additions Version: 4.0.4
VirtualBox Version: 4.1.14
[default] Matching MAC address for NAT networking...
[default] Clearing any previously set forwarded ports...
[default] Forwarding ports...
[default] -- 22 => 2222 (adapter 1)
[default] Creating shared folders metadata...
[default] Clearing any previously set network interfaces...
[default] Booting VM...
[default] Waiting for VM to boot. This can take a few minutes.
[default] VM booted and ready for use!
[default] Mounting shared folders...
[default] -- v-root: /vagrant
[default] -- manifests: /tmp/vagrant-puppet/manifests
[default] -- v-pp-m0: /tmp/vagrant-puppet/modules-0
[default] Running provisioner: Vagrant::Provisioners::Puppet...
[default] Running Puppet with /tmp/vagrant-puppet/manifests/desktop.pp...
stdin: is not a tty
notice: /Stage[apt]/Apt/File[sources.list]/content: content changed '{md5}c6f69c7769cbbd3b3c938217487efa02' to '{md5}509cf608fd519f230246df247418b7b2'

notice: /Stage[apt]/Apt/File[sources.list]/group: group changed 'root' to 'vagrant'

notice: /Stage[apt]/Apt/Exec[update]/returns: executed successfully

notice: /Stage[main]/Kernel/Package[linux-image-amd64]/ensure: ensure changed 'purged' to 'latest'

notice: /Stage[main]/Kernel/Package[linux-headers-amd64]/ensure: ensure changed 'purged' to 'latest'
$

Aplicándolo a nuestra máquina

NOTA: Mucho ojo. Si seguís estos pasos, es perfectamente posible que rompáis vuestra máquina. ¡Sobreescribiríais el @sources.list@!

Como todas las operaciones que realizamos requieren ser root, si lo ejecutamos como usuarios normales no debería ocurrir nada, pero insisto una vez más: si ejecutas esto es bajo tu cuenta y riesgo:

1
# puppet apply manifests/desktop.pp --modulepath=modules/

Poco que contar: le pedimos a puppet que aplique nuestra configuración y le indicamos dónde están los módulos.

Ventajas

El uso de Vagrant sólo nos da una ventaja: la de poder probar nuestra configuración antes de aplicarla en una máquina real.

El uso de Puppet es otro cantar:

  • podemos compartir configuraciones entre máquinas,
  • podemos replicar configuraciones,
  • podemos guardar las configuraciones en un repositorio,
  • podemos volver a una configuración determinada,
  • si identificamos un nuevo tipo de máquina, podemos basarnos en una ya existente,
  • si se rompe una máquina, podemos tener otra operativa en poco tiempo,
  • no olvidaremos instalar nada,

Aunque tiene algunos…

Inconvenientes

Requiere un poquitín de trabajo extra. Hay que modificar los archivos y después lanzar puppet, en lugar de tocar los archivos originales o ejecutar APT a mano.

Además, pueden darse situaciones poco agradables, como que un paquete entre en conflicto con otro. Como Puppet no garantiza ningún orden, puede darse que el primero se instale y el segundo desinstale al primero. Como, de forma individual, no hay errores, Puppet no nos informará del problema.

Hay algunas soluciones, como realizar pruebas. Existen distintos entornos de pruebas, pero se basan en comprobar que la sintaxis es la adecuada y, en mi opinión, es lo mismo que tener redundancia: se especifican las reglas de dos maneras distintas y deben coincidir. Personalmente, no me gusta el sistema y no le veo un beneficio.

Nuevamente, en mi opinión, lo más útil es definir una nueva etapa de pruebas que realice pruebas por los efectos, no por los requisitos: si queremos que un servidor esté corriendo, comprobamos que el puerto está ocupado. Si hemos modificado un archivo, podemos comprobar su fecha de modificación. Pero no hay ningún framework que nos facilite esta tarea.

Más información

Podéis visitar la propia web de Puppet . Está todo muy bien explicadito, salvo la estructura inicial (para lo que ya estoy yo XD).

En este artículo he introducido los tipos file", “package” y “exec”; podéis visitar la “lista completa de tipos puppet si queréis.

Así mismo, he comentado las etapas, pero hay muchos otros metaparámetros.

También tenéis la página de Vagrant, claro.

Si os gustan estos temas, os recomiendo que sigáis a Carlos Sánchez, que presentó en el CodeMotion la charla From Dev to DevOps.

Podréis encontrar más información si buscáis la palabra “devops” en google :D