IaC vs. ClickOps
Strucare botoni (ClickOps) è la pratica comune di
utilizzare interfacce grafiche per compiere un certo lavoro. Un esempio: creare un LXC in Proxmox significa partire dal pulsante Create CT nella schermata principale, assegnarli le risorse necessarie, installare pacchetti e configurare SSH, Docker e firewall a mano, per poi ripetere per ogni container.
Ecco, sto cercando di ridurre al minimo questa pratica, sostituendola con Infrastructure as Code dove possibile. Per ora sono un semplice esploratore inesperto, ma mi sto divertendo un mondo!
TLDR
Sto sperimentando diversi strumenti di orchestrazione per rendere la mia infrastruttura automatizzata, riproducibile e documentata. In questo post spiego come creare template LXC riutilizzabili in Proxmox.
La mia infrastruttura
La creazione e la manutenzione delle risorse sul mio server avvengono principalmente tramite Opentofu per la genesi di LXC e macchine virtuali, Ansible per la gestione degli aggiornamenti di sistema e dei backup dei servizi.
Ogni servizio ha il proprio file docker-compose in una repository git che mantengo nella mia istanza di Forgejo.
Ogni giorno Renovate controlla la disponibilità di aggiornamenti per i servizi. Se ne trova, crea una pull request con le modifiche necessarie.
Una volta controllate le release notes dei servizi, posso unire le PR e Komodo si occupa di aggiornare i servizi interessati.
Rimane però un passaggio ancora manuale: dopo aver creato un container con Opentofu devo collegarmi alla sua shell tramite la UI di Proxmox. Il motivo? Il template di Alpine che utilizzo non include python, necessario per eseguire Ansible e completare il setup iniziale.
Poco male, basta effettuare il setup in un nuovo container, convertirlo in template e il gioco è fatto! Ci sono solo un paio di accorgimenti da tenere a mente per rendere il processo più fluido.
Un concetto simile, forse ancora più interessante, lo sto applicando sul mio portatile. Utilizzo un'immagine personalizzata costruita con BlueBuild a partire da Bluefin. La repository si trova qui.
Creare un Template in Proxmox
Configurare l'LXC
Inizio creando un LXC Alpine che servirà come template di base per tutti i container futuri:
-
Mi collego alla shell del container appena creato e lo configuro con ciò che mi interessa:
# System update and base software installation apk update && apk upgrade apk add python3 openssh doas bash bash-completion shadow curl vim nano \ docker docker-compose openrc # Tailscale setup curl -fsSL https://tailscale.com/install.sh | sh # Timezone (adapt to your own) setup-timezone -z Europe/Rome # Enable SSH and Docker rc-update add sshd rc-update add docker boot rc-service sshd start rc-service docker start # Configure doas for wheel group mkdir -p /etc/doas.d echo "permit persist :wheel" > /etc/doas.d/20-wheel.conf chmod 644 /etc/doas.d/20-wheel.conf # Create non-root user (docker and wheel group) adduser -D -u 1000 -G docker -s /bin/bash username echo "username:temporary" | chpasswd addgroup username wheel # Setup SSH mkdir -p /home/username/.ssh chmod 700 /home/username/.ssh touch /home/username/.ssh/authorized_keys chmod 600 /home/username/.ssh/authorized_keys chown -R username:usergroup /home/username/.ssh # Hardening SSH sed -i 's/^#*PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config sed -i 's/^#*PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config rc-service sshd restart -
Aggiungo le chiavi SSH necessarie all'utente appena creato;
-
Spengo il container e rimuovo la scheda di rete con il comando
pct set <CID> --delete net0.
Generare il template
Per creare il template basta generare un backup nella cartella dedicata (nel mio caso in /tank/isos/template/cache) con il comando:
vzdump <CID> --mode stop --compress gzip --dumpdir /tank/isos/template/cache/
Rinomino il file risultante con mv new_vz_dump.tar.gz custom_alpine_3.23.tar.gz e il template è pronto all'uso!
A differenza della creazione del template tramite l'interfaccia di Proxmox, questo metodo non distrugge il container originale, che può essere eliminato o riutilizzato in caso di aggiornamenti futuri.
Creazione di nuovi LXC dal template
Ora posso generare un nuovo container tramite Opentofu utilizzando il template personalizzato al posto dell'Alpine generico. Una volta creato spengo il container, in modo da poter aggiungere le due righe di configurazione necessarie affinché Tailscale possa funzionare:
# /etc/pve/lxc/<CID>.conf
lxc.cgroup2.devices.allow: c 10:200 rwm
lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file
Non resta che creare un nuovo playbook Ansible che esegua gli aggiornamenti di sistema, acceda a Tailscale e modifichi la password dell'utente con una nuova (quella di root viene generata da Opentofu).
Assicurati di avere
passlibinstallato:sudo pacman -S python-passlib
lxc-setup.yaml
---
- name: LXC initial configuration
hosts: new_lxcs
remote_user: username
become: true
become_method: doas
gather_facts: true
vars_file: [vault.yaml]
vars:
tailscale_auth_key: "{{ vault_tailscale_auth_key }}"
tasks:
- name: Update all packages
apk:
update_cache: true
upgrade: true
- name: Ensure Docker is running
service:
name: docker
enabled: true
state: started
- name: Check if tailscale already authenticated
command: tailscale status
register: tailscale_status
failed_when: false
changed_when: false
- name: Authenticate and start Tailscale
command: tailscale up --operator=username --auth-key={{ tailscale_auth_key }}
when: "'Logged out' in tailscale_status.stdout or tailscale_status.rc != 0"
register: tailscale_up
- name: Display Tailscale status
debug:
msg: Tailscale is now connected
when: tailscale_up.changed or ('BackendState=Running' in tailscale_status.stdout)
- name: Set user password
user:
name: username
password: "{{ new_host_sudo_pass | password_hash('sha512') }}"
hosts.ini
[new_lxcs:vars]
ansible_user=username
ansible_become=yes
ansible_become_method=doas
[new_lxcs]
new-host ansible_host=new-host-ip ansible_become_pass='temporary'
vault.yaml
new_host_sudo_pass: your-password
vault_tailscale_auth_key: tskey-auth-xx..x-yy..y
Esegui il playbook con:
ansible-playbook -i hosts.ini lxc-setup.yaml --ask-vault-pass
A questo punto il container dovrebbe essere pronto all'uso!
Ultimi passaggi
Aggiornare il file hosts.ini:
- spostare il nuovo host fuori dal gruppo
[new_lxcs]; - sostituire
ansible_become_pass='temporary'conansible_become_pass={{new_host_sudo_pass}}.
Non è la soluzione più elegante, ma sono le due di notte e il mio cervello mi sta abbandonando.
Per concludere
Non ho inventato nulla di rivoluzionario. Tutto quello che ho fatto è stato applicare principi di IaC consolidati al mio piccolo homelab, che ora è ben definito in codice versionato, documentato e riproducibile.
Ho ancora molta strada da fare, ma vedere tutto prendere forma da file di configurazione invece che da sessioni struca botoni è incredibilmente soddisfacente (·ω·)