Zum Inhalt

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

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ötigen 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.

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:

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.

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 mithilfe von Homebrew installiert werden:

brew install ansible

Bei Linux kommt Ansible mithilfe der 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.

Donation via PayPalDonation via LiberaPay

Donation via Bitcoin
Bitcoin Address: 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:

# 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.

Donation via PayPalDonation via LiberaPay

Donation via Bitcoin
Bitcoin Address: bc1qfuz93hw2fhdvfuxf6mlxlk8zdadvnktppkzqzj

Ordnerstruktur für die Rollen

Für jede Rolle wird ein 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 Rollen 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:

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

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 diese Rolle keine weiteren Dateien.

Rolle update_upgrade

Name des Hauptordners update_upgrade.

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.

Donation via PayPalDonation via LiberaPay

Donation via Bitcoin
Bitcoin Address: bc1qfuz93hw2fhdvfuxf6mlxlk8zdadvnktppkzqzj

Rolle clean-server

Name des Hauptordners clean-server.

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.

Versionsverwaltung 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 eigenen Gitea-Server, betreiben, dann kannst du

Gib mir gerne einen Kaffee ☕ aus ❗️

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

Donation via PayPalDonation via LiberaPay

Donation via Bitcoin
Bitcoin Address: bc1qfuz93hw2fhdvfuxf6mlxlk8zdadvnktppkzqzj

Source

Photo by Arindam Mahanta on Unsplash