LAMP con Salt


De la misma manera que hace unos meses conté cómo crear un entorno LAMP con Puppet, en esta ocasión haremos lo mismo con Salt, también conocido como SaltStack:

  • crear una máquina virtual con Vagrant
  • configurar Salt
  • instalar todo un entorno LAMP (Linux + Apache + MySQL + Php)
  • descargarnos la web de un repositorio remoto Git
  • servir su contenido desde el servidor Apache.

Y, nuevamente, todo en unos 10 minutos.

Este artículo va por David P., que me habló de Salt, José Antonio, que me preguntó por twitter qué sistema es el mejor, y por @ricbartm, con quien estube hablando este viernes sobre Salt ;)

Jerarquía

Para facilitaros la vida, ésta es la jerarquía de archivos que vamos a usar:

.
├── salt
│   ├── minion
│   └── roots
│       └── salt
│           ├── apache
│           │   └── init.sls
│           ├── logrotate
│           │   └── init.sls
│           ├── mysql
│           │   ├── client.sls
│           │   └── server.sls
│           ├── ntp
│           │   └── init.sls
│           ├── php
│           │   └── init.sls
│           ├── php-example
│           │   └── init.sls
│           └── top.sls
└── Vagrantfile

Como vereis, voy a instalar también ntp y logrotate, con el fin de hacer un ejemplo exactamente igual que en el artículo de LAMP con Puppet.

Lectura rápida

Resulta que el artículo me ha salido más largo de lo esperado, así que he creado un repositorio Git con los archivos de este artículo.

Sé que muchos de mis lectores agradecen artículos pequeños, así que podéis usar el repositorio y saltar directamente a la sección "Al lío". Si queréis ver en detalle alguno de los archivos, podéis ir a su sección más adelante, si lo creéis necesario.

Vagrant

Éste es el contenido del archivo Vagrantfile:

# -*- coding:utf-8; tab-width:4; mode:ruby -*-
# vi: set ft=ruby :

Vagrant.configure("2") do |config|

  config.vm.define :saltexample do |vm_config|
    vm_config.vm.box = 'wheeze64'
    vm_config.vm.box_url =  'http://puppet-vagrant-boxes.puppetlabs.com/debian-70rc1-x64-vbox4210.box'
    vm_config.vm.host_name = 'salt-example'

    vm_config.vm.synced_folder "salt/roots/salt", "/srv/salt"
    vm_config.vm.synced_folder "salt/roots/pillar", "/srv/pillar"

    vm_config.vm.network :forwarded_port, host: 10080, guest: 80

    vm_config.vm.provision :salt do |salt|
      salt.minion_config = "salt/minion"
      salt.run_highstate = true
    end
  end

end

Es similar a lo contado en la receta de LAMP con Puppet, y lo explicaré igual de rápido.

Estoy creando una máquina virtual llamada "wheeze64" y que me la voy a descargar de esa URL. Si ya no es válida, podéis usar cualquier otra máquina virtual de la lista. Además la he bautizado como "salt-example".

Después me aseguro de que voy a poder acceder a mi servidor web, que estará escuchando en el puerto 80 de la VM, y que estará mapeado con el puerto 10080 de la máquina host.

En las dos últimas líneas configuro el provisionamiento mediante Salt.

Salt

El resto de archivos pertenecen a Salt. Pero antes me gustaría aclarar algunos conceptos.

  • minion es cada una de las máquinas que están bajo el control de Salt.
  • Los pillars son configuraciones básicas, los hechos (facts en terminología Puppet).
  • Los states son los estados en los que debe quedar la máquina.
  • Un archivo sls es un Salt State, y equivale vagamente a un módulo de Puppet.
  • Yaml es un lenguaje de serialización legible por humanos. Os invito a profundizar un poco más en él si os decidís por Salt.
  • jinja2 es un lenguaje de plantillas sencillo y potente, muy típico de entornos Python. También deberíais investigarlo.

Se pueden contar muchas más cosas, pero dejémoslo aquí. Es suficiente para una primera toma de contacto.

Ahora describiré cada uno de los archivos. Son pequeños y sencillos. Estos archivos son plantillas jinja2 que generarán un archivo Yaml, que es la propia configuración. Parece complicado, pero veremos que no lo es tanto.

El primer archivo a tener en cuenta es salt/minion, donde está la configuración genérica:

file_client: local

Este archivo contiene configuración básica. En este caso se indica que la configuración es local (no hay estructura cliente-servidor).

El siguiente archivo importante es salt/roots/salt/top.sls, que describe cada uno de los minion:

base:
  'salt-example':
    - ntp
    - mysql.client
    - mysql.server
    - apache
    - php
    - php-example
    - logrotate

Aquí se han indicado cada uno de los módulos que se van a instalar en la máquina salt-example.

logrotate

comencemos por uno de los módulos más sencillos, que se encuentra en salt/roots/salt/logrotate/init.sls. Lo primero, aclarar que el archivo init.sls es el que se buscará por defecto dentro del módulo. Luego veremos cómo utilizar otros. Éste es el contenido:

logrotate:
  pkg:
    - installed

Realmente estamos seteando propiedades, tales como logrotate.pkg.installed = True. Estas simples instrucciones crean un estado: el paquete logrotate (cogido del nombre del módulo) debe estar instalado.

ntp

Vamos con algo un poco más difícil, ya que contiene un servicio y el paquete debian no se llama igual que el módulo. El archivo es salt/roots/salt/ntp/init.sls:

ntp:
  pkg:
    - installed
    - name: ntpdate
  service:
    - running
    - watch:
      - pkg: ntpdate

En este caso, el estado creado es: el paquete ntp cuyo paquete debian se llama ntpdate debe estar instalado. Además, hay un servicio que debe estar running y se debe reiniciar cada vez que cambie el paquete ntpdate.

Veremos que este mismo esquema se repite para cada uno de nuestros módulos.

php

Archivo: salt/roots/salt/php/init.sls

php5:
  pkg:
    - installed

php5-mysql:
  pkg:
    - installed

/etc/php5/apache2/php.ini:
  file.replace:
    - pattern: ';\s*extension=msql.so'
    - repl: 'extension=msql.so'

  require:
    - pkg: php5-mysql

La primera parte ya la hemos visto: se asegura de que tanto los paquetes php5 como php5-mysql estén instalados. El último apartado lo que hace es asegurarse de que esté habilitada la extensión de mysql en apache. Para ello reemplaza la extensión comentada por la no comentada. Es un poco tricky, pero funcional.

apache

Vamos con el archivo salt/roots/salt/apache/init.sls:

apache:
  pkg:
    - name: 'apache2'
    - installed
  service:
    - name: 'apache2'
    - running
    - watch:
      - pkg: apache2

libapache2-mod-php5:
  pkg:
    - installed

  require:
    - pkg: apache2

Que no contiene nada que no hayamos visto ya.

mysql

He dividido mysql en dos partes: una que instalará el cliente y otra el servidor. Por eso el módulo no tiene el archivo init.sls, sino dos:

mysql.client

El archivo salt/roots/salt/mysql/client.sls:

mysql-client:
  pkg:
    - installed

Se asegura de que el cliente mysql esté instalado.

mysql.server

El archivo salt/roots/salt/mysql/server.sls:

mysql-server:
  pkg:
    - installed
    - pkgs:
      - mysql-server

  service:
    - running
    - name: mysql
    - enable: True
    - require:
      - pkg: mysql-server

# Taken from https://github.com/saltstack/salt/issues/5918
set-mysql-root-password:
  cmd.run:
  - name: 'echo "update user set password=PASSWORD(''secret'') where User=''root'';flush privileges;" | /usr/bin/env HOME=/ mysql -uroot mysql'
  - onlyif: 'echo | /usr/bin/env HOME=/ mysql -u root'
  - require:
    - service: mysql-server
    - pkg: mysql-client

Es, quizá, el módulo más complejo de todos. La primera parte ya la hemos visto. La sección set-mysql-root-password lo que hace es establecer la password de root del servidor de mysql. Es muy tricky, pero no he encontrado nada más sencillo.

php-example

Finalmente, descargamos nuestra página web. Es la misma que usé en el artículo LAMP con Puppet (archivo salt/roots/salt/mysql/client.sls):

https://github.com/magmax/small_php_example.git:
  git.latest:
    - target: /var/www/example

Debo decir que esto es lo que me parece más impresionante de todo el artículo: la sencillez para trabajar con Git. Estas tres líneas se descargan el repositorio y comprueban que esté actualizado. Así, sin más.

Al lío

Bien, ahora es cuando lanzamos todo:

$ vagrant up example

Y de nuevo podéis conectaros a http://localhost:10080/example, donde os está esperando la lista de tablas de la base de datos mysql, servida con PHP.

¡Bug! ¡Bug!

Supongo que lo corregirán, pero debo advertiros de un bug actual: El provisionamiento Salt en Vagrant está un poco verde, y no os mostrará el resultado, ni cuando sea un error. Esto me trajo de cabeza bastante tiempo. La solución es provisionar desde la propia máquina:

salt-example# salt-call --local state.highstate -l info

Conclusiones

No puedo evitar comparar Salt con Puppet. Al fin y al cabo he estado usando Puppet desde hace más de dos años. La evolución que he visto en Puppet ha sido desde módulos totalmente específicos de nuestra empresa a módulos genéricos que se pueden cargar con librarian-puppet, posibilitando configurar hiera y, así, configurarlo todo mediante Yaml (¿os suena?).

Hay un montón de módulos para Puppet. De hecho, hay proyectos para crear módulos para Puppet, como example42. En Salt hay muchísimos menos, pero la verdad es que no le hacen falta. Se han centrado en lo importante, aunque sí hecho de menos algunas cosas, como configurar vhosts de Apache directamente por configuración, en lugar de tener que proporcionar archivos. Pero supongo que estos detalles ya se irán completando.

También hay que ver otra diferencia: Ruby vs Python. Como bien saben todos los lectores del blog, yo soy más de Python. De todas maneras, en Puppet se utiliza un DSL bastante interesante, y se ofrece la posibilidad de crear los módulos en Ruby. En Salt, que yo sepa, se crean en Python. Pero me gustaría que juzgáseis vosotros mismos, comparando dos módulos similares: el módulo de Git para Puppet y el módulo de Git para Salt. He elegido éste porque es bastante similar.

Hay otra diferencia más. Quizá la más importante.

En Salt construyeron un sistema para comunicar máquinas y ejecutar comandos simultáneamente en todas ellas. Una especie de shell múltiple remota. Sobre esta arquitectura construyeron el equivalente a Puppet que permite hacer lo que se cuenta en este artículo. Esto permite algo que en Puppet han tenido que implementar aparte, que sería MCollective.

Siento no poder comparar cosas como la velocidad, eficiencia, memoria, seguridad, ... En mi opinión no hay una gran diferencia en nada de todo esto, pero no tengo datos. Sinceramente creo que no es lo más importante, ya que ambos necesitarán realizar muchas operaciones de disco para comprobar el estado actual de la máquina, y probablemente esta operación sea similar y se lleve la mayor parte del tiempo y eficiencia.

Personalmente he decidido usarlo para mis máquinas en casa. La decisión viene por dos razones: porque en la empresa ya estoy usando Puppet y así aprendo algo nuevo, y porque es Python. Espero no equivocarme.


Comentarios

Comments powered by Disqus