Salta el contingut

Ansible: Automatització Simple i Potent

3. Ansible: Automatització Simple i Potent

Filosofia i Visió

Ansible va néixer amb una missió clara: fer l'automatització accessible a tothom. El seu creador, Michael DeHaan, volia una eina tan simple que qualsevol administrador de sistemes pogués començar a utilitzar-la en minuts, sense haver d'aprendre un llenguatge de programació complet ni instal·lar infraestructura complexa.

La filosofia d'Ansible es pot resumir en tres principis: simplicitat radical, arquitectura agentless, i potència sense complexitat. Aquests principis es reflecteixen en cada aspecte del disseny de l'eina.

Arquitectura d'Ansible

Ansible utilitza una arquitectura push agentless. Això significa que tens un "control node" (pot ser el teu portàtil, un servidor dedicat, o qualsevol màquina Linux amb Python instal·lat) des del qual "empentes" les configuracions cap als servidors gestionats. No hi ha cap servidor central que els nodes hagin de consultar, ni cap agent executant-se contínuament.

La comunicació es fa via SSH per a servidors Linux/Unix, o WinRM per a Windows. Això vol dir que si ja tens accés SSH als teus servidors, ja tens tot el que necessites per començar amb Ansible. No cal obrir ports addicionals al firewall, no cal instal·lar cap software especial en els servidors gestionats (més enllà de Python, que ja ve instal·lat per defecte en la majoria de distribucions Linux modernes).

Quan executes un playbook d'Ansible (ho explicarem més endavant), el control node connecta via SSH a cada servidor, copia els mòduls d'Ansible necessaris, els executa, recull els resultats, i neteja els fitxers temporals. Tot això passa de manera transparent per l'usuari.

Nota de versió: A l'hora d'escriure aquest document (gener 2025), la versió estable d'Ansible és la 2.16.x dins del paquet ansible-core, i la versió 9.x del paquet ansible complet que inclou col·leccions. Es recomana utilitzar ansible-core 2.16 com a mínim. Podeu verificar la darrera versió a la documentació oficial: https://docs.ansible.com/

Components Clau d'Ansible

Per entendre com funciona Ansible, hem de conèixer els seus components principals.

Inventari: L'inventari és on definim quins servidors volem gestionar i com agrupar-los. Pot ser tan simple com un fitxer de text amb IPs o noms de host, o tan complex com un script que genera la llista dinàmicament consultant l'API d'AWS, Azure o altres proveïdors cloud.

Un inventari bàsic podria ser així:

[webservers]
web1.example.com
web2.example.com
web3.example.com

[databases]
db1.example.com
db2.example.com

[production:children]
webservers
databases

Aquest inventari defineix dos grups: webservers i databases. També crea un grup "production" que conté tots els altres grups. Això ens permet executar tasques contra grups específics de servidors.

Playbooks: Els playbooks són fitxers YAML que descriuen l'estat desitjat dels teus sistemes. Són l'equivalent a "receptes" o "runbooks" en altres eines. El nom "playbook" ve del món esportiu: igual que un entrenador de futbol americà té un playbook amb jugades planificades, tu tens un playbook amb configuracions planificades.

Un playbook simple per instal·lar nginx podria ser:

---
- name: Configurar servidors web
  hosts: webservers
  become: yes

  tasks:
    - name: Instal·lar nginx
      apt:
        name: nginx
        state: present
        update_cache: yes

    - name: Assegurar que nginx està executant-se
      service:
        name: nginx
        state: started
        enabled: yes

Aquest playbook és auto-explicatiu, que és una de les grans fortaleses d'Ansible. Fins i tot sense conèixer la sintaxi, és fàcil entendre què fa: instal·la nginx i s'assegura que està executant-se.

Mòduls: Els mòduls són les unitats de treball d'Ansible. Cada tasca en un playbook utilitza un mòdul. Hi ha mòduls per a pràcticament tot el que puguis imaginar: gestionar paquets (apt, yum, dnf), gestionar fitxers (copy, template, file), gestionar serveis (service, systemd), gestionar usuaris (user, group), interactuar amb clouds (ec2, azure_rm, gcp_compute), gestionar bases de dades (mysql_db, postgresql_db), i moltíssims més.

Ansible ve amb centenars de mòduls integrats, i la comunitat n'ha creat milers més disponibles en col·leccions. Fins i tot pots escriure els teus propis mòduls en Python si necessites funcionalitat específica.

Roles: A mesura que els teus playbooks creixen, és útil organitzar-los en roles. Un rol és una manera d'agrupar tasques relacionades, variables, fitxers i templates en una estructura de directoris estandarditzada. Per exemple, podries tenir un rol "nginx" que conté tot el necessari per configurar nginx, i després reutilitzar aquest rol en diferents playbooks.

Variables: Ansible té un sistema de variables potent que permet parametritzar les teves configuracions. Pots definir variables a nivell d'inventari, de grup, de host, de playbook, o de rol. Pots també utilitzar facts (dades sobre els sistemes recopilades automàticament per Ansible) i registrar l'output de tasques com a variables per utilitzar-les més tard.

Templates: Els templates utilitzen el motor de plantilles Jinja2 per generar fitxers de configuració dinàmicament. Això és extremadament útil quan necessites crear fitxers de configuració que varien segons el servidor o l'entorn. Per exemple, pots tenir un template per a la configuració d'nginx que s'adapti automàticament segons la quantitat de memòria RAM del servidor.

Flux de Treball Típic amb Ansible

Vegem com seria un flux de treball típic utilitzant Ansible en un escenari real.

Imagina que has d'afegir un nou servidor web a la teva infraestructura. Primer, afegiries el nou servidor a l'inventari en el grup "webservers". Després, executaries el playbook de configuració de webservers que ja tens preparat. Ansible es connectaria al nou servidor, instal·laria tots els paquets necessaris, copiaria els fitxers de configuració, configuraría el firewall, afegiria el servidor al load balancer, i finalment verificaria que tot funciona correctament. Tot això amb una sola comanda: ansible-playbook webserver.yml.

La clau aquí és que el mateix playbook que utilitzes per configurar un nou servidor també pots utilitzar-lo per actualitzar servidors existents. Si canvies la configuració d'nginx en el teu playbook i el tornes a executar, Ansible detectarà que la configuració dels servidors existents és diferent de l'estat desitjat, i actualitzarà només els fitxers que han canviat, reiniciant el servei si és necessari.

Exemples Pràctics d'Ansible

Vegem alguns exemples pràctics per entendre millor com funciona Ansible en situacions reals.

Exemple 1: Configuració d'un servidor web complet

Imagineu que voleu configurar un servidor web amb nginx, PHP-FPM, i una aplicació web. El playbook seria així:

---
- name: Configuració completa d'un servidor web
  hosts: webservers
  become: yes
  vars:
    php_version: "8.2"
    app_name: "myapp"
    app_domain: "example.com"

  tasks:
    - name: Actualitzar cache de paquets
      apt:
        update_cache: yes
        cache_valid_time: 3600

    - name: Instal·lar paquets necessaris
      apt:
        name:
          - nginx
          - "php{{ php_version }}-fpm"
          - "php{{ php_version }}-mysql"
          - "php{{ php_version }}-curl"
          - "php{{ php_version }}-xml"
          - git
          - ufw
        state: present

    - name: Configurar firewall
      ufw:
        rule: allow
        port: "{{ item }}"
        proto: tcp
      loop:
        - '22'
        - '80'
        - '443'

    - name: Activar firewall
      ufw:
        state: enabled

    - name: Crear directori de l'aplicació
      file:
        path: "/var/www/{{ app_name }}"
        state: directory
        owner: www-data
        group: www-data
        mode: '0755'

    - name: Clonar repositori de l'aplicació
      git:
        repo: "https://github.com/company/{{ app_name }}.git"
        dest: "/var/www/{{ app_name }}"
        version: main
        force: yes
      notify: Reload nginx

    - name: Crear configuració d'nginx des de template
      template:
        src: nginx-site.conf.j2
        dest: "/etc/nginx/sites-available/{{ app_name }}"
        owner: root
        group: root
        mode: '0644'
      notify: Reload nginx

    - name: Activar site d'nginx
      file:
        src: "/etc/nginx/sites-available/{{ app_name }}"
        dest: "/etc/nginx/sites-enabled/{{ app_name }}"
        state: link
      notify: Reload nginx

    - name: Desactivar site per defecte
      file:
        path: /etc/nginx/sites-enabled/default
        state: absent
      notify: Reload nginx

    - name: Assegurar que nginx està executant-se
      service:
        name: nginx
        state: started
        enabled: yes

    - name: Assegurar que PHP-FPM està executant-se
      service:
        name: "php{{ php_version }}-fpm"
        state: started
        enabled: yes

  handlers:
    - name: Reload nginx
      service:
        name: nginx
        state: reloaded

Aquest playbook fa moltes coses, però cada pas és clar i comprensible. Primer actualitza la cache de paquets, després instal·la nginx, PHP i altres dependencies. Configura el firewall per permetre tràfic web i SSH. Crea el directori per l'aplicació i clona el codi des de Git. Genera la configuració d'nginx utilitzant un template. Finalment, s'assegura que tots els serveis estan executant-se.

Notem l'ús de "handlers": són tasques especials que només s'executen si alguna altra tasca ha fet canvis. En aquest cas, només recarreguem nginx si hem canviat la seva configuració o el codi de l'aplicació. Això evita reinicis innecessaris.

El template d'nginx (nginx-site.conf.j2) podria ser així:

server {
    listen 80;
    server_name {{ app_domain }};
    root /var/www/{{ app_name }}/public;

    index index.php index.html;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/var/run/php/php{{ php_version }}-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }

    location ~ /\.ht {
        deny all;
    }
}

Aquest template utilitza les variables que hem definit en el playbook (app_domain, app_name, php_version) per generar una configuració personalitzada per cada servidor.

Exemple 2: Gestió d'usuaris i permisos

Un altre cas d'ús comú és la gestió d'usuaris i claus SSH:

---
- name: Gestió d'usuaris del sistema
  hosts: all
  become: yes
  vars:
    admin_users:
      - name: joan
        ssh_key: "ssh-rsa AAAAB3NzaC1yc2EAAAA... joan@company.com"
        sudo: yes
      - name: maria
        ssh_key: "ssh-rsa AAAAB3NzaC1yc2EAAAA... maria@company.com"
        sudo: yes
      - name: pere
        ssh_key: "ssh-rsa AAAAB3NzaC1yc2EAAAA... pere@company.com"
        sudo: no

  tasks:
    - name: Crear usuaris administradors
      user:
        name: "{{ item.name }}"
        state: present
        shell: /bin/bash
        groups: "{{ 'sudo' if item.sudo else '' }}"
        append: yes
      loop: "{{ admin_users }}"

    - name: Afegir claus SSH públiques
      authorized_key:
        user: "{{ item.name }}"
        key: "{{ item.ssh_key }}"
        state: present
      loop: "{{ admin_users }}"

    - name: Configurar sudoers per usuaris admin
      lineinfile:
        path: /etc/sudoers.d/admin_users
        line: "{{ item.name }} ALL=(ALL) NOPASSWD:ALL"
        create: yes
        mode: '0440'
        validate: 'visudo -cf %s'
      loop: "{{ admin_users }}"
      when: item.sudo

    - name: Deshabilitar autenticació amb password per SSH
      lineinfile:
        path: /etc/ssh/sshd_config
        regexp: '^PasswordAuthentication'
        line: 'PasswordAuthentication no'
        state: present
      notify: Restart sshd

    - name: Assegurar que només root pot accedir a /root
      file:
        path: /root
        mode: '0700'

  handlers:
    - name: Restart sshd
      service:
        name: sshd
        state: restarted

Aquest playbook crea usuaris, configura les seves claus SSH, estableix permisos sudo, i endureix la configuració de SSH. Tota la informació dels usuaris està definida en una variable, fent fàcil afegir o eliminar usuaris simplement editant la llista.

Avantatges d'Ansible

Després d'aquests exemples, podem veure clarament els avantatges d'Ansible. La seva simplicitat fa que sigui accessible per a principiants: la sintaxi YAML és llegible i intuïtiva, no cal aprendre un llenguatge de programació complet per començar. La seva arquitectura agentless significa que pots començar a utilitzar-lo immediatament sense instal·lar agents en els teus servidors. És idempotent, així que pots executar el mateix playbook múltiples vegades sense por de trencar res. Té una comunitat enorme amb milers de roles i mòduls disponibles. I finalment, escala bé des d'uns pocs servidors fins a milers, especialment quan s'utilitza amb Ansible Tower o AWX per gestió centralitzada.

Referència oficial: La documentació completa d'Ansible està disponible a https://docs.ansible.com/. Per començar, es recomana el "Getting Started Guide": https://docs.ansible.com/ansible/latest/getting_started/index.html