Aufbau Eines Kubernetes ML Cluster Auf SBCs - Teil 2

Wie in Teil 1 erwähnt, möchte ich die Bereitstellung der FCOS ‘control plane’ VMs mit Ansible automatisieren. Genau genommen möchte ich die gesamte Kubernetes Installation automatisieren, aber das später. Also fangen wir mal an.

Ansible und KVM

Es gibt eine Reihe von Ansible Modulen für KVM/libvirt/qemu, es gibt auch komplette Playbooks (etwas veraltet) und Rollen die genau das tun was ich hier erreichen möchte. Wenn es also nur darum geht eine fertige Lösung zu finden, dann einfach die Suchmaschine der Wahl nutzen. Ich persönlich baue die Dinge lieber selber, und nutze die anderen Lösungen als Referenz. Einfach weil ich finde dass in dem Moment wo ich darauf Aufbauen möchte, hilft es sehr es von Grund auf verstanden zu haben. Und darum dreht sich schließlich dieses Projekt.

Voraussetzungen

Als erstes muss unser neues Playbook sicherstellen dass die Grundvoraussetzungen erfüllt sind. Also installieren wir ein paar Pakete:

 1- name: Pakete installieren
 2  hosts: controlserver
 3  become: true # Ich setzte immer become=false in der ansible.cfg
 4  vars:
 5    packages:
 6        - '@virtualization' # es gibt eine Gruppe für kvm, virtsh, etc
 7        - python3-libvirt # für das virt ansible Modul
 8        - butane # brauchen wir später 
 9    services:
10        - libvirtd
11  tasks:
12    - name: Pakete sind installiert
13      dnf:
14        name: "{{ packages }}"
15        state: present
16
17    - name: Dienste laufen
18      systemd:
19        name: "{{ item }}"
20        state: started
21        enabled: true
22      loop: "{{ services }}" # Zur Zeit nur ein Dienst
23

Super simpel, aber irgendwo müssen wir ja anfangen. Ich weiß dass ich eine Rolle erstellen könnte (und vermutlich auch sollte), aber so sehr ich Ansible Rollen mag, finde ich es immer besser erst alles in ein Playbook zu packen. Wenn ich dann feststelle dass ich es wiederverwenden oder veröffentlichen will, mach ich daraus eine Rolle.

Das CoreOS Image installieren

Bevor wir das Fedora CoreOS Image runterladen können, müssen wir wissen welche Version wir denn wollen. Ich werde immer die neuste stabile Version verwenden. Aber welche ist das? Nun ja, wir machen hier DevOps, also gibt es dafür ein JSON: https://builds.coreos.fedoraproject.org/streams/stable.json Das schnappen wir und also und besorgen uns daraus die aktuellste Versionsnummer und die URL. Im Grunde replizieren wir hier den coreos-installer Befehl. Weil wir können.

 1# tasks/getversion.yaml
 2- name: JSON anfragen # Wir könnten hier den Stream (stable/testing) auswählen
 3  uri:
 4    url: https://builds.coreos.fedoraproject.org/streams/stable.json
 5  register: releases
 6
 7- name: Neueste Version auslesen
 8  set_fact:
 9    fcos_latest: "{{ releases.json.architectures.x86_64.artifacts.qemu.release }}"
10    fcos_latest_url: "{{ releases.json.architectures.x86_64.artifacts.qemu.formats['qcow2.xz'].disk.location }}"

Ja, in der JSON steckt auch die Architektur, also könnte ich da eine Variable reinpacken. Aber die Raspberries müssen eh auf “bare metal” laufen, daher werden wir dieses Playbook da nicht verwenden können.

Ein kleiner Hinweis zum URI Modul: JSON lädt es immer runter, auch ohne “return_content: true”. Dann sind die Daten direkt unter Variable.json, und nicht unter Variable.content. Ich hatte massive Probleme auf die Daten unter .content zuzugreifen, unter .json geht das einfach so.

Jetzt müssen wir noch das Image runterladen, entpacken, usw. Wie mit dem coreos-installer in Teil 1 , aber diesmal mit einem Playbook. Ich nutze hier Nick Curry’s exzellente Rolle als Vorlage, aber natürlich mit einigen Anpassungen.

 1# tasks/download.yaml
 2- name: Verzeichnis existiert
 3  file:
 4    state: directory
 5    path: "{{ domain_path }}"
 6    mode: '1777' # Ja, global beschreibbar
 7    setype: virt_image_t
 8
 9- name: Image ist da
10  stat:
11    path: "{{ domain_path }}/{{ image_name }}"
12  register: image_status 
13  
14- name: Image runterladen und entpacken
15  when: not image_status.stat.exists
16  block:
17    - name: qcow2 Image runterladen
18      get_url:
19        url: "{{ fcos_latest_url }}"
20        dest: "{{ domain_path }}"
21
22    - name: Image entpacken
23      command:  # ansible kann xz erzeugen, aber nicht entpacken. WTF?
24        cmd: "unxz {{ image_name }}.xz"
25        chdir: "{{ domain_path }}"

Keine Tricks, außer dass die Variablen vom Haupt-Playbook übergeben werden. Wenn wir nun ein ls auf dem “domain_path” machen, sehen wir das Image in all seiner entpackten Blöße:

1[jan@h2fed ~]$ ls -l /opt/kvm/fcos
2total 1536724
3-rw-r--r--. 1 root root 1573715968 Nov 13 21:16 fedora-coreos-34.20211031.3.0-qemu.x86_64.qcow2

Zündung!

(Markdown hat keinen Sinn für Spannung. Der Linter beschwert sich über das Ausrufezeichen in der Überschrift.)
Wir kommen zum spannenden (und aktuell sehr einfachen) Teil: Wir brauchen ein “ignition file”. Vorerst werden wir die einfache Datei aus Teil 1 verwenden. Ich nutze hier butane auf dem verwalteten Server, und kopiere die Datei als Vorlage rüber. Ich weiß dass ich die YAML mit Ansible zu JSON umwandeln könnte, aber ich will sicherstellen dass die .ign Datei korrekt ist.

 1- name: Ignition Vorlage kopieren
 2  template:
 3    src: templates/simple.yaml
 4    dest: "{{ domain_path }}/simple.yaml"
 5    
 6- name: butane ausführen
 7  command: 
 8    cmd: butane --pretty --strict simple.yaml -o simple.ign
 9    chdir: "{{ domain_path }}"
10  become: false  # Kein Grund als root zu laufen

Das war’s auch schon für diesen Teil. Als nächstes kommen wir zur fummeligen Angelegenheit, die VMs mit qemu/libvirt/kvm auszuführen, und dafür brauche ich sehr viel Kaffee. Alle hier erzeugten Dateien finden sich in meinem GitHub repository falls jemand die tatsächlich verwenden möchte.


Aufbau Eines Kubernetes ML Cluster Auf SBCs - Teil 1