Ansible Teil 2 - Facts und Jinja2 Filter

Im ersten Teil der kleinen Ansible Serie ging es um die Installation von RPM Paketen für die Oracle Software. Der nächste Schritt wäre nun der Aufbau einer Datenbank mit dem Database Configuration Assistant (DBCA) der Firma Oracle. Es macht aber Sinn, vorab zum besseren Verständnis die Punkte Ansible Facts und Jinja2 Filter zu behandeln.

In unserem Demo-Beispiel soll es später die Möglichkeit geben, die Hauptspeicher-Grösse der Datenbank-Instanz in Megabyte als Variable zu definieren. Ist kein Wert explizit gesetzt, soll als Default-Wert 50% des verfügbaren Hauptspeichers genutzt werden. Für die praktische Umsetzung gibt es in Ansible Standard-Facts die automatisch vom Zielsystem ermittelt werden. In unserem Fall das Fact ansible_memtotal_mb das wir als Grundlage für eigene Berechnungen nutzen könnten:

- name: calculate totalMemory parameter for dbca
  debug:
    msg: "{{ oracle_memory | default((ansible_memtotal_mb / 2) | round | int) }}"

Zusätzlich möchten wir den DBCA später nur starten, wenn auf dem Zielsystem noch keine entsprechende Datenbank vorhanden ist. Ein Indikator für diese Erkennung ist der Inhalt der Datei /etc/oratab. In diesem Fall ist die Verarbeitung etwas aufwendiger, zunächst könnten wir den Inhalt von /etc/oratab in Ansible verfügbar machen:

- name: Slurp /etc/oratab
  ansible.builtin.slurp:
    src: /etc/oratab
  register: slurped_etc_oratab

Aus diesem Inhalt können wir dann mit Jinja2 Filtern eine Liste der ORACLE_SIDs herausfiltern:

- name: Extract list of Oracle SIDs from slurped /etc/oratab
  ansible.builtin.set_fact:
    oracle_sids: "{{ slurped_etc_oratab.content | 
                     ansible.builtin.b64decode | 
                     ansible.builtin.regex_findall('^((?!#).+):.*:.*$', multiline=True) }}"

Die so ermittelten ORACLE_SIDs könnten wir für Debug-Zwecke in einer Schleife ausgeben lassen:

- name: Iterate over list of Oracle SIDs and display them
  ansible.builtin.debug:
    msg: "{{ item }}"
  loop: "{{ oracle_sids }}"

Um zu verhindern, dass der DBCA gestartet wird, wenn die gewünschte ORACLE_SID schon in der /etc/oratab vorhanden ist, könnten wir den späteren Task mit folgender when-Bedingung versehen:

- name: Only execute if given SID is not in extracted Oracle SID list
  ansible.builtin.debug:
    msg: "Database {{ oracle_sid }} is not in list..."
  when: oracle_sid not in oracle_sids

Ansible Teil 1 - YUM und RPM-Pakete

In einem der letzten Blog-Posts hatte ich gezeigt, wie mit wenigen Schritten ein Yum-Repository für RPM-Pakete auf einem NAS angelegt werden kann. Nun soll es darum gehen, diese Pakete auf einem System mit Rocky Linux 9 via Ansible automatisiert zu installieren.

Einer der Grundbaustein von Ansible sind die Tasks in denen definiert wird, was auf einem System automatisiert werden soll. Glück für uns, für die Konfiguration von Yum-Repositories und die Installation von RPM-Paketen gibt es schon fertige Standard-Module.

Hier ein Beispiel für die YUM-Repository Konfiguration und die Installation der Oracle 23ai Free Edition:

- name: "Handle local YUM Repository"
  ansible.builtin.yum_repository:
    name: "local-yum-repo"
    baseurl: "{{ yum_repo_url }}"
    file: "local_yum_repo"
    description: "Local YUM Repo on NAS"
    enabled: yes
    gpgcheck: no
    proxy: "_none_"
    state: "present"

- name: Install Oracle RPM Packages for 23ai
  ansible.builtin.dnf:
    name: 
      - oracle-database-preinstall-23ai
      - oracle-database-free-23ai
    state: installed

In weiteren Blog-Posts werde ich zeigen, wie sich im Anschluss eine Oracle Datenbank erstellen lässt und welche weiteren Bausteine für eine Ansible Automatisierung notwendig sind.

Lokales YUM-Repository für eigene RPM-Pakete

Speziell für eigene RPM-Pakete bietet sich ein YUM-Repository im LAN an. Damit lässt sich die Installation von RPM-Paketen automatisieren und man spart sich - gerade bei großen Paketen - den Download aus dem Netz.

Für Test- und Entwicklungszwecke hat sich bei mir ein NAS der Firma Synology bewährt. Als Basis dient ein NFS-Share und der "Web Station"-Service. So lassen sich die RPM-Pakete bequem von meiner Workstation auf das NAS schieben. Das YUM-Repository lässt sich im zweiten Schritt mit dem Befehl createrepo anlegen oder aktualisieren.

Zunächst die gewünschen RPM-Pakete auf das NFS-Share verschieben / kopieren:

mv oracle-database-preinstall-23ai-1.0-2.el9.x86_64.rpm /media/web/
mv oracle-database-free-23ai-1.0-1.el9.x86_64.rpm /media/web/

Falls noch kein YUM-Repository existiert:

createrepo /media/web/

Nach diesem Aufruf gibt es ein Unterverzeichnis repodata mit den entsprechenden Metadaten des Repository.

Um die Metadaten nach dem Hinzufügen von Paketen zu aktualisieren reicht ein:

createrepo --update /media/web/

Gitea - Git Versionsverwaltung in einfach

Die Versionsverwaltung Git hat sich über die letzten Jahre als Standard in der Softwareentwicklung etabliert. Die meisten Kunden setzten hierbei auf Gitlab oder Bitbucket von der Firma Atlassian. Gitea bietet eine schlanke Alternative gerade für das Selbsthosting von kleinen Repos.

Nachdem Amazon AWS den Dienst CodeCommit im letzten Jahr eingestellt hat, bot sich der Wechsel zu einem selbstgehosteten Gitea unter Proxmox an. Tipp, die Übernahme von Repos ist direkt über die Web-GUI von Gitea möglich:

In den nachfolgenden Dialogen besteht nun die Möglichkeit für verschiedene Repos eine einfache Migration durchzuführen. Hier als Beispiel der Dialog für CodeCommit:

Über diesen Weg lässt sich zumindest der Source Code inklusive der Commit-History bequem übernehmen. Deutlich mehr Aufwand wäre sicherlich notwendig, wenn angebundene DevOps-Prozesse z.B. für Build-Pipelines in eine neue Umgebung übernommen werden sollen.

Damit bietet Git die passenden Konzepte für die Herausforderungen der nächsten Zeit, wenn es darum geht, von Cloud A zu Cloud B, oder zurück nach On-Premise zu wechseln.

K3s unter Rocky Linux 8 mit k3sup

Letztes Jahr hatte ich im Blog-Eintrag CodeReady Containers for OKD unter Windows 10 ein kleines lokales Setup vorgestellt. Für Entwicklung und Test von Kubernetes Operatoren benötigt man aber auch schnell eine Umgebung aus mehreren Nodes. Mit dem Tool k3sup und der Kubernetes Distribution K3s lässt sich eine derartige Umgebung unter Rocky Linux 8 mit wenigen Befehlen aufsetzen.

Der Kubernetes Cluster besteht hierbei aus drei Rocky Linux 8 VMs (je 2 CPUs / 4 GB RAM / 20 GB Disk). Kubectl und k3sup nutze ich direkt auf meiner Entwickler-Workstation unter WSL2.

Im ersten Schritt wird k3sup lokal installiert:

curl -sLSo get_k3sup.sh https://get.k3sup.dev
bash get_k3sup.sh
sudo install k3sup /usr/local/bin/
which k3sup

Entgegen der k3s-Installationsanleitung habe ich auf den VMs die Firewall nicht deaktiviert. Stattdessen habe ich folgende Firewall-Rules eingerichtet.

Auf dem Server-Node (rock8-t05):

sudo firewall-cmd --permanent --add-port=6443/tcp
sudo firewall-cmd --permanent --add-port=10250/tcp
sudo firewall-cmd --permanent --add-port=8472/udp
sudo firewall-cmd --permanent --zone=trusted --add-source=10.42.0.0/16 # pods
sudo firewall-cmd --permanent --zone=trusted --add-source=10.43.0.0/16 # services
sudo firewall-cmd --reload

Auf den Agent-Nodes (rock8-t06 und rock8-t07):

sudo firewall-cmd --permanent --add-port=10250/tcp
sudo firewall-cmd --permanent --add-port=8472/udp
sudo firewall-cmd --permanent --zone=trusted --add-source=10.42.0.0/16 # pods
sudo firewall-cmd --permanent --zone=trusted --add-source=10.43.0.0/16 # services
sudo firewall-cmd --reload

Nachdem die Firewalls auf den einzelnen Nodes nun vorbereitet sind, können wir via k3sup den Server-Node (rock8-t05) aufbauen:

k3sup install --user ansible --host rock8-t05

Sobald der Server-Node steht, können die beiden Agent-Nodes eingerichtet werden:

k3sup join --user ansible --server-host rock8-t05 --host rock8-t06
k3sup join --user ansible --server-host rock8-t05 --host rock8-t07

Damit sollte der Kubernetes Cluster schon einsatzbereit sein. Dies lässt sich z.B. mit folgenden Befehlen prüfen:

$ export KUBECONFIG=~/kubeconfig
$ kubectl config set-context default
Context "default" modified.
$ kubectl cluster-info
Kubernetes control plane is running at https://rock8-t05:6443
CoreDNS is running at https://rock8-t05:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
Metrics-server is running at https://rock8-t05:6443/api/v1/namespaces/kube-system/services/https:metrics-server:https/proxy

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
$ kubectl get node
NAME        STATUS   ROLES                  AGE     VERSION
rock8-t06   Ready    <none>                 4m22s   v1.23.6+k3s1
rock8-t07   Ready    <none>                 106s    v1.23.6+k3s1
rock8-t05   Ready    control-plane,master   8m39s   v1.23.6+k3s1