Saltar al contenido

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.

FamiliaFichero principalNombre del servicio
Debian y derivadas/etc/apache2/apache2.confapache2
Red Hat y derivadas/etc/httpd/conf/httpd.confhttpd

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.

OrdenQué activaEntre qué directorios crea el enlace
a2enmodUn módulo del servidormods-available y mods-enabled
a2ensiteLa configuración de un sitiosites-available y sites-enabled
a2dismod, a2dissiteDesactivan quitando el enlaceLos mismos
bash
a2enmod ssl
a2ensite 100-llegandoalcorte.conf
systemctl reload apache2      # aplicar sin cortar las conexiones en curso

Para 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.

bash
apache2ctl -t             # comprobar la configuración ANTES de recargar
apache2ctl -M             # módulos cargados
apache2ctl status         # resumen del estado del servidor
apache2ctl restart

La 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.

DirectivaQué define
ServerNameEl nombre principal por el que responde este sitio
ServerAliasOtros nombres que también atiende el mismo sitio
DocumentRootEl directorio del que se sirven los ficheros
ErrorLogFichero donde se registran los errores
CustomLogFichero de accesos, con el formato indicado detrás
SSLEngineActiva el cifrado en este host virtual
SSLCertificateFileEl certificado del sitio
SSLCertificateKeyFileLa clave privada asociada al certificado
apache
<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óduloCómo atiendeCuándo conviene
preforkUn proceso por conexión, sin hilosMáxima compatibilidad con módulos antiguos
workerVarios procesos, cada uno con varios hilosMenos memoria por conexión
eventComo worker, liberando el hilo mientras la conexión esperaMuchas 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.

ConceptoQué significa
Infraestructura como códigoLa 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ónEl proceso de aplicar y mantener esa configuración a lo largo del tiempo, no una sola vez
DevOpsLa 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.

EnfoqueQué se escribe
DeclarativoEl estado final que se quiere, sin decir cómo llegar a él
ImperativoLos 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.

HerramientaModelo
AnsibleEmpuje
ChefTirón
PuppetTirón
SCCMTirón
WSUSTirón
SaltStackLos dos: empuje y tirón
StackStormEmpuje
TerraformEmpuje
CFEngineTirón
OtterEmpuje

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.

FicheroPara qué
/etc/ansible/hostsEl inventario: qué máquinas hay y en qué grupos
/etc/ansible/ansible.cfgLa configuración general, incluida la ruta del inventario
/etc/ansible/group_vars/nombreVariables que se aplican a todo un grupo del inventario
yaml
# /etc/ansible/hosts
[servidores_temario]
srv-temario-1 ansible_ssh_host=10.20.30.11
srv-temario-2 ansible_ssh_host=10.20.30.12

Hay 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.

bash
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.

yaml
---
- 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: true

Los 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.

bash
ansible-playbook contenido.yml
ansible-playbook contenido.yml --check     # simular sin aplicar nada

Ansible 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.

FicheroDóndeQué define
/etc/salt/masterEn el maestroInterfaz de escucha, puertos, usuario y raíz de ficheros
/etc/salt/minionEn el nodoA qué maestro se conecta y por qué puerto
/etc/salt/minion_idEn el nodoSu 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.

PuertoPara qué
4505Publicación: por donde el maestro difunde las órdenes
4506Retorno: 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.

bash
salt-key -L               # listar todas las claves y su estado
salt-key -A               # aceptar las pendientes

Las ó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.

bash
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.

yaml
# /srv/salt/web.sls
apache2:
  pkg.installed
  service.running:
    - enable: True
bash
salt '*' state.apply web

La ú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.

bash
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