Raspberry Pi Administration mit Ansible

Updates lassen sich auf deinen vielen Raspberry Pis sehr einfach un zentral mit einem Ansible Playbook einspielen.

Du brauchst dich dafür nicht mehr per SSH bei jedem deiner Raspberry Pis einzeln anmelden und den Update- und Upgrade-Befehl ausführen.

Mit Ansible startest du das Playbook bequem auf deinem Laptop bzw. PC mit einem Befehl und der Rest wird von Ansible automatisch erledigt.

Updates einspielen und die Server auf dem aktuellsten Stand zu halten ist eine unverzichtbare Aufgabe, die regelmäßig durchgeführt werden muss.

Mit Ansible lässt sich die eigene Raspberry Pi Serverfarm sehr schön orchestrieren.

Wenn man viele Server hat, dann kann diese Aufgabe einige Zeit in Anspruch nehmen.

Mit Ansible lassen sich die Raspberry Pis im eigenen Netzwerk sehr schnell und unkompliziert updaten.

Wie sieht der ganze Prozess mit Ansible aus?

Bisher sieht der Prozess so aus:

ssh benutzername@ip-adresse-rpi1
screen
sudo apt update && sudo apt dist-upgrade && sudo apt-get clean
exit (Strg+D)
exit (Strg+D)

ssh benutzername@ip-adresse-rpi2
screen
sudo apt update && sudo apt dist-upgrade && sudo apt-get clean
exit (Strg+D)
exit (Strg+D)
...

ssh benutzername@ip-adresse-rpin
screen
sudo apt update && sudo apt dist-upgrade && sudo apt-get clean
exit (Strg+D)
exit (Strg+D)

Mit Ansible vereinfacht sich der Update-Prozess erheblich.

Ansible mit YubiKey
Ansible mit YubiKey

Neuer Prozess

ansible-playbook -i inventory.ini update_upgrade.yml -v

Was wird benötigt?

Damit du mit Ansible deine Raspberry Pis aktualisieren und auf dem aktuellsten Stand halten kannst, sind ein paar Dinge vorzubereiten.

SSH-Zugriff auf RPis einrichten

Für die Verwendung von Ansible benötige wir einen funktionierenden SSH-Zugriff auf unsere Raspberry Pis.

In meinem Fall verwende ich dafür einen YubiKey den ich nach diesen Anleitungen konfiguriert habe.

Wenn du keinen YubiKey oder anderen Hardwaretoken hast, kannst du den ganzen Prozess auch mit "normalen" SSH-Schlüsseln umsetzen.

YubiKey vorbereiten

Bevor der YubiKey zur Authentifizierung verwendet werden kann, sind die GPG-Schlüssel zu erstellen und auf den Hardwaretoken zu übertragen.

Für die einzelnen Schritte findest du Anleitungen in meinem Blog.

YubiKey 06 – OpenPGP Schlüsselpaare erstellen – Master Key und Sub Keys

YubiKey 07 – Revoke-Datei erstellen und Schlüssel exportieren

YubiKey 08 – Hilfreiche GPG-Befehle zur Verwaltung von Schlüsselpaaren

YubiKey 09 – OpenPGP-Schlüssel auf den YubiKey exportieren

Raspberry Pi anpassen

Die Raspberry Pis sind ebenfalls vorzubereiten, dass eine Anmeldung per SSH und dem YubiKey möglich ist.

Die Raspberry Pis sind nach dieser Anleitung konfiguriert:

YubiKey 10 – Öffentlichen GPG-Schlüssel auf Linux-Server übertragen und für passwortlose Anmeldung nutzen

Client für YubiKey konfigurieren

Zum Abschluss ist dein Client noch für die Verwendung mit dem YubiKey zu konfigurieren.

Abhängig von deinem Betriebssystem findest du in meinem Blog die entsprechende Anleitung für die Einrichtung.

SSH-Verbindung gesichert! – YubiKey am Mac

Windows

Zur ➡ YubiKey Themenseite.

Ansible auf Client installieren

Ansible muss dann noch auf deinem Client installiert werden. Bei Linux und MacOS ist das sehr einfach

Für MaxOS kann Ansible mit Hilfe von Homebrew installiert werden:

brew install ansible

Bei Linux kommt Ansible mit Hilfe die Repositories auf den Client

sudo apt-get install ansible 

Damit ist die Konfiguration des Clients und des bzw. der Server abgeschlossen und das Ansible Playbook kann erstellt werden.


Gib mir gerne einen Kaffee ☕ aus!

Wenn dir meine Beiträge gefallen und geholfen haben, dann kannst du mir gerne einen Kaffee ☕ ausgeben.

PayPal Logo


liberapay.com/strobelstefan.de


Kaffee via Bitcoin

bc1qfuz93hw2fhdvfuxf6mlxlk8zdadvnktppkzqzj


ansible.cfg

[defaults]
inventory = {{ lookup('env','PWD') }}/inventory.ini
 
[ssh_connection]
# Control the mechanism for transferring files (new)
# If set, this will override the scp_if_ssh option
#   * sftp  = use sftp to transfer files
#   * scp   = use scp to transfer files
#   * piped = use 'dd' over SSH to transfer files
#   * smart = try sftp, scp, and piped, in that order [default]
transfer_method = smart

# ansible.cfg
# For more details and options please refer to
# https://github.com/ansible/ansible/blob/devel/examples/ansible.cfg

inventory.ini

In den inventory.ini werden alle Server aufgelistet.
Für eine bessere Übersicht können die Server in unterschiedliche Kategorien zusammengefasst werden.

Diese Funktion ist bei großen Infrastrukturen und umfangreichen Playbooks hilfreich. Bei diesem kleinen Beispiel aber nicht wirklich wichtig.

Die Gruppierung dient dazu aufzuzeigen, wie eine inventory.ini aufgebaut werden kann.

In der inventory.ini verwende ich host names und keine IP-Adressen.

Auf meinem Laptop habe ich die /etc/hosts Datei gepflegt und die hosts names werden automatisch den richtigen IP-Adressen zugeordnet.

Die Verwendung von IP-Adressen ist einfacher, wenn keine host names aktiv gepflegt werden.

Ich habe auch eine kleine Besonderheit bei mir, der Server share ist nur über einen Jump Host per SSH erreichbar.

Der Jump Host ermöglicht es von einem Netzwerksegment in ein anderes zu gelangen, ohne das der Client dem anderen Segment angehört.

Die Konfiguration dafür ist in der ~/.ssh/config definiert.
Wie das Ganz eingerichtet wird beschreibe ich in diesem Beitrag:

SSH-Verbindung und Jump Host

# ini file is in use to update & upgrade the Raspberry Pi servers.
[raspberry_server]
radiopi-wlan ansible_host=radiopi-wlan
pihole-lan ansible_host=pihole
nextcloud2 ansible_host=nextcloud
nextcloud-share ansible_host=share

[radio]
#radiopi-lan
radiopi-wlan

[pihole]
pihole-lan

[nextcloud]
nextcloud2 ansible_host=nextcloud

[share]
nextcloud-share ansible_host=share

[raspberry_server:vars]
ansible_user=benutzer
ansible_become=yes
ansible_become_method=sudo
ansible_python_interpreter='/usr/bin/env python3'

###############
## General Playbook Variables
###############

# You can define if a backup of files should be created automatically when
# a new file overwrites it
# 'yes' or 'no'
  - backup_of_scripts='no'

Ansible Playbook update_upgrade.yml

In dieser Datei wird festgelegt, welche Schritte Ansible durchführen soll.

Die Namen ping, update_upgrade und clean_server sind dabei die Namen der Ordner im Hauptordner roles

- ansible.cfg

- inventory.ini

- update_upgrade.yml

- roles/

   - ping/
       - defaults/
       - files/
       - handlers/
       - meta/
       - tasks/
       - templates/
       - tests/
       - vars/

   - update_upgrade/
       - defaults/
       - files/
       - handlers/
       - meta/
       - tasks/
       - templates/
       - tests/
       - vars/

   - clean_server/
       - defaults/
       - files/
       - handlers/
       - meta/
       - tasks/
       - templates/
       - tests/
       - vars/

Die Datei update_upgrade.yml ist das Ansible Playbook, dass alle Schritte enthält, die von Ansible für die definierten Server in der inventory.ini durchlaufen werden sollen.

#
# Playbook - Update And Upgrade
#
# -------------------------------------------
#
# Description:
# Update and upgrade all hosts in group raspberry_server
#
# -------------------------------------------
# Dry Run:
# ansible-playbook -i inventory.ini update_upgrade.yml --check
#
# Run:
# ansible-playbook -i inventory.ini update_upgrade.yml -v
#
---
- hosts: raspberry_server
  gather_facts: false

  roles:
    - ping
    - update_upgrade
    - clean_server
    #- gather_facts


  tasks:

      # - name: Ping all server
      #   include: roles/ping/tasks/ping.yml

Gib mir gerne einen Kaffee ☕ aus!

Wenn dir meine Beiträge gefallen und geholfen haben, dann kannst du mir gerne einen Kaffee ☕ ausgeben.

PayPal Logo


liberapay.com/strobelstefan.de


Kaffee via Bitcoin

bc1qfuz93hw2fhdvfuxf6mlxlk8zdadvnktppkzqzj


Ordnerstruktur für die Rollen

Für jede Rollen wird einen neuer Ordner mit einem aussagekräftigen Namen im Hauptordner roles angelegt.

In diesem Beispiel heißen die Ordner:

  • ping
  • update_upgrade
  • clean_server

Diese Ordner sind damit die Hauptordner für die neuen Rolle und enthalten immer die gleichen Unterordner mit der hier aufgezeigten Benennung.

Für das einfache Beispiel, dass wir hier verwenden, ist die Ordnerstruktur eigentlich viel zu umfangreich. Wenn aber mehr mit Ansible gearbeitet wird, dann wirst du an einer solchen Ordnerstruktur nicht vorbeikommen. Also warum nicht gleich damit anfangen?

Ich habe mit an den Best Practices orientiert und den Vorschlag aus der offiziellen Dokumentation entnommen:

https://docs.ansible.com/ansible/2.8/user_guide/playbooks_best_practices.html

roles/

    README.md              # Role Information

    - name-of-role/        # unique role name

       - defaults/         #
        -- main.yml        #  <-- default lower priority variables for this

       - files/            #
        -- bar.txt         #  <-- files for use with the copy resource
        -- foo.sh          #  <-- script files for use with the script resource

       - handlers/         #
        -- main.yml        #  <-- handlers file

       - meta/             #
        -- main.yml        #  <-- role dependencies

       - tasks/            #
        -- main.yml        #  <-- tasks file can include smaller files if warranted

       - templates/        #  <- files for use with the template resource
        -- ntp.conf.j2     #  <-- templates end in .j2

       - tests/            #
        -- test.yml        #  <-- test playbook script 
        -- inventory.ini   #  <-- test inventory file

       - vars/             #
        -- main.yml        #  <-- variables associated with this role

Die Aufgaben (= tasks) liegen im Ordner tasks und dort in der Datei main.yml.

Hier nicht verwirren lassen, egal welche Rolle verwendet wird, die Datei im Ordner tasks ist immer mit main.yml zu benennen.

Die Zuordnung macht Ansible mit dem Namen des übergeordneten Rollen-Ordners. Also in unserem Beispiel

  • ping
  • update_upgrade
  • clean_server

Rolle Ping

Name des Hauptordners ping.

Ansible Ordner Struktur
Ansible Ordner Struktur

Die gesamte Ordnerstruktur für ping sieht wie folgt aus:

  • ping/
    • defaults/
    • files/
    • handlers/
    • meta/
    • tasks/
    • templates/
    • tests/
    • vars/

Die Datei main.yml im Ordner tasks hat den folgenden Inhalt.

#
# Tasks: Ping
#
# -------------------------------------------
#
# Description:
# Send ping to remote servers
#
# -------------------------------------------
#
#
---

# Send a ping and check if the server is available
  - name: "{{ ping1 }}"
    ping:

Die Datei main.yml im Ordner vars hat den folgenden Inhalt.

---
# vars file for ping

ping_test: "test"
ping1: "Name"

Alle anderen Unterordner enthalten für dieses Rolle keine weiteren Dateien.

Rolle update_upgrade

Name des Hauptordners update_upgrade.

Ansible Ordner Struktur
Ansible Ordner Struktur

Die gesamte Ordnerstruktur für update_upgrade sieht wie folgt aus:

  • update_upgrade/
    • defaults/
    • files/
    • handlers/
    • meta/
    • tasks/
    • templates/
    • tests/
    • vars/

Die Datei main.yml im Ordner tasks hat den folgenden Inhalt.

#
# Task: 
# Update And Upgrade
#
# -------------------------------------------
#
# Description:
# Update and upgrade all Debian based OS
#
# -------------------------------------------
#
#
---

# Update process
# https://docs.ansible.com/ansible/latest/collections/ansible/builtin/apt_module.html
- name: Update apt repo and cache on all Debian/Ubuntu boxes
  ansible.builtin.apt:
    update_cache: yes
    cache_valid_time: 3600
    upgrade: full
  register: apt
- debug: msg={{ apt.stdout.split('\n')[:-1] }}

# https://www.jeffgeerling.com/blog/2022/ansible-playbook-upgrade-ubuntudebian-servers-and-reboot-if-needed
- name: Check if a reboot is needed on all servers
  ansible.builtin.stat:
    path: /var/run/reboot-required
    get_md5: no
  register: reboot_required_file

- name: Reboot the server (if required).
  ansible.builtin.reboot:
  when: reboot_required_file.stat.exists == true

Gib mir gerne einen Kaffee ☕ aus!

Wenn dir meine Beiträge gefallen und geholfen haben, dann kannst du mir gerne einen Kaffee ☕ ausgeben.

PayPal Logo


liberapay.com/strobelstefan.de


Kaffee via Bitcoin

bc1qfuz93hw2fhdvfuxf6mlxlk8zdadvnktppkzqzj


Rolle clean-server

Name des Hauptordners clean-server.

Ansible Ordner Struktur
Ansible Ordner Struktur

Die gesamte Ordnerstruktur für clean-server sieht wie folgt aus:

  • clean-server/
    • defaults/
    • files/
    • handlers/
    • meta/
    • tasks/
    • templates/
    • tests/
    • vars/

Die Datei main.yml im Ordner tasks hat den folgenden Inhalt.

#
# Task: Clean Server
#
# -------------------------------------------
#
# Description:
# The playbook removes all useless and unused packages
#
#
#
# -------------------------------------------
#
#
---

# https://docs.ansible.com/ansible/latest/collections/ansible/builtin/apt_module.html
- name: Remove useless packages from the cache
  apt:
    autoclean: yes

- name: Remove dependencies that are no longer required
  apt:
    autoremove: yes

Rolle gather-facts

Es lassen sich auch sämtlichen Informationen der Pis ausgegeben. Dafür gibt es umfangreichen Funktionen in Ansible.

Für dieses Beispiel wird das nicht verwendet, aber es zeigt ganz gut die Möglichkeiten von gather-facts.

Die Datei main.yml im Ordner tasks hat den folgenden Inhalt.

#
#
# Task: 
# Gather Facts
#
# -------------------------------------------
#
# Description:
# Gather Facts
# In Playbook gather facts must be set on "true"
# gather_facts: true
#
# -------------------------------------------
#
#
---

########################### 
# List facts for information/debugging only
###########################
# https://www.middlewareinventory.com/blog/ansible-facts-list-how-to-use-ansible-facts/

# List all available facts for hosts.
#    - debug:
#        msg: "{{vars}}"

# list ansible_default_ipv4 
#    - debug:
#        msg: "{{ansible_default_ipv4}}"

# list only IP address of ansible_default_ipv4
#    - debug:
#        msg: "{{ansible_default_ipv4.address}}"


########################### 
# Load facts into variables
###########################

    - debug: var=hostvars[inventory_hostname]['ansible_distribution']
    - debug: var=hostvars[inventory_hostname]['ansible_distribution_file_variety']
    - debug: var=hostvars[inventory_hostname]['ansible_distribution_version']
    - debug: var=hostvars[inventory_hostname]['ansible_dns']['nameservers']

    - debug: var=hostvars[inventory_hostname]['ansible_default_ipv4']
    - debug: var=hostvars[inventory_hostname]['ansible_default_ipv4']['address']
    - debug: var=hostvars[inventory_hostname]['ansible_default_ipv4']['alias']
    - debug: var=hostvars[inventory_hostname]['ansible_default_ipv4']['broadcast']
    - debug: var=hostvars[inventory_hostname]['ansible_default_ipv4']['gateway']
    - debug: var=hostvars[inventory_hostname]['ansible_default_ipv4']['interface']
    - debug: var=hostvars[inventory_hostname]['ansible_default_ipv4']['macaddress']
    - debug: var=hostvars[inventory_hostname]['ansible_default_ipv4']['mtu']
    - debug: var=hostvars[inventory_hostname]['ansible_default_ipv4']['netmask']
    - debug: var=hostvars[inventory_hostname]['ansible_default_ipv4']['network']
    - debug: var=hostvars[inventory_hostname]['ansible_default_ipv4']['type']


# Links: 
# https://docs.ansible.com/ansible/latest/user_guide/playbooks_vars_facts.html

Weitere Tweaks

McFly

Wenn du einen Mac verwendest, dann kannst du dir das kleine Tool McFly installieren und sehr einfach dein Playbook in der Shell Historie heraussuchen und aufrufen.

Das wiederholte Eingeben des Ansible-Befehls ist damit unnötig. Mit dem Shortcut Strg + R kannst du durch die Historie surfen.

https://github.com/cantino/mcfly

Versioniereung mit Git

Alle Dateien des Ansible Playbooks lassen sich hervorragend über Git versionieren und managen.

Für das eigene Hosting bietet sich hier besonders Gitea an, dass keine großen Anforderungen an die Hardware stellt.

Möchtest du keinen eigene Gitea-Server betreiben, dann kannst du ➡ codeberg.org verwenden.

Photo by Arindam Mahanta on Unsplash

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert