Cómo utilicé Ansible para automatizar las actualizaciones en casa

En casa, tengo dispositivos informáticos que funcionan las 24 horas del día, los 7 días de la semana. Para mantenerlos actualizados e instalar actualizaciones automáticamente, dejo el trabajo a Ansible. Si es completamente nuevo en Ansible, encontrará una buena introducción en:

Mi red doméstica incluye los siguientes dispositivos:

  • Dos Raspberry Pi con sistema operativo Raspbian
  • Mi host KVM que ejecuta Debian 10 Buster
  • Dos hosts RHEL 8
  • Un Synology DS213air
  • Cuatro hosts RHEL en una red aislada

El host marcado con el cuadrado rojo es mi nodo de control de Ansible. Su trabajo es actualizar mi entorno Linux. Lo elegí porque este host puede llegar a todos los demás hosts del entorno. Otros hosts, por ejemplo, los hosts Red Hat Enterprise Linux 7 (RHEL) -Ansible, solo pueden llegar a los hosts dentro de la red aislada.

Índice

condiciones previas

En todos mis hosts, hay una cuenta de usuario que puede usar sudo para ejecutar comandos con privilegios de root. Para mayor comodidad, creé un par de claves SSH y distribuí la clave pública SSH a los hosts que quiero actualizar con Ansible. Si necesita ayuda para generar las claves SSH, consulte Usar ssh-keygen y compartir para la autenticación con clave en Linux por Tyler Carrigan.

Para usar el host RHEL8-calamar Como nodo de control de Ansible, tendré que activar un repositorio que proporcione Ansible e instalarlo:

$ sudo subscription-manager repos --enable=ansible-2.9-for-rhel-8-x86_64-rpms
$ sudo dnf -y install ansible

Para otras distribuciones consultar con la oficial Documentación.

El archivo de configuración predeterminado de Ansible se encuentra en /etc/ansible/ansible.cfg. Dado que este archivo es enviado y controlado por el paquete RPM, me gusta crear un archivo de configuración personalizado en ~/.ansible.cfg simplemente copiando el original y editándolo. Solo hice algunos cambios:

$ egrep -v "^ $ | ^ # | ^ [" ~/.ansible.cfg
inventory      = ~/ansible/hosts
private_key_file = /home/user/.ssh/ansible_id_rsa

As you can see, I've created an ansible directory in my HOME directory to store my host inventory file. I've also specified the path to the SSH private key Ansible should use to connect to the nodes on the network.

With this configuration, all the magic is controlled from my HOME directory, and I won't need any root privileges on my Ansible Control Node to get the following job done.

Create a static inventory file

In this use case, I use a static inventory file by putting my hosts with their FQDN into the ~/ansible/hosts file.

[special]
localhost torre-pc.lan

[yum]
rhel7-ansible.private1 rhel7-t1.private1 rhel8-t1.private1 rpm-repo-r8.private1 podhost-r8-1.lan

[apt]
raspi-sht21.lan pi-agujero.lan

[ipkg]
diskstation.lan

Como puede ver, he agrupado los hosts en mi red por el administrador de paquetes que están usando. Esto es útil al crear el libro de jugadas para actualizarlos. El grupo [special] contiene mi propio nodo de control Ansible y mi hipervisor KVM, donde se ejecuta mi nodo de control Ansible.

Para obtener más información sobre el inventario de Ansible, consulte: Cómo construir tu inventario.

Comprobar conectividad

Antes de crear el libro de jugadas que actualiza mis hosts, verifico si mi nodo de control Ansible RHEL8-calamar puedo conectarme a todos mis hosts usando el siguiente comando ad-hoc:

$ ansible all -m ping -T 30
rhel7-t1.private1 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}
rhel7-ansible.private1 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}
rpm-repo-r8.private1 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "ping": "pong"
}
rhel8-t1.private1 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "ping": "pong"
}
podhost-r8-1.lan | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "ping": "pong"
}
[WARNING]: Platform linux on host tower-pc.lan is using the discovered Python
interpreter at /usr/bin/python, but future installation of another Python
interpreter could change this. See https://docs.ansible.com/ansible/2.9/referen
ce_appendices/interpreter_discovery.html for more information.
tower-pc.lan | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}
[WARNING]: sftp transfer mechanism failed on [diskstation.lan]. Use
ANSIBLE_DEBUG=1 to see detailed information
[WARNING]: Platform linux on host diskstation.lan is using the discovered
Python interpreter at /usr/bin/python, but future installation of another
Python interpreter could change this. See https://docs.ansible.com/ansible/2.9/
reference_appendices/interpreter_discovery.html for more information.
diskstation.lan | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}
[WARNING]: Platform linux on host pi-hole.lan is using the discovered Python
interpreter at /usr/bin/python, but future installation of another Python
interpreter could change this. See https://docs.ansible.com/ansible/2.9/referen
ce_appendices/interpreter_discovery.html for more information.
pi-hole.lan | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}
[WARNING]: Platform linux on host raspi-sht21.lan is using the discovered
Python interpreter at /usr/bin/python, but future installation of another
Python interpreter could change this. See https://docs.ansible.com/ansible/2.9/
reference_appendices/interpreter_discovery.html for more information.
raspi-sht21.lan | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}

El tiempo de espera predeterminado para una conexión de Ansible es de 10 segundos. Porque mi Estación de disco Synology está en modo de suspensión la mayor parte del tiempo, uso la opción -T para especificar un tiempo de espera de 30 segundos para darle tiempo a despertar.

Las advertencias no me molestan en este momento, así que paso a crear el libro de jugadas.

Si quieres saber más sobre los comandos ad-hoc, lee Introducción a los comandos ad-hoc en documentos oficiales.

el libro de jugadas

Mi libro de jugadas consta de tres partes. Cada juego ejecuta una tarea en hosts que pertenecen a un determinado grupo en mi inventario, excepto [special]. En este ejemplo simple, cada juego se conecta a un grupo de hosts, los actualiza y luego los reinicia en caso de que se instale alguna actualización.

Para determinar si se han instalado actualizaciones, registro variables que almacenan los valores de retorno de las tareas donde las guardé. Los uso para verificar si el estado de una tarea ha cambiado. Si es así, el sistema se reiniciará. Aquí está el libro de jugadas:

---
- hosts: yum
  tasks:
  - name: Update all installed packages using YUM module
    yum:
      name: '*'
      state: latest
      update_cache: yes
      update_only: yes
    register: yum_update_status

  - name: Remove packates not needed anymore
    yum:
      autoremove: yes

  - name: Reboot when packages were updated
    reboot:
    when: yum_update_status.changed

- hosts: apt
  tasks:
  - name: Update all installed packages using APT module
    apt:
      name: '*'
      state: latest
      update_cache: yes
      only_upgrade: yes
    register: apt_update_status

  - name: Remove packages not needed anymore
    apt:
      autoremove: yes

  - name: Reboot when packages were updated
    reboot:
      post_reboot_delay: 60
    when: apt_update_status.changed

- hosts: ipkg
  tasks:
  - name: Update the Packages installed on Diskstation
    command: /opt/bin/ipkg update && /opt/bin/ipkg upgrade

Como te habrás dado cuenta, el estación de disco no se ha reiniciado. Esto se debe a que solo se actualizarán las herramientas del espacio de usuario y no es necesario reiniciar. Si hay disponible una nueva versión del sistema operativo para el estación de disco, lo actualizaré manualmente porque todavía no hay un módulo Ansible para él. Es una historia similar con los anfitriones. tour-pc.lan y rhel8-squid.lan. Los dejé fuera de este libro de jugadas a propósito. Mi nodo de control Ansible y mi hipervisor KVM son lo suficientemente importantes para mí que los actualizo manualmente.

Aquí hay un vistazo a la primera ejecución del libro de jugadas:

$ ansible-playbook -T 30 -b --ask-become-pass pkg_update.yml
BECOME password:

PLAY [yum] **************************************************************************************************************************************

TASK [Gathering Facts] **************************************************************************************************************************
ok: [podhost-r8-1.lan]
ok: [rhel7-t1.private1]
ok: [rhel8-t1.private1]
ok: [rpm-repo-r8.private1]
ok: [rhel7-ansible.private1]

TASK [Update all installed packages using YUM module] *******************************************************************************************
ok: [rhel8-t1.private1]
ok: [podhost-r8-1.lan]
ok: [rpm-repo-r8.private1]
ok: [rhel7-t1.private1]
ok: [rhel7-ansible.private1]

TASK [Remove packates not needed anymore] *******************************************************************************************************
ok: [rhel7-t1.private1]
ok: [rhel7-ansible.private1]
ok: [rhel8-t1.private1]
ok: [podhost-r8-1.lan]
ok: [rpm-repo-r8.private1]

TASK [Reboot when packages were updated] ********************************************************************************************************
skipping: [rhel7-ansible.private1]
skipping: [rhel7-t1.private1]
skipping: [rhel8-t1.private1]
skipping: [rpm-repo-r8.private1]
skipping: [podhost-r8-1.lan]

PLAY [apt] **************************************************************************************************************************************

TASK [Gathering Facts] **************************************************************************************************************************
ok: [pi-hole.lan]
ok: [raspi-sht21.lan]

TASK [Update all installed packages using APT module] *******************************************************************************************
changed: [pi-hole.lan]
changed: [raspi-sht21.lan]

TASK [Remove packages not needed anymore] *******************************************************************************************************
ok: [pi-hole.lan]
ok: [raspi-sht21.lan]

TASK [Reboot when packages were updated] ********************************************************************************************************
changed: [pi-hole.lan]
changed: [raspi-sht21.lan]

PLAY [ipkg] *************************************************************************************************************************************

TASK [Gathering Facts] **************************************************************************************************************************
ok: [diskstation.lan]

TASK [Update the Packages installed on Diskstation] *********************************************************************************************
changed: [diskstation.lan]

PLAY RECAP **************************************************************************************************************************************
diskstation.lan            : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
pi-hole.lan                : ok=4    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
podhost-r8-1.lan           : ok=3    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0   
raspi-sht21.lan            : ok=4    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
rhel7-ansible.private1     : ok=3    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0   
rhel7-t1.private1          : ok=3    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0   
rhel8-t1.private1          : ok=3    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0   
rpm-repo-r8.private1       : ok=3    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0   

Como puede ver, mis máquinas RHEL ya estaban actualizadas. No hay nada que actualizar o eliminar, por lo que no es necesario reiniciar. Mi Raspberry Pis, por otro lado, tenía actualizaciones y se instalaron. Luego se reiniciaron ambos dispositivos. el estación de disco tiene su estado modificado, demasiado. Pero sé que es porque uso el command módulo, que devuelve modificado cada vez que se ejecuta, ya sea que algo en su nodo haya cambiado o no.

Conclusión

En este artículo, le mostré un ejemplo simple pero no muy sofisticado de cómo mantengo actualizados mis dispositivos Linux en casa usando la automatización de Ansible. Le muestra cómo usar los grupos en su inventario en diferentes partes de su libro de jugadas usando algunos de los módulos de Ansible y un solo comando.

Artículos de interés

Subir