Apache y automatización
La otra mitad del trabajo: levantar un servidor web con sus sitios y sus módulos, y dejar de configurar máquinas a mano describiendo en ficheros el estado que deben tener, con las dos herramientas que representan los dos modelos, Ansible y SaltStack.
Apache: configuración, módulos y sitios
El servidor web de referencia se administra por ficheros de texto, y lo primero es saber dónde están, porque cada familia de distribuciones lo coloca en un sitio distinto y hasta el servicio se llama diferente.
| Familia | Fichero principal | Nombre del servicio |
|---|---|---|
| Debian y derivadas | /etc/apache2/apache2.conf | apache2 |
| Red Hat y derivadas | /etc/httpd/conf/httpd.conf | httpd |
En la familia Debian, la configuración no es un fichero gigante sino un directorio con piezas, y hay una idea que se repite en módulos y en sitios: cada pieza existe en un directorio de disponibles y se activa creando un enlace simbólico en el de activados. Desactivar algo es quitar el enlace, no borrar la configuración.
| Orden | Qué activa | Entre qué directorios crea el enlace |
|---|---|---|
| a2enmod | Un módulo del servidor | mods-available y mods-enabled |
| a2ensite | La configuración de un sitio | sites-available y sites-enabled |
| a2dismod, a2dissite | Desactivan quitando el enlace | Los mismos |
a2enmod ssl
a2ensite 100-llegandoalcorte.conf
systemctl reload apache2 # aplicar sin cortar las conexiones en cursoPara gobernar el servicio existe además una herramienta propia del servidor, apachectl, que en la familia Debian aparece también como apache2ctl. Aporta dos cosas que systemctl no da: la lista de módulos realmente cargados y, sobre todo, la comprobación de sintaxis antes de recargar.
apache2ctl -t # comprobar la configuración ANTES de recargar
apache2ctl -M # módulos cargados
apache2ctl status # resumen del estado del servidor
apache2ctl restartLa comprobación de sintaxis es el hábito que evita dejar un servidor caído: si la configuración tiene un error, el servicio no vuelve a levantar tras recargarlo.
Para el examen
Hábito imprescindible: comprobar la sintaxis antes de recargar
Qué evita: dejar el servidor caído: con un error de configuración no vuelve a levantar
Servir varios sitios: el host virtual
Un host virtual permite que una sola máquina, con una sola dirección, atienda varios sitios distintos. El servidor decide cuál sirve mirando el nombre que pide el cliente, así que la clave del bloque es la directiva de nombre de servidor.
| Directiva | Qué define |
|---|---|
| ServerName | El nombre principal por el que responde este sitio |
| ServerAlias | Otros nombres que también atiende el mismo sitio |
| DocumentRoot | El directorio del que se sirven los ficheros |
| ErrorLog | Fichero donde se registran los errores |
| CustomLog | Fichero de accesos, con el formato indicado detrás |
| SSLEngine | Activa el cifrado en este host virtual |
| SSLCertificateFile | El certificado del sitio |
| SSLCertificateKeyFile | La clave privada asociada al certificado |
<VirtualHost *:443>
ServerName www.llegandoalcorte.com
ServerAlias llegandoalcorte.com
DocumentRoot /var/www/llc/publico
ErrorLog /var/log/apache2/llc-error.log
CustomLog /var/log/apache2/llc-acceso.log combined
SSLEngine on
SSLCertificateFile /etc/ssl/llc/sitio.crt
SSLCertificateKeyFile /etc/ssl/llc/sitio.key
</VirtualHost>El asterisco de la etiqueta de apertura significa cualquier dirección de la máquina, y el número que va detrás es el puerto. Un sitio cifrado se declara en el 443 y su versión sin cifrar en el 80, que normalmente se limita a redirigir al primero.
Hay una decisión de configuración que no está en el host virtual y que condiciona el rendimiento entero: el módulo de multiproceso, que define cómo atiende el servidor las peticiones simultáneas. Se elige uno y solo uno.
| Módulo | Cómo atiende | Cuándo conviene |
|---|---|---|
| prefork | Un proceso por conexión, sin hilos | Máxima compatibilidad con módulos antiguos |
| worker | Varios procesos, cada uno con varios hilos | Menos memoria por conexión |
| event | Como worker, liberando el hilo mientras la conexión espera | Muchas conexiones abiertas y poco tráfico |
El host virtual se elige por el nombre pedido, no por la dirección: por eso una sola máquina puede servir decenas de sitios con un solo par de dirección y puerto.
Para el examen
Cómo se elige el host virtual: por el NOMBRE pedido, no por la dirección
Qué permite: servir decenas de sitios con un solo par de dirección y puerto
Infraestructura como código
Configurar máquinas a mano funciona hasta que hay tres. A partir de ahí aparecen las diferencias entre servidores que deberían ser iguales, nadie recuerda por qué uno tiene un parámetro distinto y reconstruir una máquina perdida se convierte en arqueología. La respuesta a ese problema es describir la configuración en ficheros y dejar que una herramienta la aplique.
| Concepto | Qué significa |
|---|---|
| Infraestructura como código | La configuración y el aprovisionamiento se escriben en ficheros de definición, se versionan y se revisan como cualquier otro código |
| Automatización continua de la configuración | El proceso de aplicar y mantener esa configuración a lo largo del tiempo, no una sola vez |
| DevOps | La figura que reúne el trabajo de desarrollo y el de operaciones, y que es quien usa estas herramientas |
Las dos ideas van juntas pero no son la misma. La primera responde a cómo se describe el estado deseado; la segunda, a cómo se garantiza que las máquinas sigan en ese estado con el paso del tiempo, cuando alguien toca algo a mano o cuando la definición cambia.
Las ventajas son concretas y explican por qué esto es hoy la norma: el fichero de definición sirve de documentación siempre actualizada, cualquier cambio queda registrado con su autor en el sistema de versiones, y levantar una máquina idéntica a otra deja de depender de la memoria de nadie.
Lo que se versiona no es la máquina, es su descripción. Ese cambio de enfoque es lo que hace reproducible una infraestructura.
Para el examen
Qué se versiona: la descripción de la máquina, no la máquina
Qué se consigue: una infraestructura reproducible
Los dos ejes: declarativo o imperativo, empujar o tirar
Las herramientas de gestión de la configuración se clasifican por dos criterios independientes, y las preguntas de examen mezclan los dos. El primero es qué se escribe en el fichero de definición.
| Enfoque | Qué se escribe |
|---|---|
| Declarativo | El estado final que se quiere, sin decir cómo llegar a él |
| Imperativo | Los pasos concretos que hay que ejecutar, en orden |
Declarar es decir que el servidor web tiene que estar instalado y en marcha; ordenar es decir que se ejecute la instalación y después el arranque. La ventaja del primero es que se puede aplicar cien veces con el mismo resultado, porque la herramienta comprueba antes de actuar. La mayoría de las herramientas admiten los dos usos; la excepción que se cita es CFEngine, que es únicamente declarativa.
El segundo criterio es quién toma la iniciativa. En el modelo de empuje, el servidor central se conecta a los nodos y les aplica la configuración cuando el administrador lo decide. En el de tirón, es cada nodo el que pregunta periódicamente al servidor si hay algo nuevo para él, lo que exige tener un agente instalado.
| Herramienta | Modelo |
|---|---|
| Ansible | Empuje |
| Chef | Tirón |
| Puppet | Tirón |
| SCCM | Tirón |
| WSUS | Tirón |
| SaltStack | Los dos: empuje y tirón |
| StackStorm | Empuje |
| Terraform | Empuje |
| CFEngine | Tirón |
| Otter | Empuje |
El modelo condiciona la arquitectura. Empujar no requiere agente en el nodo, pero sí que el servidor central pueda alcanzarlo. Tirar funciona aunque el nodo esté detrás de una red que el central no alcanza, a cambio de mantener un agente en cada máquina.
Ansible empuja y no necesita agente; SaltStack admite los dos modelos y sí lo necesita. Es la comparación que más aparece en el temario.
Para el examen
Primer eje: declarativo o imperativo
Segundo eje: empujar (push) o tirar (pull)
Ansible: empuja y no necesita agente
SaltStack: admite los dos modelos y sí necesita agente
Ansible
Ansible se instala solo en el servidor central. Los nodos administrados no llevan nada especial: basta con poder entrar por SSH y que tengan Python, porque lo que Ansible hace por debajo es exactamente lo del subtema anterior, conectarse y ejecutar. Esa ausencia de agente es su rasgo distintivo.
| Fichero | Para qué |
|---|---|
| /etc/ansible/hosts | El inventario: qué máquinas hay y en qué grupos |
| /etc/ansible/ansible.cfg | La configuración general, incluida la ruta del inventario |
| /etc/ansible/group_vars/nombre | Variables que se aplican a todo un grupo del inventario |
# /etc/ansible/hosts
[servidores_temario]
srv-temario-1 ansible_ssh_host=10.20.30.11
srv-temario-2 ansible_ssh_host=10.20.30.12Hay dos formas de trabajar. La rápida es la orden suelta: se indica sobre qué grupo actuar, qué módulo usar y con qué argumentos. Es cómoda para comprobaciones y para acciones puntuales, y es de uso imperativo.
ansible all -m ping
ansible all -a "uname -a" # el módulo por defecto es command
ansible all -m shell -a "df -h /srv"
ansible servidores_temario -m apt -a "name=apache2 state=present"La forma seria es el playbook: un fichero en formato YAML donde se describe el estado que deben tener los nodos. Ahí es donde Ansible se vuelve declarativo, porque cada tarea dice a qué estado hay que llegar y la herramienta decide si hace falta actuar.
---
- name: Preparar los servidores de contenido
hosts: servidores_temario
become: true
tasks:
- name: El servidor web debe estar instalado
apt:
name: apache2
state: present
- name: Y en marcha desde el arranque
service:
name: apache2
state: started
enabled: trueLos estados de un paquete son pocos y se preguntan. present e installed piden que esté instalado; latest pide además que esté en la última versión disponible; absent y remove piden que no esté.
De ahí sale la propiedad que da valor a todo esto: la idempotencia. Aplicar el mismo playbook diez veces deja la máquina igual que aplicarlo una, porque cada tarea comprueba antes de actuar y no hace nada si el estado ya es el pedido. Es lo que separa un playbook de un guion, que ejecutaría la instalación diez veces.
ansible-playbook contenido.yml
ansible-playbook contenido.yml --check # simular sin aplicar nadaAnsible no necesita agente, pero sí SSH y Python en el nodo. Sus playbooks van en YAML y su valor está en que son idempotentes.
Para el examen
Qué NO necesita: agente instalado
Qué sí necesita: SSH y Python en el nodo
Playbooks: en YAML e idempotentes
Inventario: el fichero que lista las máquinas
SaltStack
SaltStack resuelve el mismo problema con la arquitectura contraria: un servidor central, el maestro, y un agente instalado en cada máquina administrada, el minion. El agente mantiene abierta una conexión con el maestro, y eso es lo que le permite ser muy rápido sobre miles de nodos.
| Fichero | Dónde | Qué define |
|---|---|---|
| /etc/salt/master | En el maestro | Interfaz de escucha, puertos, usuario y raíz de ficheros |
| /etc/salt/minion | En el nodo | A qué maestro se conecta y por qué puerto |
| /etc/salt/minion_id | En el nodo | Su identificador, que por defecto es el nombre de la máquina |
Los dos puertos son un dato de examen. El maestro publica órdenes por uno y recoge los resultados por el otro, y son puertos distintos precisamente porque el canal de publicación es de uno a muchos.
| Puerto | Para qué |
|---|---|
| 4505 | Publicación: por donde el maestro difunde las órdenes |
| 4506 | Retorno: por donde los nodos devuelven los resultados |
La relación entre maestro y nodo se establece con claves. El nodo se presenta, su clave queda pendiente y el administrador la acepta. Hasta entonces, el maestro no le manda nada.
salt-key -L # listar todas las claves y su estado
salt-key -A # aceptar las pendientesLas órdenes se dirigen a un conjunto de nodos mediante un patrón, y detrás va la función que deben ejecutar. Este uso es imperativo y equivale a las órdenes sueltas de Ansible.
salt '*' test.version
salt '*' disk.usage
salt '*' cmd.run 'systemctl is-active apache2'
salt '*' pkg.install vim
salt '*' network.interfaces
salt-cp '*' politica.conf /etc/llc/La parte declarativa son los estados, ficheros con extensión .sls y formato YAML que viven en la raíz de ficheros del maestro y describen cómo debe quedar el nodo. Se aplican empujándolos desde el maestro o dejando que el nodo los recoja por su cuenta, que es la doble naturaleza de la herramienta.
# /srv/salt/web.sls
apache2:
pkg.installed
service.running:
- enable: Truesalt '*' state.apply webLa última pieza propia son los grains, que son datos del nodo por los que se puede preguntar y, sobre todo, por los que se puede seleccionar: sistema operativo, modelo de procesador, memoria. Permiten dirigir una orden solo a las máquinas que cumplan una condición, y se pueden definir grains propios.
salt '*' grains.items # todo lo que se puede preguntar
salt '*' grains.get os
salt-cp -G 'os:Rocky' ajuste.py /usr/local/Existe además un modo autónomo, en el que el nodo aplica sus propios estados sin contactar con ningún maestro. Se configura indicándolo en el fichero del nodo y se ejecuta localmente, y sirve para máquinas aisladas o para probar un estado antes de desplegarlo.
En SaltStack, los grains describen al nodo y sirven para seleccionarlo; los estados en ficheros .sls describen cómo debe quedar. No confundir unos con otros.
Para el examen
Grains: describen al nodo y sirven para seleccionarlo
Estados: ficheros .sls que describen cómo debe quedar