Ensamblar y Ejecutar un Playbook con Roles Personalizados, de Git y del Sistema
En este paso, unirás todos los componentes que has preparado: tu rol personalizado, la dependencia de Git y el Rol del Sistema RHEL. Crearás un playbook principal que orquestará estos roles para configurar completamente el servidor web de desarrollo.
Piensa en este paso como el ensamblaje de una máquina compleja a partir de diferentes piezas: cada rol cumple un propósito específico y trabajan juntos para crear un entorno completo de servidor web. Vamos a desglosar esto en partes manejables:
Primero, asegúrate de estar en el directorio principal del proyecto.
cd ~/project
Antes de sumergirnos en la configuración, entendamos qué estamos creando:
- Configuración de Ansible: Establece cómo se comporta Ansible y dónde encuentra los archivos.
- Inventario: Define qué servidores gestionar (en nuestro caso, localhost).
- Variables: Almacenan datos que nuestros roles utilizarán (información del desarrollador, configuraciones de SELinux).
- Contenido del Rol Personalizado: Las tareas reales que configurarán los entornos de desarrollo.
- Playbook Principal: El orquestador que ejecuta todo en el orden correcto.
1. Crear Configuración y Inventario de Ansible
El archivo ansible.cfg es como un archivo de configuración que le dice a Ansible cómo comportarse. Sin él, necesitarías especificar rutas y opciones en cada comando. Con él, Ansible sabe automáticamente dónde encontrar tus roles, colecciones e inventario.
Crea el archivo ansible.cfg usando nano. Este archivo le dice a Ansible dónde encontrar tus roles, colecciones e inventario.
nano ansible.cfg
Añade el siguiente contenido. Entendamos cada línea:
[defaults]
inventory = inventory
roles_path = roles
collections_paths = collections
host_key_checking = False
[privilege_escalation]
become = True
Qué hace cada configuración:
inventory = inventory: En lugar de escribir -i inventory cada vez, Ansible usará automáticamente este archivo.
roles_path = roles: Ansible buscará roles en el directorio roles.
collections_paths = collections: Ansible encontrará tus colecciones instaladas aquí.
host_key_checking = False: Evita errores de verificación de claves SSH en entornos de laboratorio.
become = True: Ejecuta automáticamente tareas con privilegios elevados cuando es necesario.
Guarda y sal de nano (Presiona Ctrl+X, luego Y, luego Enter).
El archivo de inventario le dice a Ansible qué máquinas gestionar. En nuestro caso, estamos configurando la máquina local.
nano inventory
Añade la siguiente línea:
localhost ansible_connection=local
Qué significa esto:
localhost: El nombre de nuestro host de destino.
ansible_connection=local: En lugar de SSH, usa conexiones locales (ya que estamos gestionando la misma máquina en la que ejecutamos Ansible).
Guarda y sal de nano.
2. Definir Variables de Rol
Las variables en Ansible son como configuraciones que tus roles pueden usar. En lugar de codificar valores como nombres de usuario o números de puerto en tus tareas, los defines en archivos de variables. Esto hace que tu automatización sea flexible y reutilizable.
El directorio group_vars/all es una ubicación especial donde Ansible carga automáticamente variables para todos los hosts. Cualquier archivo YAML en este directorio se vuelve disponible para tus playbooks y roles.
Crea la estructura de directorios para variables que se aplican a todos los hosts:
mkdir -p group_vars/all
Ahora, crea un archivo para definir la información del desarrollador. Estos datos serán utilizados por tu rol personalizado para crear cuentas de usuario y configuraciones web.
nano group_vars/all/developers.yml
Añade el siguiente contenido:
---
web_developers:
- username: jdoe ## Primer desarrollador
port: 9081 ## Puerto personalizado para el sitio web de este desarrollador
- username: jdoe2 ## Segundo desarrollador
port: 9082 ## Puerto personalizado para el sitio web de este desarrollador
Qué significa esta estructura de datos:
web_developers: Una lista que contiene información del desarrollador.
- Cada desarrollador tiene un
username y un port.
- Tu rol personalizado recorrerá esta lista para crear configuraciones para cada desarrollador.
Guarda y sal.
A continuación, crea un archivo de variables para la configuración de SELinux. SELinux (Security-Enhanced Linux) es un módulo de seguridad que controla lo que las aplicaciones pueden hacer.
nano group_vars/all/selinux.yml
Añade el siguiente contenido:
---
selinux_state: enforcing ## Establece SELinux en modo enforcing (máxima seguridad)
selinux_ports: ## Lista de puertos que se permitirán usar a Apache
- ports: "9081" ## Permitir el puerto 9081
proto: "tcp" ## Protocolo: TCP
setype: "http_port_t" ## Tipo SELinux: puerto HTTP
state: "present" ## Añadir esta regla
- ports: "9082" ## Permitir el puerto 9082
proto: "tcp" ## Protocolo: TCP
setype: "http_port_t" ## Tipo SELinux: puerto HTTP
state: "present" ## Añadir esta regla
Entendiendo la configuración de SELinux:
selinux_state: enforcing: SELinux bloqueará activamente las acciones no autorizadas.
selinux_ports: Una lista de configuraciones de puertos.
http_port_t: El tipo SELinux que permite a Apache enlazarse a puertos.
- Por defecto, Apache solo puede usar los puertos 80 y 443; necesitamos permitir explícitamente 9081 y 9082.
Guarda y sal.
3. Poblar el Rol Personalizado
Tu rol apache.developer_configs actualmente tiene la estructura de directorios pero sin contenido real. Necesitamos añadir:
- Plantillas (Templates): Archivos que pueden incluir variables (usando sintaxis Jinja2).
- Tareas (Tasks): El trabajo real que realizará Ansible.
- Manejadores (Handlers): Tareas especiales que solo se ejecutan cuando se les notifica (como reiniciar servicios).
- Metadatos (Metadata): Información sobre las dependencias del rol.
Las plantillas te permiten crear archivos de configuración que se adaptan según tus variables. La extensión .j2 indica que es una plantilla Jinja2.
nano roles/apache.developer_configs/templates/developer.conf.j2
Añade el siguiente contenido:
{% for dev in web_developers %}
Listen {{ dev.port }}
<VirtualHost *:{{ dev.port }}>
ServerAdmin webmaster@localhost
DocumentRoot /var/www/{{ dev.username }}
<Directory /var/www/{{ dev.username }}>
Options Indexes FollowSymLinks
AllowOverride None
Require all granted
</Directory>
</VirtualHost>
{% endfor %}
Entendiendo la sintaxis de la plantilla:
{% for dev in web_developers %}: Inicia un bucle a través de la lista de desarrolladores.
{{ dev.port }}: Inserta el número de puerto para este desarrollador.
{{ dev.username }}: Inserta el nombre de usuario para este desarrollador.
{% endfor %}: Finaliza el bucle.
- El resultado serán configuraciones de host virtual separadas para cada desarrollador.
Qué crea esto: Para nuestros dos desarrolladores, esta plantilla generará una configuración de Apache que:
- Hace que Apache escuche en los puertos 9081 y 9082.
- Crea hosts virtuales que sirven contenido desde
/var/www/jdoe y /var/www/jdoe2.
- Establece los permisos apropiados para cada directorio.
Guarda y sal.
Las tareas son el trabajo real que realiza Ansible. Cada tarea utiliza un módulo de Ansible para lograr algo específico.
nano roles/apache.developer_configs/tasks/main.yml
Añade el siguiente contenido y entendamos cada tarea:
---
## Tarea 1: Crear cuentas de usuario para cada desarrollador
- name: Create developer user accounts
ansible.builtin.user: ## Usa el módulo 'user'
name: "{{ item.username }}" ## Crea el usuario con este nombre
state: present ## Asegura que el usuario exista
loop: "{{ web_developers }}" ## Haz esto para cada desarrollador en la lista
## Tarea 2: Crear directorios web para cada desarrollador
- name: Create developer web root directories
ansible.builtin.file: ## Usa el módulo 'file'
path: "/var/www/{{ item.username }}" ## Crea este directorio
state: directory ## Asegura que sea un directorio
owner: "{{ item.username }}" ## Establece el propietario
group: "{{ item.username }}" ## Establece el grupo
mode: "0755" ## Establece permisos (rwxr-xr-x)
loop: "{{ web_developers }}"
## Tarea 3: Crear una página web de ejemplo para cada desarrollador
- name: Create a sample index.html for each developer
ansible.builtin.copy: ## Usa el módulo 'copy'
content: "Welcome to {{ item.username }}'s dev space\n" ## Contenido del archivo
dest: "/var/www/{{ item.username }}/index.html" ## Dónde colocar el archivo
owner: "{{ item.username }}" ## Propietario del archivo
group: "{{ item.username }}" ## Grupo del archivo
mode: "0644" ## Permisos del archivo (rw-r--r--)
loop: "{{ web_developers }}"
## Tarea 4: Desplegar el archivo de configuración de Apache
- name: Deploy developer apache configs
ansible.builtin.template: ## Usa el módulo 'template'
src: developer.conf.j2 ## Archivo de plantilla de origen
dest: /etc/httpd/conf.d/developer.conf ## Destino en el servidor
mode: "0644" ## Permisos del archivo
notify: restart apache ## Dispara el manejador de reinicio cuando esto cambie
Entendiendo conceptos clave:
loop: Repite la tarea para cada elemento de la lista.
{{ item.username }}: Se refiere al nombre de usuario del elemento actual en el bucle.
notify: restart apache: Cuando esta tarea realiza cambios, activará un manejador llamado "restart apache".
- Permisos de archivo:
0755 significa que el propietario puede leer/escribir/ejecutar, los demás pueden leer/ejecutar; 0644 significa que el propietario puede leer/escribir, los demás solo pueden leer.
Guarda y sal.
Los manejadores (handlers) son tareas especiales que solo se ejecutan cuando son notificadas por otras tareas. Típicamente se usan para acciones como reiniciar servicios.
nano roles/apache.developer_configs/handlers/main.yml
Añade el siguiente contenido:
---
- name: restart apache ## Este nombre debe coincidir con la declaración notify:
ansible.builtin.service: ## Usa el módulo 'service'
name: httpd ## El nombre del servicio (Apache se llama 'httpd' en RHEL)
state: restarted ## Reinicia el servicio
¿Por qué usar manejadores?
- Eficiencia: El servicio solo se reinicia si la configuración realmente cambió.
- Orden: Todas las tareas se ejecutan primero, luego todos los manejadores se ejecutan al final.
- Idempotencia: Múltiples tareas pueden notificar al mismo manejador, pero solo se ejecuta una vez.
Guarda y sal.
Finalmente, necesitamos decirle a Ansible que nuestro rol personalizado depende del rol infra.apache que instalamos anteriormente.
nano roles/apache.developer_configs/meta/main.yml
Reemplaza el contenido del archivo con:
---
dependencies:
- role: infra.apache ## Este rol debe ejecutarse antes que nuestro rol personalizado
Qué hace esto:
- Cuando Ansible ejecuta
apache.developer_configs, ejecutará automáticamente infra.apache primero.
- Esto asegura que Apache esté instalado y configurado antes de que agreguemos nuestras configuraciones personalizadas.
- Las dependencias se ejecutan en el orden en que están listadas.
Guarda y sal.
4. Ensamblar y Ejecutar el Playbook Principal
Un playbook es como una receta que le dice a Ansible qué hacer y en qué orden. Nuestro playbook hará lo siguiente:
- Configurar los ajustes de SELinux (pre_tasks).
- Ejecutar nuestros roles (que incluye la cadena de dependencias).
Crea el archivo del playbook principal:
nano web_dev_server.yml
Añade el siguiente contenido con explicaciones detalladas:
---
- name: Configure Dev Web Server ## Nombre del playbook
hosts: localhost ## Ejecutar en localhost
pre_tasks: ## Tareas que se ejecutan antes de los roles
## Tarea 1: Configurar el modo SELinux
- name: Set SELinux to enforcing mode
ansible.posix.selinux: ## Módulo de la colección ansible.posix
policy: targeted ## Usar la política SELinux 'targeted'
state: "{{ selinux_state }}" ## Usar la variable que definimos
when: selinux_state is defined ## Ejecutar solo si la variable existe
## Tarea 2: Configurar puertos SELinux
- name: Configure SELinux ports for Apache
community.general.seport: ## Módulo de la colección community.general
ports: "{{ item.ports }}" ## Número de puerto
proto: "{{ item.proto }}" ## Protocolo (tcp)
setype: "{{ item.setype }}" ## Tipo SELinux (http_port_t)
state: "{{ item.state }}" ## present o absent
loop: "{{ selinux_ports }}" ## Iterar sobre nuestra lista de puertos
when: selinux_ports is defined ## Ejecutar solo si la variable existe
roles: ## Roles a ejecutar
- apache.developer_configs ## Nuestro rol personalizado (que activará infra.apache)
Entendiendo el orden de ejecución:
pre_tasks: La configuración de SELinux se ejecuta primero.
roles: Las dependencias de roles se ejecutan (infra.apache), luego nuestro rol personalizado.
handlers: Cualquier manejador notificado se ejecuta al final.
Por qué este orden es importante:
- SELinux debe configurarse antes de que Apache intente enlazarse a puertos personalizados.
- Apache debe estar instalado antes de que podamos configurar hosts virtuales.
- Los reinicios de servicio ocurren después de que toda la configuración se completa.
Guarda y sal.
Ahora estás listo para ejecutar tu automatización completa:
ansible-playbook web_dev_server.yml
El playbook se ejecutará y verás una salida detallada. Aquí tienes lo que puedes esperar (por ejemplo):
PLAY [Configure Dev Web Server] *************************************************
TASK [Gathering Facts] **********************************************************
ok: [localhost] ## Ansible recopila información del sistema
TASK [Set SELinux to enforcing mode] *******************************************
changed: [localhost] ## El modo SELinux fue cambiado
TASK [Configure SELinux ports for Apache] **************************************
changed: [localhost] => (item={'ports': '9081', 'proto': 'tcp', 'setype': 'http_port_t', 'state': 'present'})
changed: [localhost] => (item={'ports': '9082', 'proto': 'tcp', 'setype': 'http_port_t', 'state': 'present'})
TASK [infra.apache : Ensure Apache is installed.] *******************************
changed: [localhost] ## El paquete Apache fue instalado
TASK [apache.developer_configs : Create developer user accounts] ****************
changed: [localhost] => (item={'username': 'jdoe', 'port': 9081})
changed: [localhost] => (item={'username': 'jdoe2', 'port': 9082})
TASK [apache.developer_configs : Create developer web root directories] *********
changed: [localhost] => (item={'username': 'jdoe', 'port': 9081})
changed: [localhost] => (item={'username': 'jdoe2', 'port': 9082})
TASK [apache.developer_configs : Create a sample index.html for each developer] *
changed: [localhost] => (item={'username': 'jdoe', 'port': 9081})
changed: [localhost] => (item={'username': 'jdoe2', 'port': 9082})
TASK [apache.developer_configs : Deploy developer apache configs] ***************
changed: [localhost] ## El archivo de configuración fue creado
RUNNING HANDLER [apache.developer_configs : restart apache] *********************
changed: [localhost] ## Apache fue reiniciado
PLAY RECAP **********************************************************************
localhost : ok=17 changed=12 unreachable=0 failed=0 skipped=3 rescued=0 ignored=0
¡Has ensamblado y ejecutado con éxito un playbook complejo que combina múltiples roles de diferentes fuentes para crear un entorno completo de desarrollo web!