
🌐 Auch auf: English · Français · Español
Deep Dive aus der Reihe rund um die Ansible-Tour. Niveau: fortgeschrittene Linux-Administration. Die Tour erklärt die Rollen; die Deep Dives erklären die Technik darunter – wie sie funktioniert, warum sie hier ist, was genau dieses Setup damit macht, wie man sie im Alltag betreibt und was es sonst noch gegeben hätte.
Beide Anwendungen stehen hier auf MariaDB. Der Blog betreibt sie als Pod neben WordPress (Teil 3), die beiden Nextcloud-Server direkt auf dem Host (Teil 4). Dieser Beitrag erklärt, was InnoDB mit Speicher und Platte macht, auf welche Handvoll Einstellungen es bei diesen Lasten ankommt, was die Messungen sagen – und wie eine einzige Zeile in einer systemd-Unit dafür sorgte, dass SELinux die Datenbank fünf Wochen lang nicht schützte.
🧭 Wie MariaDB und InnoDB funktionieren
Der Buffer Pool: die Datenbank lebt im RAM
InnoDB, die Storage-Engine hinter jeder Tabelle hier, liest und schreibt Daten in Seiten zu 16 KB. Gelesene Seiten bleiben im Buffer Pool, einem großen Speicherblock; der nächste Zugriff auf dieselbe Zeile kostet keinen Plattenzugriff. Änderungen passieren zuerst im Buffer Pool und werden später in die Datendateien geschrieben. Die wichtigste Einzelzahl eines MariaDB-Servers ist deshalb der Anteil der Seitenanfragen, die der Buffer Pool beantworten kann – die Trefferquote.
Redo-Log und Doublewrite: warum ein Absturz nichts verliert
Bevor eine geänderte Seite in ihre Datendatei geschrieben wird, landet die Änderung im Redo-Log, einem sequenziellen Journal. Nach einem Absturz spielt InnoDB es nach, und jede bestätigte Transaktion ist wieder da. Der Doublewrite-Buffer schützt vor einer Seite, die beim Stromausfall nur halb geschrieben war: Jede Seite geht erst in einen separaten Bereich, dann an ihren echten Platz, sodass eine zerrissene Seite aus der Kopie repariert werden kann.
Eine Einstellung entscheidet, wie oft das Redo-Log auf die Platte gezwungen wird. Mit innodb_flush_log_at_trx_commit = 1 (Standard) wartet jeder Commit auf ein fsync. Mit 2 wird das Log beim Commit geschrieben, aber nur einmal pro Sekunde synchronisiert – viel schneller bei vielen kleinen Schreibvorgängen, mit dem Risiko, bei einem harten Absturz der ganzen Maschine (nicht nur von MariaDB) bis zu einer Sekunde an Commits zu verlieren.
Temporäre Tabellen: Speicher oder Platte
Abfragen mit GROUP BY, DISTINCT, UNION oder manche Sortierungen brauchen eine interne temporäre Tabelle. Sie entsteht im Speicher bis tmp_table_size (und max_heap_table_size); eine größere – oder eine mit TEXT/BLOB-Spalten, die die Memory-Engine nicht halten kann – geht auf die Platte nach tmpdir. WordPress und Nextcloud nutzen TEXT-Spalten großzügig, temporäre Tabellen auf der Platte sind also kein Zeichen schlechter Optimierung, sondern unvermeidbar. Die Frage ist nur, wo „Platte“ ist.
Verbindungen, Threads, das Slow-Log
Jede Client-Verbindung bekommt einen Thread und eigene Puffer; max_connections begrenzt, wie viele es geben kann, und damit den Speicherverbrauch im schlimmsten Fall. Das Slow Query Log hält jede Anweisung fest, die langsamer als long_query_time ist – die erste Anlaufstelle, wenn eine Seite langsam ist.
🛡️ Warum es hier ist
- Beide Anwendungen werden damit gebaut und getestet. WordPress unterstützt offiziell nichts anderes; Nextcloud empfiehlt MySQL oder MariaDB.
- Eine LTS-Reihe. 10.11 bekommt Fehlerbehebungen bis Februar 2028; Updates innerhalb der Reihe sind klein und vorhersehbar.
- Gemeinschaftlich verwaltet. MariaDB wird von der MariaDB Foundation und der Firma entwickelt, nicht von einem einzelnen Hersteller mit eigenen Prioritäten.
🔧 Was dieses Repository macht
Zwei Arten, dieselbe Datenbank zu betreiben
| Blog | Nextcloud-Server | |
|---|---|---|
| Wo | Pod in K3s (mariadb:10.11.19) | Auf dem Host, MariaDB-Community-Repository, Reihe auf 10.11 festgelegt |
| Rolle | blog_k3s_deploy | next_mariadb |
| Buffer Pool | 384 MB | 2 GB / 512 MB |
tmpdir | speicherbasiertes Volume (512 MB) | tmpfs von systemd (4 GB) |
| Erreichbar über | ClusterIP-Service, NetworkPolicy: nur der WordPress-Pod | 0.0.0.0:3306, nftables: nur das Pod-Netz |
| SELinux | container_t mit eigenen MCS-Kategorien | mysqld_t |
Warum zwei Arten? Der Blog wurde von Anfang an „alles im Cluster“ gebaut. Für Nextcloud ist die Datenbank die schwerste und wertvollste Komponente, und auf dem Host bekommt sie die volle systemd-Sandbox, eine eigene SELinux-Domain und die Backup-Werkzeuge der Distribution ohne Container dazwischen.
Die Einstellungen, auf die es ankommt
[mysqld]
innodb_buffer_pool_size = 2G # per host (512M on the small one)
innodb_flush_method = O_DIRECT # no double caching in the page cache
innodb_flush_log_at_trx_commit = 2 # sync the redo log once per second
innodb_linux_aio = aio # io_uring is disabled in the kernel
max_connections = 50 # measured peak: 15
tmp_table_size = 32M
max_heap_table_size = 32M
slow_query_log = 1
long_query_time = 1
skip_name_resolve = 1 # pod IPs cannot be resolved anywayinnodb_linux_aio = aio: AlmaLinux schaltet io_uring ab (kernel.io_uring_disabled = 2). Der Standard „auto“ probierte es trotzdem zuerst, schrieb bei jedem Start eineEPERM-Warnung ins Log und fiel dann ohnehin auf klassisches Linux-AIO zurück.skip_name_resolve: Jede Verbindung aus einem Pod löste einen Reverse-DNS-Lookup für eine10.42.x-Adresse aus, der nie gelingen kann – Latenz beim Verbinden und eine Warnung pro Verbindung im Log. Sicher, weil kein Konto über einen Hostnamen berechtigt ist.max_connections = 50: Die gemessene Spitze lag bei 15 und 10 Verbindungen; 50 lässt Luft und begrenzt den Speicher im schlimmsten Fall.
Temporäre Tabellen im RAM
Vier bis sechs Prozent aller temporären Tabellen gehen auf diesen Servern auf die Platte – TEXT-Spalten, siehe oben. Seit dem 16. Oktober 2026 ist diese „Platte“ RAM: Das systemd-Drop-in gibt MariaDB ein eigenes /tmp als tmpfs (TemporaryFileSystem=/tmp:size=4G), der Blog-Pod hängt dort ein speicherbasiertes Volume ein. Ein tmpfs belegt nur Speicher, solange Dateien existieren; 4 GB lassen Platz für die Sortierdateien eines Index-Neuaufbaus bei einem Nextcloud-Upgrade. Eine Falle auf dem Weg: Zusammen mit PrivateTmp=yes gewinnt das private Verzeichnis auf der Platte, und das tmpfs wird stillschweigend ignoriert.
Die Sandbox – und fünf Wochen ohne SELinux
Das Drop-in 99-hardening.conf nimmt der Datenbank fast alles weg, was sie nicht braucht: überhaupt keine Capabilities (ein Server auf Port 3306 braucht keine), NoNewPrivileges, ein schreibgeschütztes Dateisystem bis auf Daten- und Log-Verzeichnis, einen Systemaufruf-Filter, keinen Zugriff auf Kernel-Parameter, Module oder Logs, MemoryDenyWriteExecute.
Die Unit der Distribution startet MariaDB über /bin/sh -c "… galera_recovery …; exec mariadbd". Mit gesetztem NoNewPrivileges verweigert SELinux den Domain-Übergang von der Shell (initrc_t) nach mysqld_t, außer die Policy erlaubt ihn ausdrücklich – und das tut sie nicht. Ergebnis: Vom 11. September bis 16. Oktober 2026 lief MariaDB als initrc_t, eine Domain, die SELinux kaum einschränkt, während jede Prüfung „enforcing“ meldete. Die Lösung ist eine Zeile: ExecStart=/usr/sbin/mariadbd $MYSQLD_OPTS, direkt von systemd gestartet, ohne die Shell, die es nur für Galera-Cluster gibt. Der SELinux-Deep-Dive erzählt die ganze Geschichte; die Lehre für jede Härtung: ps -eZ prüfen, nicht nur den Modus.
Konten
- Der Anwendungsbenutzer darf sich nur aus dem Pod-Netz (
10.42.%) verbinden, nicht von überall. - Anonyme Konten, die
test-Datenbank und ein Proxy-Recht auf einen Hostnamen, das durchskip_name_resolvebedeutungslos wurde, sind entfernt. - Jeder Exporter hat einen eigenen Benutzer mit minimalen Rechten (
PROCESS,BINLOG MONITOR,SELECT) – auf den Nextcloud-Hosts nutzte der Exporter früher das Root-Passwort mit, seine Konfigurationsdatei enthielt also faktisch das Root-Passwort. - Root auf dem Host meldet sich über
/root/.my.cnfan; im Pod kommt das Root-Passwort aus einem Kubernetes-Secret, das als Datei eingehängt ist, nicht als Umgebungsvariable.
📊 Gemessen
| Blog | Nextcloud A (größer) | Nextcloud B | |
|---|---|---|---|
| Version | 10.11.19 | 10.11.19 | 10.11.19 |
| Daten + Indizes | 47 MB | 361 MB | 58 MB |
| Buffer Pool | 384 MB | 2 GB | 512 MB |
| Buffer Pool ungenutzt | 87 % | 84 % | 89 % |
| Trefferquote seit Neustart (~9–11 h) | 99,70 % | 99,45 % | 99,66 % |
| Temporäre Tabellen auf „Platte“ (tmpfs) | 3,3 % | 6,0 % | 4,1 % |
| Spitze Verbindungen | 6 | 15 | 10 |
| Langsame Abfragen (> 1 s) | 0 | 0 | 0 |
Diese Datenbanken sind klein. Die ganze Nextcloud-Datenbank des größeren Servers passt fünfmal in seinen Buffer Pool, und die Trefferquote liegt nach wenigen Stunden schon über 99 % – die Fehlgriffe sind fast alle das erste Lesen nach dem morgendlichen Neustart. Keine Abfrage dauerte länger als eine Sekunde. Zwei Schlüsse: Optimierung über das hier Getane hinaus brächte nichts Messbares, und die Buffer Pools sind großzügig; auf dem größeren Server könnte er auf 1 GB schrumpfen und Speicher zurückgeben – bei 23 GB RAM gibt es dafür keinen Grund.
🧰 Betrieb im Alltag
# host (Nextcloud): root via /root/.my.cnf
mariadb -e "SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read%'"
mariadb -e "SHOW ENGINE INNODB STATUS\G" | less
tail -f /var/log/mariadb/slow.log
# pod (blog): root password from the mounted Secret
k3s kubectl -n wordpress exec -it deploy/mariadb -c mariadb -- \
sh -c 'MYSQL_PWD="$(cat /etc/secrets/db-root-password)" mariadb -uroot'
# is it confined?
ps -eZ | grep mariadbd
# a consistent dump without locking the application
mariadb-dump --single-transaction --quick --routines --events nextcloud | gzip > nc.sql.gzBackups
Ein Backup-Rechner holt jede Nacht per SSH einen Dump jeder Datenbank (--single-transaction erzeugt einen konsistenten Schnappschuss, ohne InnoDB-Tabellen zu sperren), behält die letzten 14, nachdem jeder auf Vollständigkeit geprüft ist, und versioniert alles 365 Tage lang. Wiederherstellen heißt gunzip < dump | mariadb nextcloud in eine leere Datenbank – getestet, nicht angenommen.
Upgrades
Innerhalb der Reihe kommt das Host-Paket aus dem festgelegten Repository, und dnf-automatic spielt Sicherheitskorrekturen ein; der Blog-Pod folgt dem Image-Pin in versions.yml. Ein Wechsel der Reihe – zur nächsten LTS – ist ein bewusster Schritt: erst Dump, dann Repository oder Image ändern, starten, mariadb-upgrade ausführen, Anwendung prüfen.
Passwörter
Alle Datenbank-Passwörter liegen im Ansible-Vault. Eines zu wechseln heißt: im Vault ändern, Playbook laufen lassen – es setzt das neue Passwort im selben Lauf in MariaDB und in der Anwendungskonfiguration.
🆘 Spickzettel: Symptom → Befehl
Die Kurzfassung dieses Artikels für den Moment, in dem etwas nicht stimmt. Auf dem Host als root ausführen; <…> sind Platzhalter.
| Symptom | Befehl |
|---|---|
| Läuft sie, und eingesperrt? | systemctl status mariadb |
| Sie startet nicht | journalctl -u mariadb -n 50 |
| Die Anwendung ist langsam | tail -f /var/log/mariadb/slow.log |
| Sperren oder Deadlocks | mariadb -e "SHOW ENGINE INNODB STATUS\G" | less |
| Ist der Buffer Pool groß genug? | mariadb -e "SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read%'" |
| Eine Datenbank-Shell auf dem Blog | k3s kubectl -n wordpress exec -it deploy/mariadb -c mariadb -- \ |
| Ein Dump vor einer Änderung | mariadb-dump --single-transaction --quick --routines --events nextcloud | gzip > nc.sql.gz |
| Sind die Tabellen in Ordnung? | mariadb-check --all-databases |
| Keine Datenbank-Grafiken in Grafana | systemctl status mysqld_exporter |
🔭 Wie es weitergeht
- Point-in-Time-Recovery. Mit eingeschaltetem Binärlog könnte eine Wiederherstellung auf jede Sekunde zwischen zwei nächtlichen Dumps gehen. Bei diesen Schreibmengen reicht der nächtliche Dump; es wäre die erste Änderung, falls das einmal nicht mehr stimmt.
- Die nächste LTS-Reihe deutlich vor Februar 2028, zuerst auf dem kleineren Nextcloud-Server getestet.
- Abfrage-Analyse mit dem Performance Schema in Grafana – heute gibt es nichts Langsames zu analysieren.
⚖️ Alternativen – und warum MariaDB
| Option | Stärke | Warum nicht hier |
|---|---|---|
| PostgreSQL | Hervorragende Datenbank, Nextcloud unterstützt sie | WordPress unterstützt sie nicht nativ; zwei Datenbank-Engines für zwei Anwendungen würden den Betrieb verdoppeln. |
| MySQL 8 | Das Original, von beiden unterstützt | Von einem Hersteller gesteuert, Funktionen entfernen sich von MariaDB; kein Vorteil für diese Lasten. |
| SQLite | Gar kein Server | Gut für Tests, nicht für eine Mehrbenutzer-Nextcloud mit gleichzeitigen Schreibzugriffen. |
| MariaDB 10.11 LTS | Von beiden unterstützt, gemeinschaftlich verwaltet, Langzeitreihe | Gewählt. |
MariaDB ist die unaufregendste Komponente in diesem Setup, und das ist ein Kompliment. Die interessanten Funde lagen gar nicht in der Datenbank, sondern drumherum: in der System-Unit, die sie startete, im Kernel, der eine I/O-Schnittstelle abschaltete, im Verzeichnis, in das temporäre Tabellen geschrieben wurden. Die Datenbank selbst hat einfach ihre Arbeit getan – ohne eine einzige Abfrage über einer Sekunde.


