Files
ansible/readme.md
2026-07-12 08:44:40 +02:00

6.7 KiB
Raw Blame History

Ansible + PatchMon — zarządzanie flotą serwerów

Inwentarz jest pobierany dynamicznie z PatchMon (patchmon_inventory.yml, plugin patchmon.dynamic_inventory). Grupy hostów pochodzą wprost z grup przypisanych w PatchMon — filtrowanie na potrzeby playbooków (--limit) odbywa się przez te grupy, nie przez ręczne listy hostów.

Pierwsze uruchomienie (świeży klon repozytorium)

Sekrety nie są trzymane w gicie — trzeba je uzupełnić lokalnie:

cp patchmon_inventory.yml.example patchmon_inventory.yml
# uzupełnij api_url / api_key / api_secret w patchmon_inventory.yml

mkdir -p ssh_keys && chmod 700 ssh_keys
cp /ścieżka/do/twojego/klucza ssh_keys/id_rsa && chmod 600 ssh_keys/id_rsa

Grupy PatchMon używane w tym projekcie

Grupa Znaczenie
root Hosty logowane bezpośrednio na konto root.
mgmt Hosty logowane na konto mgmt (z sudo).
Update-now Wszystkie hosty dopuszczone do automatycznej aktualizacji (root mgmt). Domyślny cel dla crona/autopilota.
Type-HOST Fizyczne hosty Proxmox — traktuj ostrożniej (restart hosta restartuje też jego VM/CT).
Type-VM / Type-CT Maszyny wirtualne / kontenery LXC.
Site-nni / Site-on Lokalizacje.

Konto logowania (ansible_user) nie jest już podawane ręcznie przez -e — jest ustawiane automatycznie przez group_vars/root.yml i group_vars/mgmt.yml na podstawie grupy PatchMon danego hosta. Dzięki temu jedno uruchomienie playbooka na grupie mieszanej (np. Update-now) obsłuży poprawnie oba typy kont.

Struktura projektu

ssh_keys/
  id_rsa                          # klucz prywatny SSH (poza gitem, patrz sekcja "Sekrety")
group_vars/
  all.yml                        # klucz SSH, become, próg dysku, lista kluczy admina
  root.yml / mgmt.yml            # ansible_user per grupa PatchMon
playbooks/
  playbook_upgrade.yml           # apt update + dist-upgrade
  playbook_cleanup.yml           # autoremove/autoclean, czyszczenie journala, alarm dysku
  playbook_docker_cleanup.yml    # czyszczenie nieużywanych obrazów Docker (tylko hosty z Dockerem)
  playbook_fleet_report.yml      # raport: OS, uptime, dysk, liczba aktualizacji, reboot-required
  playbook_security_baseline.yml # unattended-upgrades + wyłączenie logowania hasłem SSH
  playbook_ssh.yml               # blokada pustych haseł SSH
  playbook_ssh_keys.yml          # zarządzanie kluczami publicznymi na koncie logowania
  playbook_autopilot.yml         # orkiestracja pod cron: upgrade -> cleanup -> fleet_report
templates/
  50unattended-upgrades.j2
  fleet_report.j2
cron/
  run-autopilot.sh               # wrapper do crona (flock + log)
  crontab.example
  ansible-autopilot.logrotate
reports/                          # tu trafiają wygenerowane raporty floty

Użycie

# Aktualizacja pakietów na całej flocie dopuszczonej do auto-update
ansible-playbook -i patchmon_inventory.yml playbooks/playbook_upgrade.yml --limit "Update-now"

# Sprzątanie/kondycja systemu (autoremove, journal, alarm zajętości dysku)
ansible-playbook -i patchmon_inventory.yml playbooks/playbook_cleanup.yml --limit "Update-now"

# Czyszczenie nieużywanych obrazów Docker (tylko hosty, na których jest Docker)
ansible-playbook -i patchmon_inventory.yml playbooks/playbook_docker_cleanup.yml --limit "Update-now"

# Raport stanu floty -> plik w reports/
# WAŻNE: dopisz ",localhost" do --limit, inaczej faza renderowania raportu
# zostanie pominięta ("skipping: no hosts matched").
ansible-playbook -i patchmon_inventory.yml playbooks/playbook_fleet_report.yml --limit "Update-now,localhost"

# Baza bezpieczeństwa: unattended-upgrades + wyłączenie logowania hasłem SSH
# (dotyka sshd_config — uruchamiaj świadomie, ręcznie, nie z crona)
ansible-playbook -i patchmon_inventory.yml playbooks/playbook_security_baseline.yml --limit "Update-now"

# Blokada logowania na puste hasła
ansible-playbook -i patchmon_inventory.yml playbooks/playbook_ssh.yml --limit "Update-now"

# Zarządzanie kluczami SSH konta logowania (uzupełnij admin_ssh_keys w group_vars/all.yml)
ansible-playbook -i patchmon_inventory.yml playbooks/playbook_ssh_keys.yml --limit "Update-now"

# Pełny przebieg autopilot ręcznie (to samo co robi cron)
ansible-playbook -i patchmon_inventory.yml playbooks/playbook_autopilot.yml --limit "Update-now,localhost"

Można też celować w węższe grupy, np. --limit "Site-on", --limit "Type-CT", albo pojedynczy host: --limit "pve".

Reboot — tylko raportowanie, nigdy automatycznie

playbook_fleet_report.yml sprawdza /var/run/reboot-required na każdym hoście i wypisuje listę hostów wymagających restartu w raporcie (reports/fleet_report_*.txt). Żaden playbook w tym projekcie nie restartuje hostów automatycznie — decyzję i moment restartu (szczególnie dla Type-HOST, czyli fizycznego Proxmoksa) podejmuje operator ręcznie, po przejrzeniu raportu.

Bezobsługowe uruchamianie (cron)

cron/run-autopilot.sh to wrapper na playbook_autopilot.yml:

  • blokuje się przez flock, żeby nie nakładały się dwa równoległe przebiegi,
  • loguje przebieg (start/koniec/kod wyjścia) do /var/log/ansible-autopilot.log,
  • domyślnie celuje w grupę Update-now (nadpisywalne zmienną TARGET_GROUP).

Instalacja:

chmod +x cron/run-autopilot.sh
crontab -e   # wklej zawartość cron/crontab.example
sudo cp cron/ansible-autopilot.logrotate /etc/logrotate.d/ansible-autopilot

Każdy przebieg playbook_fleet_report.yml (w tym z crona) dokłada nowy plik do reports/ — warto od czasu do czasu posprzątać stare raporty, np. find reports/ -name 'fleet_report_*.txt' -mtime +30 -delete.

Sekrety

  • Klucz prywatny SSH leży w ssh_keys/id_rsa (katalog chmod 700, plik chmod 600). Katalog ssh_keys/ jest w .gitignore — klucz nigdy nie trafia do repozytorium. Ścieżka do niego jest ustawiona centralnie w group_vars/all.yml (ansible_ssh_private_key_file: "{{ playbook_dir }}/../ssh_keys/id_rsa"), więc wszystkie playbooki i cron/run-autopilot.sh korzystają z niej automatycznie — nie trzeba nic dodatkowo konfigurować.
  • api_key/api_secret/api_url PatchMon leżą w patchmon_inventory.yml, który jest w .gitignore — nigdy nie trafia do repozytorium. Wersja do repo to patchmon_inventory.yml.example (same placeholdery, bez sekretów). Przy zakładaniu projektu od zera: cp patchmon_inventory.yml.example patchmon_inventory.yml i uzupełnij api_url/api_key/api_secret. (Plugin patchmon.dynamic_inventory wczytuje ten plik bezpośrednio, bez templatowania Jinja2/Vault, dlatego to rozdzielenie na plik realny + .example, a nie np. {{ lookup('env', ...) }}, jest tu jedynym niezawodnym sposobem.)