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.
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:
/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.
Prerequisiti
Prima di iniziare assicurati di avere:
- Control node: Ansible installato sulla tua macchina locale (qualsiasi OS)
- Host remoto
deb1.lanraggiungibile via SSH con un utentesudo - Chiave SSH già copiata sull'host (
ssh-copy-id [email protected]) - Connessione Internet su
deb1.lanper scaricare i sorgenti (o mirror locale)
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:
ansible webservers -m ping
Struttura del progetto
Creiamo la directory di lavoro Ansible sul control node:
mkdir -p ansible-apache/{inventory,templates}
cd ansible-apache
La struttura finale del progetto:
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
Inventario e configurazione
[webservers]
deb1.lan
[webservers:vars]
ansible_user=ansible
ansible_become=true
ansible_become_method=sudo
[defaults]
inventory = inventory/hosts.ini
remote_user = ansible
host_key_checking = False
stdout_callback = yaml
[privilege_escalation]
become = True
become_method = sudo
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:
# 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:
[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
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.
---
- 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
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.
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.
Esecuzione del playbook
Come sempre, un dry-run prima dell'esecuzione reale:
# 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:
PLAY RECAP *****************************************************
deb1.lan : ok=12 changed=9 unreachable=0 failed=0
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.
Verifica
Controlla la versione di Apache installata nel prefix:
ansible webservers -m command -a "/sw/apache/bin/httpd -v"
Verifica lo stato del servizio systemd:
ansible webservers -m command -a "systemctl status apache-sw --no-pager"
Verifica la struttura autoconsistente creata nel prefix:
ansible webservers -m command -a "ls /sw/apache/"
bin build cgi-bin conf error htdocs icons include lib logs man modules
Infine, testa la risposta HTTP dalla tua macchina locale:
curl -si http://deb1.lan/ | head -5
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.