Actualmente, administrar un servidor es un proceso relativamente sencillo, solo se modifican algunos archivos de configuración, se ejecutan algunos comandos, y listo, no presenta mayor problema. Con dos servidores es un caso muy similar aunque, comienzan a cambiar las cosas ya que los archivos de configuración difieren ligeramente, y ahora se tienen que ejecutar los comandos en los 2 servidores.
El problema real comienza cuando en lugar de uno o dos servidores se administran 10 o mas, y si además de eso agregamos virtualización en cada uno de los servidores, seguramente se volvería una labor muy demandante, repetitiva y aburrida el mantener actualizadas y sincronizadas las configuraciones en dichos equipos.
Afortunadamente, para este tipo de tareas contamos con herramientas de automatización y control de configuraciones, que nos permiten mantener centralizada la configuración de los equipos, facilitando la administración de uno, dos, 10 o muchos mas servidores.
Introducción
El presente articulo tiene como objetivo describir el proceso de instalación, configuración y puesta en marcha de un servidor Puppet bajo Linux dentro de Amazon Web Services y usando Nginx como servidor web, así como varios servidores FreeBSD que funcionaran como nodos.
Puppet Labs define Puppet en su pagina como "Una herramienta open-source de siguiente generación para la automatización de servidores". Cuenta con un lenguaje declarativo que sirve para expresar la configuración del sistema, un esquema cliente-servidor para la distribución, y una librería para ejecutar dicha configuración.
Así como Puppet existen múltiples herramientas de control de configuraciones, se puede observar una tabla comparativa y con mayor información en la siguiente dirección:
http://en.wikipedia.org/wiki/Comparison_of_open_source_configuration_management_software
Para la implementación del esquema cliente-servidor, Puppet se apoya de un servidor web, por defecto utiliza Webrick, sin embargo, no será utilizado ya que como bien dice en la documentación, es un servidor únicamente de prueba y no se recomienda para producción.
En este documento utilizaremos Nginx + Passenger puesto que ofrecen mejor desempeño y aprovechamiento de recursos comparados con Apache + Passenger.
Esquema de funcionamiento
Para nuestra implementación utilizaremos los siguientes recursos:
- Servidor maestro en Amazon en una instancia EC2 Micro con Amazon Linux de 64 Bits
- Los clientes serán equipos con FreeBSD 8.x.
- En ambos casos la versión de Puppet a utilizar es la 2.7.x con Ruby 1.8.x.
Instalación
Una vez "lanzada" la instancia, solo hay que descargar la llave pública, cambiarle los permisos a 0600 y conectarse al servidor vía ssh utilizando las instrucciones correspondientes (aparecen al dar "Connect" en la instancia).
Una vez dentro del servidor y para comodidad de uno mismo, lo primero que haremos será asignarle un password a root y subir a ese nivel.
La instancia por defecto cuenta con muy pocos paquetes y dichos paquetes son versiones desactualizadas, por lo cual el siguiente paso será actualizar el sistema e instalar las dependencias necesarias.
yum upgrade yum install gcc yum install gcc-c++ yum install make yum install libcurl-devel yum install openssl-devel yum install facter yum install ruby-augeas yum install ruby-devel yum install rubygems yum install rubygem-rake gem install rack
El siguiente paso es instalar Nginx con el módulo de Passenger:
wget http://nginx.org/download/nginx-1.0.6.tar.gz tar xzf nginx-1.0.6.tar.gz /usr/bin/gem install passenger passenger-install-nginx-module
Al instalar passenger usando el script de instalación, hay que tener a la mano los siguientes valores ya que los solicitara el script:
Método de instalación: 2
Ruta absoluta del código fuente de nginx: /root/nginx-1.0.x
Prefijo de instalación: /usr/local (a la BSD)
Parámetros adicionales: --with-http_realip_module
Al finalizar las preguntas, el script de instalación realizará todas las actividades necesarias para compilar, instalar y comenzar a utilizar Nginx con soporte Ruby on Rails. El archivo de configuración final se guardará en la siguiente ruta:
/usr/local/conf/nginx.conf
El último paso es instalar Puppet, en este caso lo haremos desde código fuente para asegurarnos de que es la versión adecuada. Algunas versiones generan problemas con diferentes versiones de Ruby.
wget http://puppetlabs.com/downloads/puppet/puppet-2.7.3.tar.gz tar xzf puppet-2.7.3.tar.gz && cd puppet-2.7.3 ruby install.rb
Con esto hemos terminado la instalación del servidor, el siguiente paso es la configuración.
Configuración de Puppet
Antes de configurar Nginx es necesario preparar e incializar el entorno Puppet.
Para prepararlo primero ejecutamos lo siguiente:
groupadd puppet useradd -d /nonexistent -N -s /sbin/nologin -g puppet puppet mkdir -p /etc/puppet/rack/public mkdir -p /var/log/puppet chown -R puppet:puppet /etc/puppet/rack cp /root/puppet-2.7.3/ext/rack/files/config.ru /etc/puppet/rack
Con la última linea copiamos el archivo de ejecución de Puppet (rails/rack).
Luego se tiene que generar el archivo "puppet.conf" con la configuración del servidor maestro, en nuestra instalación se generará en el directorio "/etc/puppet".
El formato del archivo es "ini", y todas las directivas deben de ir dentro del bloque "[master]".
Para mantener el enfoque del documento, se utilizará la configuración por defecto, salvo por las siguientes 2 lineas que es importante agregar al archivo:
ssl_client_header = HTTP_X_CLIENT_DN ssl_client_verify_header = HTTP_X_CLIENT_VERIFY
Los valores HTTP_X_CLIENT_DN Y HTTP_X_CLIENT_VERIFY tiene que coincidir con sus equivalentes en el archivo de configuración de nginx.conf (passenger_set_cgi_param HTTP_X_CLIENT_DN $ssl_client_s_dn y passenger_set_cgi_param HTTP_X_CLIENT_VERIFY $ssl_client_verify), de lo contrario no se podrá autenticar, se presentaran errores al firmar los certificados o no se podrán descargar archivos en los nodos desde el servidor Puppet.
Luego iniciamos por única vez el servidor maestro puppet, y lo detenemos para evitar problemas con Nginx.
puppet master kill `cat /var/lib/puppet/run/master.pid`
Esto genera la estructura de directorios, la llave privada y el ".csr", que será el archivo enviado al servidor para su firma. También se pudieron generar de forma manual los directorios y tanto la llave como el .csr, pero no tiene caso si ya esta automatizado.
Es importante destacar que previo a este paso el servidor ya debe de contar con un hostname, ya que este será utilizado durante la generación de los certificados.
Configuración Nginx
Una vez instaladas las aplicaciones y con un hostname, se tiene que agregar en el archivo /etc/hosts, de preferencia, con una dirección IP pública y estática.
IP_ADDRESS puppet
Al momento de instalar Passenger, el script activó de forma automática el soporte de Rails, por lo cual solo nos resta agregar un "server" en el archivo de configuración nginx.conf:
server { listen 8140 default ssl; server_name puppet; passenger_enabled on; root /etc/puppet/rack/public; error_log /var/log/puppet/error.log; access_log /var/log/puppet/access.log; types { } default_type application/x-raw; ssl on; ssl_certificate /etc/puppet/ssl/certs/HOSTNAME.pem; ssl_certificate_key /etc/puppet/ssl/private_keys/HOSTNAME.pem; ssl_client_certificate /etc/puppet/ssl/ca/ca_crt.pem; ssl_crl /etc/puppet/ssl/ca/ca_crl.pem; ssl_verify_client optional; ssl_protocols SSLv3 TLSv1; ssl_ciphers HIGH:+MEDIUM; ssl_prefer_server_ciphers on; ssl_verify_depth 1; ssl_session_cache shared:SSL:8m; ssl_session_timeout 10m; proxy_headers_hash_max_size 1024; proxy_headers_hash_bucket_size 128; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; passenger_set_cgi_param HTTP_X_CLIENT_DN $ssl_client_s_dn; passenger_set_cgi_param HTTP_X_CLIENT_VERIFY $ssl_client_verify; # opcionales, no hacen ninguna diferencia ya que se utilizan los de arriba. proxy_set_header X-Client-Verify $ssl_client_verify; proxy_set_header X-Client-DN $ssl_client_s_dn; proxy_set_header X-SSL-Subject $ssl_client_s_dn; proxy_set_header X-SSL-Issuer $ssl_client_i_dn; }
Algunas aclaraciones con respecto a la configuración necesaria para que funcione puppet y evitarnos problemas al validar los certificados. proxy_headers_hash_* sirve para evitar un warning de nginx, las primeras 3 opciones proxy_set_header nos sirven en entornos con host virtuales y para proxy reverso. Las opciones proxy_set_header X-* son opcionales.
Las lineas correspondientes a "passenger_set_cgi_param" sirven para asociar los valores del certificado ssl con las opciones definidas en el archivo puppet.conf (lineas ssl_client_header = HTTP_X_CLIENT_DN y ssl_client_verify_header = HTTP_X_CLIENT_VERIFY) y como se dijo previamente, son importantes para evitar problemas con la autenticación al servidor puppet, y la definición de privilegios.
"ssl_verify_client" ES MUY IMPORTANTE dejarla en "optional" por 2 razones, la primera es que cuando en un agent se desea conectar a un servidor por primera vez, se genera el juego de llaves, y se contacta al servidor para que firme el certificado. La segunda razón es para que una vez firmado el certificado, el agent se autentique con el servidor y obtenga los privilegios adecuados durante su operación.
En caso de dejarlo en "on" u "off", se pueden perder muchas horas investigando el problema.
Instalación y configuración de nodos
En el caso de FreeBSD, la instalación es mucho mas sencilla, lo único que hay que hacer es ejecutar el siguiente comando:
cd /usr/ports/sysutils/puppet make install clean
De esa forma nos instalará la última versión de Puppet y Ruby.
El siguiente paso es asociar la ip del servidor con el hostname "puppet", la forma mas sencilla es agregarlo al archivo /etc/hosts.
IP_ADDRESS puppet
Después configuramos el "agent" agregando las siguientes lineas al archivo puppet.conf:
[agent] server = puppet runinterval = 3600 splaylimit = 1800 splay = true
En el caso de FreeBSD, la ruta al archivo es /usr/local/etc/puppet/puppet.conf.
Lo único relevante de esta configuración es que con "runinterval" se consultar al servidor maestro cada hora en lugar de cada media hora, y con las opciones "splay*" se especifica que no se ejecute inmediatamente, ambas opciones tienen el fin de no saturar el servidor al ejecutar multiples nodos de forma simultanea.
Ahora es momento de iniciar el agent para la preparación del entorno Puppet:
puppet agent --test
De esta forma se inicializará el entorno en el nodo, se generará el .key y .csr correspondientes, y se contactará al maestro para solicitar la firma del .csr, y terminará el proceso.
Hasta este momento aún no hay intercambio de configuraciones entre maestro y nodo ya que el servidor antes tiene que generar el certificado correspondiente. Esto lo hacemos en el maestro con el siguiente comando:
puppetca -s HOSTNAME
Una vez firmado el certificado del agent, este ya puede consultar al servidor Puppet para descargar las configuraciones correspondientes a dicho nodo, esto lo hacemos ejecutando el demonio de la siguiente forma:
puppet agent
Es importante mencionar que el proceso de solicitud de certificado y validación, se tiene que repetir en cada uno de los nodos. Pueden realizarse todas las solicitudes primero y luego las validaciones para ahorrar un poco de tiempo.
Ejemplo de uso
Ahora que tenemos correctamente configurado nuestro entorno, la primera prueba que haremos será distribuir un archivo a todos los nodos.
Para realizar dicha actividad tendremos que crear el directorio "/etc/puppet/manifests" y dentro crear el archivo "site.pp" con el siguiente contenido:
node default { file { "/etc/puppettest.txt": content => "prueba de puppet", owner => root, group => wheel, mode => 444, ensure => present; } }
Después de una hora aproximadamente, o dependiendo de los tiempos definidos en la configuración de cada nodo, el archivo "/etc/puppettest.txt" debe ser generado con el contenido "prueba de puppet" en cuanto se vuelva a ejecutar el daemon.
A grandes rasgos, las lineas anteriores instruyen a los nodos a mantener el archivo /etc/puppettest.txt, con contenido "prueba de puppet", con root como propietario, grupo wheel, y con permisos 444. En caso de no existir, se crea, o en caso de tener otro tipo de contenido, se remplaza con el definido.
Si deseamos aplicar los cambios de forma inmediata en algún nodo, solo es necesario ejecutar el comando "puppet agent" con la opción "--test":
puppet agent --test
Si al comando anterior le agregamos la opción "--noop", se nos mostrará la lista de actividades que se realizarán sobre el nodo, sin ejecutarlas, esto es muy útil para revisar los cambios que se aplicaran.
A simple vista no parece muy útil copiar archivos en cada servidor, es una labor que se puede realizar manualmente. Sin embargo, cuando hablamos de distribuir archivos sshd_config, httpd.conf, php.ini, y demás; entre 10 o mas servidores, y con configuraciones particulares a cada nodo, se vuelve una necesidad contar con una herramienta de este tipo.
Conclusión
Puppet permite de manera sencilla mediante el uso de variables, módulos y plantillas; la distribución de configuraciones generales o particulares, entre cualquier número de nodos, manteniendo centralizada la configuración para su fácil acceso, modificación y resguardo.
El enfoque principal de este documento fue la instalación y puesta en marcha del entorno, esto debido a que la información es muy escasa y dispersa, y el potencial de Nginx/Passenger + Puppet es enorme. Si además sumamos la infraestructura de Amazon dentro de nuestra plataforma y servicios actuales, tendremos un entorno muy robusto y de alto desempeño.
Queda pendiente para un articulo posterior, ejemplificar casos practicos que demuestren la versatilidad del sistema, sin embargo, se invita al lector a revisar la documentación de Puppet para comenzar a aprovechar el entorno recientemente implementado.
Refinamiento
Puesto que el servidor se encuentra en una instancia EC2 y utiliza ip privada, se sugiere activar un Elastic IP y asignarla a dicha instancia, esto debido a que en caso de reinicio del servidor maestro, la dirección IP cambiara, dejando sin conexión con el maestro a los nodos.
Otra recomendación sería ejecutar mediante un crontab el agent en los nodos, esto ya que al utilizarlo como daemon tiende a ir creciendo el uso de memoria del proceso, haciendo necesario un reinicio del mismo después de cierto tiempo.
Autor
Angel Gabriel León Rodríguez