Qué vas a conseguir
Sustituir el parcheo manual de la guía de RHEL y Ubuntu por un playbook que hace lo mismo en todos los servidores, en orden, parando si uno falla. Al terminar tendrás un repositorio con el inventario, el playbook y la salida de la primera ejecución para el ticket de cambio.
Antes de empezar
- Una máquina de control con Python 3 y acceso SSH a los servidores (tu portátil Linux/macOS, WSL o una VM pequeña).
- Una clave SSH desplegada en los servidores para un usuario con
sudosin contraseña (o con contraseña: Ansible la pide con-K). - Los servidores en la guía de Git del Nivel 2: el playbook es código y va bajo control de versiones desde el primer día.
1. Instalar y comprobar la conexión
python3 -m pip install --user ansibleansible --version | head -1ansible [core 2.17.4]mkdir -p ~/ops/ansible && cd ~/ops/ansiblecat > inventory.ini <<'EOF'[web]srv-web-01.contoso.localsrv-web-02.contoso.local [db]srv-db-01.contoso.local [linux:children]webdb [linux:vars]ansible_user=ansibleansible_become=trueEOFansible linux -i inventory.ini -m pingsrv-web-01.contoso.local | SUCCESS => {"changed": false, "ping": "pong"}srv-web-02.contoso.local | SUCCESS => {"changed": false, "ping": "pong"}srv-db-01.contoso.local | SUCCESS => {"changed": false, "ping": "pong"}ping aquí no es ICMP: es "me conecto por SSH y encuentro Python". Si uno falla, prueba ssh ansible@srv-db-01.contoso.local a mano antes de buscar en Ansible.
2. Comandos sueltos antes del playbook
ansible linux -i inventory.ini -m shell -a "uptime; df -h / | tail -1"srv-web-01.contoso.local | CHANGED | rc=0 >> 08:12:03 up 41 days, 2:17, 0 users, load average: 0.08, 0.05, 0.01/dev/mapper/rhel-root 50G 31G 20G 62% /...ansible web -i inventory.ini -m service -a "name=httpd state=started" --checksrv-web-01.contoso.local | SUCCESS => {"changed": false, "name": "httpd", "state": "started"}--check es el modo ensayo: dice qué cambiaría sin tocar nada. Úsalo siempre la primera vez.
3. El playbook de parcheo
---- name: Parcheo mensual de servidores Linux hosts: linux serial: 1 max_fail_percentage: 0 tasks: - name: Actualizar paquetes (RHEL) ansible.builtin.dnf: name: "*" state: latest when: ansible_os_family == "RedHat" register: dnf_result - name: Actualizar paquetes (Ubuntu) ansible.builtin.apt: upgrade: dist update_cache: true when: ansible_os_family == "Debian" register: apt_result - name: ¿Hace falta reiniciar? (RHEL) ansible.builtin.command: needs-restarting -r register: nr failed_when: nr.rc not in [0, 1] changed_when: false when: ansible_os_family == "RedHat" - name: ¿Hace falta reiniciar? (Ubuntu) ansible.builtin.stat: path: /var/run/reboot-required register: rr when: ansible_os_family == "Debian" - name: Reiniciar solo si hace falta ansible.builtin.reboot: reboot_timeout: 600 when: (nr.rc is defined and nr.rc == 1) or (rr.stat is defined and rr.stat.exists) - name: Comprobar que el servicio principal está activo ansible.builtin.service_facts: - name: Fallar si httpd no está corriendo en los web ansible.builtin.assert: that: ansible_facts.services['httpd.service'].state == 'running' fail_msg: "httpd no arrancó tras el parcheo" when: "'web' in group_names"Lo importante está en tres líneas: serial: 1 (un servidor a la vez), max_fail_percentage: 0 (si uno falla, se para todo) y el reboot condicionado. needs-restarting -r devuelve 1 cuando hace falta reiniciar, por eso se acepta como código válido.
4. Ejecutar: ensayo, real y evidencia
ansible-playbook -i inventory.ini patch.yml --check --diffPLAY [Parcheo mensual de servidores Linux] ************************************TASK [Actualizar paquetes (RHEL)] *********************************************changed: [srv-web-01.contoso.local]...PLAY RECAP ********************************************************************srv-web-01.contoso.local : ok=5 changed=1 unreachable=0 failed=0 skipped=3ansible-playbook -i inventory.ini patch.yml | tee logs/patch-2026-10.log...TASK [Reiniciar solo si hace falta] *******************************************changed: [srv-web-01.contoso.local]TASK [Fallar si httpd no está corriendo en los web] ***************************ok: [srv-web-01.contoso.local]PLAY RECAP ********************************************************************srv-web-01.contoso.local : ok=7 changed=2 unreachable=0 failed=0 skipped=2srv-web-02.contoso.local : ok=7 changed=2 unreachable=0 failed=0 skipped=2srv-db-01.contoso.local : ok=6 changed=1 unreachable=0 failed=0 skipped=3El PLAY RECAP con failed=0 y el log con tee son la evidencia que adjuntas al ticket de cambio. Si un servidor falla, Ansible se detiene ahí y los siguientes quedan sin tocar: eso es lo que quieres en una ventana de parcheo.
Consejo: ansible-playbook ... --limit srv-web-01.contoso.local ejecuta el mismo playbook en uno solo. Es la forma de probar en el servidor menos crítico antes de la ventana.
5. Lo que apuntas en el ticket
- Versión del playbook (el commit de Git), inventario usado y la fecha.
- El
PLAY RECAPcompleto y qué servidores se reiniciaron. - Si alguno falló: el error de la tarea y qué se hizo a mano.
Siguientes pasos
- Separa variables por entorno (
group_vars/web.yml) y guarda secretos conansible-vault, nunca en claro en el repo. - Con Windows, Ansible funciona por WinRM o SSH con los módulos
ansible.windows.*: el mismo playbook, otro inventario. - El siguiente paso natural es un rol:
ansible-galaxy init roles/baselinepara la configuración base de un servidor nuevo (usuarios, NTP, firewall, agente de monitorización).
¿Falta algo o en tu entorno es distinto? Escríbeme y lo corrijo.
Tech Career Lab · newsletter
¿Te ha servido? Cada dos semanas envío una idea práctica para perfiles IT: LinkedIn, proyectos, networking y procesos de selección.
Apúntate y llévate las 33 ideas →