geek.sonde.uk updated:
Ansible Apache Debian From Source

Apache autoconsistente con Ansible

Imparerai a usare Ansible per compilare Apache da sorgente e installarlo in /sw/apache/ su deb1.lan come cartella completamente autoconsistente — con bin/, conf/, logs/, modules/ e tutto il necessario al suo interno. Zero dipendenza dai path di sistema.

~20 min read Linux / Debian LAN Difficulty: Intermediate
01

Perché compilare da sorgente

Installare Apache tramite apt distribuisce i file in decine di path di sistema (/usr/sbin, /etc/apache2, /var/log/apache2, ecc.). L'approccio prefix — compilare con --prefix=/sw/apache — genera invece una cartella completamente autoconsistente:

tree /sw/apache/
/sw/apache/
├── bin/          # httpd, apachectl, ab, htpasswd, ...
├── conf/         # httpd.conf e file di configurazione
│   ├── extra/    # VirtualHost, SSL, MPM, ...
│   └── original/ # backup dei conf originali
├── htdocs/       # document root di default
├── logs/         # error_log, access_log, httpd.pid
├── modules/      # mod_ssl.so, mod_rewrite.so, ...
├── lib/
├── include/
└── man/

Vantaggi concreti: nessun conflitto con altri Apache sul sistema, spostamento o backup con un semplice tar, versioni multiple in parallelo in prefissi diversi, e disinstallazione pulita con un solo rm -rf /sw/apache.

02

Prerequisiti

Prima di iniziare assicurati di avere:

  • Control node: Ansible installato sulla tua macchina locale (qualsiasi OS)
  • Host remoto deb1.lan raggiungibile via SSH con un utente sudo
  • Chiave SSH già copiata sull'host (ssh-copy-id [email protected])
  • Connessione Internet su deb1.lan per scaricare i sorgenti (o mirror locale)
⚠️
Tempo di compilazione

La compilazione da sorgente richiede 3–8 minuti a seconda della CPU di deb1.lan. Il playbook è idempotente: se /sw/apache/bin/httpd esiste già, il passo di compilazione viene saltato completamente.

Verifica la connettività prima di procedere:

bash
ansible webservers -m ping
03

Struttura del progetto

Creiamo la directory di lavoro Ansible sul control node:

bash
mkdir -p ansible-apache/{inventory,templates}
cd ansible-apache

La struttura finale del progetto:

tree ansible-apache/
ansible-apache/
├── inventory/
│   └── hosts.ini
├── templates/
│   ├── vhost-deb1.conf.j2   # VirtualHost Jinja2
│   └── apache-sw.service.j2 # unit systemd Jinja2
├── ansible.cfg
└── apache.yml               # playbook principale
04

Inventario e configurazione

ini inventory/hosts.ini
[webservers]
deb1.lan

[webservers:vars]
ansible_user=ansible
ansible_become=true
ansible_become_method=sudo
ini ansible.cfg
[defaults]
inventory         = inventory/hosts.ini
remote_user       = ansible
host_key_checking = False
stdout_callback   = yaml

[privilege_escalation]
become        = True
become_method = sudo
05

Template Jinja2

Creiamo due template Jinja2: il VirtualHost di Apache e la unit systemd per gestirlo come servizio. Entrambi usano le stesse variabili definite nel playbook.

VirtualHost — va in /sw/apache/conf/extra/, all'interno del prefix autoconsistente:

apache templates/vhost-deb1.conf.j2
# VirtualHost generato da Ansible — non modificare manualmente
<VirtualHost *:{{ apache_port }}>

    ServerName   {{ apache_server_name }}
    DocumentRoot {{ apache_prefix }}/htdocs

    <Directory "{{ apache_prefix }}/htdocs">
        Options       Indexes FollowSymLinks
        AllowOverride All
        Require       all granted
    </Directory>

    ErrorLog  {{ apache_prefix }}/logs/error.log
    CustomLog {{ apache_prefix }}/logs/access.log combined

</VirtualHost>

Unit systemd — permette di usare systemctl start|stop|restart apache-sw:

systemd templates/apache-sw.service.j2
[Unit]
Description=Apache HTTP Server ({{ apache_prefix }})
After=network.target

[Service]
Type=forking
ExecStart={{ apache_prefix }}/bin/apachectl start
ExecStop={{ apache_prefix }}/bin/apachectl stop
ExecReload={{ apache_prefix }}/bin/apachectl graceful
PIDFile={{ apache_prefix }}/logs/httpd.pid
PrivateTmp=true
Restart=on-failure

[Install]
WantedBy=multi-user.target
06

Playbook apache.yml

Il playbook si divide in tre blocchi logici: installazione delle dipendenze di build, compilazione da sorgente con prefix, e configurazione del servizio. La variabile chiave è apache_prefix: /sw/apache — tutto il resto ne deriva.

yaml apache.yml
---
- name: Compila e installa Apache da sorgente in /sw/apache
  hosts: webservers
  become: true

  # ── Variabili ─────────────────────────────────────────────────
  vars:
    apache_prefix:      /sw/apache
    apache_version:     "2.4.62"        # aggiorna qui per upgrade
    apache_port:        80
    apache_server_name: deb1.lan
    apache_user:        www-data
    apache_group:       www-data
    apache_src_dir:     /tmp/httpd-build

  # ── Task ──────────────────────────────────────────────────────
  tasks:

    # ── BLOCCO 1: dipendenze di compilazione ──────────────────────

    - name: Aggiorna la cache APT
      ansible.builtin.apt:
        update_cache: true
        cache_valid_time: 3600

    - name: Installa dipendenze di compilazione
      ansible.builtin.apt:
        name:
          - build-essential
          - wget
          - libapr1-dev
          - libaprutil1-dev
          - libpcre2-dev
          - libssl-dev
          - libxml2-dev
          - zlib1g-dev
        state: present

    # ── BLOCCO 2: compilazione da sorgente ────────────────────────

    - name: Crea la directory di build temporanea
      ansible.builtin.file:
        path:  "{{ apache_src_dir }}"
        state: directory
        mode:  "0755"

    - name: Scarica i sorgenti Apache {{ apache_version }}
      ansible.builtin.get_url:
        url:  "https://downloads.apache.org/httpd/httpd-{{ apache_version }}.tar.gz"
        dest: "{{ apache_src_dir }}/httpd-{{ apache_version }}.tar.gz"
        mode: "0644"

    - name: Estrai i sorgenti
      ansible.builtin.unarchive:
        src:        "{{ apache_src_dir }}/httpd-{{ apache_version }}.tar.gz"
        dest:       "{{ apache_src_dir }}"
        remote_src: true
        creates:    "{{ apache_src_dir }}/httpd-{{ apache_version }}"

    - name: Compila e installa Apache in {{ apache_prefix }}
      ansible.builtin.shell: |
        ./configure \
          --prefix={{ apache_prefix }} \
          --enable-ssl \
          --enable-so \
          --enable-rewrite \
          --enable-deflate \
          --enable-headers \
          --with-mpm=event
        make -j$(nproc)
        make install
      args:
        chdir:   "{{ apache_src_dir }}/httpd-{{ apache_version }}"
        creates: "{{ apache_prefix }}/bin/httpd"  # idempotenza: salta se già installato

    # ── BLOCCO 3: configurazione del servizio ─────────────────────

    - name: Imposta User e Group in httpd.conf
      ansible.builtin.lineinfile:
        path:   "{{ apache_prefix }}/conf/httpd.conf"
        regexp: "^{{ item.key }}"
        line:   "{{ item.key }} {{ item.value }}"
      loop:
        - { key: "User",  value: "{{ apache_user }}"  }
        - { key: "Group", value: "{{ apache_group }}" }
      notify: Riavvia Apache

    - name: Abilita l'include del VirtualHost in httpd.conf
      ansible.builtin.lineinfile:
        path:  "{{ apache_prefix }}/conf/httpd.conf"
        line:  "Include conf/extra/vhost-deb1.conf"
        state: present
      notify: Riavvia Apache

    - name: Distribuisce il template VirtualHost in conf/extra/
      ansible.builtin.template:
        src:  templates/vhost-deb1.conf.j2
        dest: "{{ apache_prefix }}/conf/extra/vhost-deb1.conf"
        mode: "0644"
      notify: Riavvia Apache

    - name: Copia la pagina index di benvenuto in htdocs/
      ansible.builtin.copy:
        content: |
          <!DOCTYPE html>
          <html><head><meta charset="utf-8">
          <title>{{ apache_server_name }}</title></head>
          <body>
          <h1>Apache su {{ apache_server_name }} funziona!</h1>
          <p>Installato da Ansible — prefix: {{ apache_prefix }}</p>
          </body></html>
        dest:  "{{ apache_prefix }}/htdocs/index.html"
        owner: "{{ apache_user }}"
        group: "{{ apache_group }}"
        mode:  "0644"

    - name: Imposta ownership ricorsiva su {{ apache_prefix }}/logs e htdocs
      ansible.builtin.file:
        path:    "{{ item }}"
        owner:   "{{ apache_user }}"
        group:   "{{ apache_group }}"
        recurse: true
      loop:
        - "{{ apache_prefix }}/htdocs"
        - "{{ apache_prefix }}/logs"

    - name: Distribuisce la unit systemd apache-sw.service
      ansible.builtin.template:
        src:  templates/apache-sw.service.j2
        dest: /etc/systemd/system/apache-sw.service
        mode: "0644"
      notify:
        - Ricarica systemd
        - Riavvia Apache

    - name: Abilita e avvia il servizio apache-sw
      ansible.builtin.systemd:
        name:         apache-sw
        state:        started
        enabled:      true
        daemon_reload: true

  # ── Handlers ──────────────────────────────────────────────────
  handlers:

    - name: Ricarica systemd
      ansible.builtin.systemd:
        daemon_reload: true

    - name: Riavvia Apache
      ansible.builtin.systemd:
        name:  apache-sw
        state: restarted
ℹ️
Idempotenza sul passo di compilazione

Il parametro creates: "{{ apache_prefix }}/bin/httpd" è la chiave: se il binario esiste già, Ansible salta l'intero blocco configure → make → make install. Per forzare una ricompilazione basta rimuovere /sw/apache/bin/httpd sull'host remoto e rieseguire il playbook.

⚠️
Upgrade di versione

Per aggiornare Apache basta cambiare apache_version nelle vars e rieseguire il playbook. Poiché il binary check usa il path e non la versione, valuta di aggiungere un task dedicato che confronti httpd -v con la versione desiderata prima del blocco di build.

07

Esecuzione del playbook

Come sempre, un dry-run prima dell'esecuzione reale:

bash
# Dry-run: simula senza modificare
ansible-playbook apache.yml --check

# Esecuzione reale (la compilazione richiede qualche minuto)
ansible-playbook apache.yml

Output atteso a fine esecuzione:

output
PLAY RECAP *****************************************************
deb1.lan : ok=12  changed=9  unreachable=0  failed=0
ℹ️
Seconda esecuzione

Eseguendo il playbook una seconda volta, il passo di compilazione mostrerà skipping (il binary esiste già) e tutti gli altri task mostreranno ok invece di changed. Questo è il comportamento corretto dell'idempotenza Ansible.

08

Verifica

Controlla la versione di Apache installata nel prefix:

bash
ansible webservers -m command -a "/sw/apache/bin/httpd -v"

Verifica lo stato del servizio systemd:

bash
ansible webservers -m command -a "systemctl status apache-sw --no-pager"

Verifica la struttura autoconsistente creata nel prefix:

bash
ansible webservers -m command -a "ls /sw/apache/"
output atteso
bin  build  cgi-bin  conf  error  htdocs  icons  include  lib  logs  man  modules

Infine, testa la risposta HTTP dalla tua macchina locale:

bash
curl -si http://deb1.lan/ | head -5
Installazione completata

Se vedi HTTP/1.1 200 OK e il servizio è active (running), Apache è operativo in /sw/apache/ come installazione completamente autoconsistente, gestita da systemd con il nome apache-sw.