Esempi Pyinfra
Esempi Pyinfra
Questa pagina raccoglie esempi leggibili e progressivi: si parte dal minimo indispensabile e si arriva a pattern piu vicini a un uso reale.
Prima idea da fissare
Un progetto pyinfra semplice ha di solito almeno due file:
inventory.py deploy.py
L'inventory descrive i target. Il deploy descrive cosa fare.
Esempio base 1: esecuzione su host locali
Se vuoi solo capire la CLI:
pyinfra @local exec -- echo "hello world"
Questo serve quando vuoi verificare:
- che pyinfra sia installato
- che il runner funzioni
- che l'output arrivi correttamente
Tradotto in pratica:
- se il comando parte, sai che
pyinfraesiste davvero sul manager - se l'operation viene eseguita, sai che il runner locale funziona
- se vedi
hello world, sai che l'output viene raccolto correttamente
Esempio concreto: devo controllare 10 server
Questo e uno dei casi piu utili per capire bene come ragiona pyinfra.
Scenario:
- hai una macchina manager
- da quella macchina vuoi controllare o configurare 10 server target
Cosa deve esserci sul manager
Sul manager devono esserci:
Pythonpyinfra- i file di progetto, ad esempio
inventory.pyedeploy.py - connettivita SSH verso i 10 server
- chiavi SSH oppure credenziali adatte
- eventuali permessi
sudose il deploy deve fare operazioni amministrative
In sintesi:
- il manager e la macchina da cui lanci i comandi
- il manager contiene la logica
- il manager non deve essere per forza uno dei 10 server target
Cosa deve esserci sui 10 server target
In generale pyinfra e agentless, quindi non richiede un agente pyinfra installato sui target.
Di solito sui target servono:
- accesso SSH dal manager
- un utente valido con cui entrare
- eventuale
sudose devi amministrare il sistema - shell e strumenti di base coerenti con quello che vuoi eseguire
In pratica:
- sui target non installi pyinfra
- sui target devi rendere possibile l'accesso e l'esecuzione delle operazioni richieste
Riepilogo semplice
- manager: installi pyinfra
- target: non installi pyinfra
- manager -> target: usi SSH
Esempio con 10 server
Inventory minimale:
all_servers = [
"srv01.example.net",
"srv02.example.net",
"srv03.example.net",
"srv04.example.net",
"srv05.example.net",
"srv06.example.net",
"srv07.example.net",
"srv08.example.net",
"srv09.example.net",
"srv10.example.net",
]
Se vuoi dividere per gruppi:
web_servers = [
"srv01.example.net",
"srv02.example.net",
"srv03.example.net",
"srv04.example.net",
"srv05.example.net",
]
db_servers = [
"srv06.example.net",
"srv07.example.net",
]
misc_servers = [
"srv08.example.net",
"srv09.example.net",
"srv10.example.net",
]
Primo test vero sui 10 server
Prima di fare configurazione, conviene fare un controllo innocuo.
Deploy:
from pyinfra.operations import server
server.shell(
name="Show hostname and uptime",
commands=[
"hostname",
"uptime",
],
)
Esecuzione dal manager:
pyinfra inventory.py deploy.py
Questo test serve a verificare:
- che il manager raggiunga tutti i target
- che l'utente SSH funzioni
- che pyinfra riesca a eseguire comandi su tutti i server
- che i risultati siano leggibili nell'output
Esempio di controllo piu utile
Se vuoi controllare disco e un servizio:
from pyinfra.operations import server
server.shell(
name="Check disk",
commands=["df -h /"],
)
server.shell(
name="Check nginx service",
commands=["systemctl is-active nginx || true"],
)
Questo non modifica nulla, ma ti da gia una fotografia dei 10 server.
Esempio di deploy reale
Se vuoi installare vim e assicurarti che nginx sia attivo:
from pyinfra.operations import apt, systemd
apt.packages(
name="Install vim",
packages=["vim"],
update=True,
)
systemd.service(
name="Ensure nginx is running",
service="nginx",
running=True,
enabled=True,
)
In questo caso:
- sul manager hai solo pyinfra e i file del deploy
- sui target non hai pyinfra, ma hai accesso SSH e permessi adeguati
- pyinfra esegue il lavoro dal manager verso tutti i target
Come pensarlo mentalmente
Formula utile:
- manager = cervello
- target = macchine su cui applicare le operazioni
Pyinfra gira nel cervello, non nelle braccia.
Esempio base 2: inventory minimale
web_servers = [
"web-01.example.net",
"web-02.example.net",
]
Qui hai solo una lista di host.
Esempio base 3: operation semplice
from pyinfra.operations import server
server.shell(
name="Show uptime",
commands=["uptime"],
)
Esecuzione:
pyinfra inventory.py deploy.py
Esempio base 4: installare pacchetti
from pyinfra.operations import apt
apt.packages(
name="Install packages",
packages=["vim", "curl"],
update=True,
)
Questo e un esempio di operation idempotente: se i pacchetti sono gia presenti, pyinfra non rifara cambiamenti inutili.
Esempio base 5: gestire file
from pyinfra.operations import files
files.file(
name="Ensure app log exists",
path="/var/log/app.log",
user="app",
group="app",
mode="644",
)
Esempio base 6: gestire servizi
from pyinfra.operations import systemd
systemd.service(
name="Ensure nginx is running",
service="nginx",
running=True,
enabled=True,
)
Esempio intermedio 1: dati host
Inventory:
app_servers = [
("app-01.example.net", {"install_nginx": True}),
("app-02.example.net", {"install_nginx": False}),
]
Deploy:
from pyinfra import host
from pyinfra.operations import apt
if host.data.get("install_nginx"):
apt.packages(
name="Install nginx",
packages=["nginx"],
update=True,
)
Qui usi Python per rendere il deploy diverso in base ai dati del target.
Esempio intermedio 2: gruppi
from pyinfra import host, local
from pyinfra.operations import server
if "web_servers" in host.groups:
local.include("tasks/web.py")
if "db_servers" in host.groups:
local.include("tasks/database.py")
server.shell(
name="Run everywhere",
commands=["hostname"],
)
Questo pattern e utile quando vuoi dividere la logica in file piu piccoli.
Esempio avanzato 1: deploy locale che richiama un helper Python
Questo e il pattern usato nel progetto Proxmox Web UI: pyinfra gira su @local ma esegue un helper esterno che contiene la logica vera.
#!/usr/bin/env python3
import os
import shlex
from pathlib import Path
from pyinfra.operations import server
BASE_DIR = Path(__file__).resolve().parents[1]
python_bin = os.environ.get("PYTHON_BIN", str(BASE_DIR / "venv" / "bin" / "python"))
helper = BASE_DIR / "lib" / "proxmox_ssh.py"
cmd = f"{shlex.quote(python_bin)} {shlex.quote(str(helper))}"
server.shell(
name=f"Create Proxmox VM {os.environ.get('VM_NAME', '')}",
commands=[cmd],
)
Idea pratica:
- pyinfra fa da orchestratore locale
- il file helper fa il lavoro specializzato
- le variabili ambiente passano i parametri del job
Esempio avanzato 2: usare pyinfra come backend di una UI
Nel caso GazziNet il flusso e:
- la UI riceve input dell'utente
- la UI valida i campi
- la UI crea un job con stato e log
- il job lancia
pyinfra @local deploy.py -y - pyinfra richiama un helper che esegue SSH su Proxmox
Questa soluzione e utile quando vuoi:
- separare presentazione e automazione
- conservare log testuali chiari
- evitare che il browser parli direttamente con Proxmox
Esempio avanzato 3: rete dinamica e validazione
Nel progetto VM automatiche la UI gestisce:
- DHCP oppure IP statico
- bridge selezionabile
- VLAN opzionale
- validazione preventiva bridge + VLAN
- ricerca del primo VMID libero
In questo caso pyinfra non decide la rete da solo: riceve i valori gia normalizzati e li passa al backend di provisioning.
Esempio dry run
pyinfra inventory.py deploy.py --dry
Utile prima di un deploy reale.
Pattern reali da ricordare
- tenere inventory e deploy in Git
- separare logica generale e helper specializzati
- validare input prima di lanciare pyinfra
- non mescolare segreti nei file versionati
- aggiungere sempre un modo semplice per verificare il risultato