Esempi Pyinfra: differenze tra le versioni
(Espansione esempi Pyinfra con casi base e avanzati) |
(Aggiunta) |
||
| (Una versione intermedia di uno stesso utente non è mostrata) | |||
| Riga 14: | Riga 14: | ||
== Esempio base 1: esecuzione su host locali == | == Esempio base 1: esecuzione su host locali == | ||
Se vuoi solo capire la CLI: | Se vuoi solo capire la CLI: | ||
< | <pre> | ||
pyinfra @local exec -- echo "hello world" | pyinfra @local exec -- echo "hello world" | ||
</ | </pre> | ||
Questo serve quando vuoi verificare: | Questo serve quando vuoi verificare: | ||
| Riga 22: | Riga 22: | ||
* che il runner funzioni | * che il runner funzioni | ||
* che l'output arrivi correttamente | * che l'output arrivi correttamente | ||
Tradotto in pratica: | |||
* se il comando parte, sai che <code>pyinfra</code> esiste davvero sul manager | |||
* se l'operation viene eseguita, sai che il runner locale funziona | |||
* se vedi <code>hello world</code>, 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: | |||
* <code>Python</code> | |||
* <code>pyinfra</code> | |||
* i file di progetto, ad esempio <code>inventory.py</code> e <code>deploy.py</code> | |||
* connettivita SSH verso i 10 server | |||
* chiavi SSH oppure credenziali adatte | |||
* eventuali permessi <code>sudo</code> se 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 <code>sudo</code> se 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: | |||
<pre> | |||
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", | |||
] | |||
</pre> | |||
Se vuoi dividere per gruppi: | |||
<pre> | |||
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", | |||
] | |||
</pre> | |||
== Primo test vero sui 10 server == | |||
Prima di fare configurazione, conviene fare un controllo innocuo. | |||
Deploy: | |||
<pre> | |||
from pyinfra.operations import server | |||
server.shell( | |||
name="Show hostname and uptime", | |||
commands=[ | |||
"hostname", | |||
"uptime", | |||
], | |||
) | |||
</pre> | |||
Esecuzione dal manager: | |||
<pre> | |||
pyinfra inventory.py deploy.py | |||
</pre> | |||
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: | |||
<pre> | |||
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"], | |||
) | |||
</pre> | |||
Questo non modifica nulla, ma ti da gia una fotografia dei 10 server. | |||
== Esempio di deploy reale == | |||
Se vuoi installare <code>vim</code> e assicurarti che <code>nginx</code> sia attivo: | |||
<pre> | |||
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, | |||
) | |||
</pre> | |||
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 == | == Esempio base 2: inventory minimale == | ||
< | <pre> | ||
web_servers = [ | web_servers = [ | ||
"web-01.example.net", | "web-01.example.net", | ||
"web-02.example.net", | "web-02.example.net", | ||
] | ] | ||
</ | </pre> | ||
Qui hai solo una lista di host. | Qui hai solo una lista di host. | ||
== Esempio base 3: operation semplice == | == Esempio base 3: operation semplice == | ||
< | <pre> | ||
from pyinfra.operations import server | from pyinfra.operations import server | ||
| Riga 41: | Riga 200: | ||
commands=["uptime"], | commands=["uptime"], | ||
) | ) | ||
</ | </pre> | ||
Esecuzione: | Esecuzione: | ||
< | <pre> | ||
pyinfra inventory.py deploy.py | pyinfra inventory.py deploy.py | ||
</ | </pre> | ||
== Esempio base 4: installare pacchetti == | == Esempio base 4: installare pacchetti == | ||
< | <pre> | ||
from pyinfra.operations import apt | from pyinfra.operations import apt | ||
| Riga 57: | Riga 216: | ||
update=True, | update=True, | ||
) | ) | ||
</ | </pre> | ||
Questo e un esempio di operation idempotente: se i pacchetti sono gia presenti, pyinfra non rifara cambiamenti inutili. | Questo e un esempio di operation idempotente: se i pacchetti sono gia presenti, pyinfra non rifara cambiamenti inutili. | ||
== Esempio base 5: gestire file == | == Esempio base 5: gestire file == | ||
< | <pre> | ||
from pyinfra.operations import files | from pyinfra.operations import files | ||
| Riga 72: | Riga 231: | ||
mode="644", | mode="644", | ||
) | ) | ||
</ | </pre> | ||
== Esempio base 6: gestire servizi == | == Esempio base 6: gestire servizi == | ||
< | <pre> | ||
from pyinfra.operations import systemd | from pyinfra.operations import systemd | ||
| Riga 84: | Riga 243: | ||
enabled=True, | enabled=True, | ||
) | ) | ||
</ | </pre> | ||
== Esempio intermedio 1: dati host == | == Esempio intermedio 1: dati host == | ||
Inventory: | Inventory: | ||
< | <pre> | ||
app_servers = [ | app_servers = [ | ||
("app-01.example.net", {"install_nginx": True}), | ("app-01.example.net", {"install_nginx": True}), | ||
("app-02.example.net", {"install_nginx": False}), | ("app-02.example.net", {"install_nginx": False}), | ||
] | ] | ||
</ | </pre> | ||
Deploy: | Deploy: | ||
< | <pre> | ||
from pyinfra import host | from pyinfra import host | ||
from pyinfra.operations import apt | from pyinfra.operations import apt | ||
| Riga 106: | Riga 265: | ||
update=True, | update=True, | ||
) | ) | ||
</ | </pre> | ||
Qui usi Python per rendere il deploy diverso in base ai dati del target. | Qui usi Python per rendere il deploy diverso in base ai dati del target. | ||
== Esempio intermedio 2: gruppi == | == Esempio intermedio 2: gruppi == | ||
< | <pre> | ||
from pyinfra import host, local | from pyinfra import host, local | ||
from pyinfra.operations import server | from pyinfra.operations import server | ||
| Riga 125: | Riga 284: | ||
commands=["hostname"], | commands=["hostname"], | ||
) | ) | ||
</ | </pre> | ||
Questo pattern e utile quando vuoi dividere la logica in file piu piccoli. | Questo pattern e utile quando vuoi dividere la logica in file piu piccoli. | ||
| Riga 132: | Riga 291: | ||
Questo e il pattern usato nel progetto Proxmox Web UI: pyinfra gira su <code>@local</code> ma esegue un helper esterno che contiene la logica vera. | Questo e il pattern usato nel progetto Proxmox Web UI: pyinfra gira su <code>@local</code> ma esegue un helper esterno che contiene la logica vera. | ||
< | <pre> | ||
#!/usr/bin/env python3 | #!/usr/bin/env python3 | ||
import os | import os | ||
| Riga 149: | Riga 308: | ||
commands=[cmd], | commands=[cmd], | ||
) | ) | ||
</ | </pre> | ||
Idea pratica: | Idea pratica: | ||
| Riga 180: | Riga 339: | ||
== Esempio dry run == | == Esempio dry run == | ||
< | <pre> | ||
pyinfra inventory.py deploy.py --dry | pyinfra inventory.py deploy.py --dry | ||
</ | </pre> | ||
Utile prima di un deploy reale. | Utile prima di un deploy reale. | ||
Versione attuale delle 19:21, 29 mar 2026
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