Backup ohne verprobte Recovery ist wertlos – 5 Gründe

written by Martin Häcker on

Backup und Recovery

Auf der DevOpsCon 25 in Berlin habe ich viel aus dem Vortrag Backup and Disaster Recovery: Business as Usual or What Needs to Change Now? - DevOps Conference & Camps gezogen. Zum technischen Inhalt gehe ich noch mal Separat ein, aber zuerst wollte ich meine Schlüssel-Lernergebnisse auf einer sehr hohen Flughöhe mitbringen.

1. Backups ohne Restore sind nur Datenfriedhöfe

Ein Backup ist kein Wert an sich. Es ist nur die Basis für Geschäftskontinuität – erst der funktionierende Restore bringt das Unternehmen zurück ins Geschäft.

2. Zeit entscheidet über Resilienz

Recovery Time Objective (RTO) ist der kritische Faktor – nicht die schiere Menge oder Existenz von Kopien. Entscheidend ist: Wie lange darf welcher Teil des Geschäfts ausfallen, bis es ernsthafte Schäden nimmt?

3. Kontinuierlicher Minimal-Restore trennt Dauer von Ausfallzeit

Ein innovativer Ansatz ist, kontinuierlich eine schlanke, nicht skalierte Version der Systeme aus den Backups hochzufahren. Damit lässt sich jederzeit beweisen: Restore funktioniert. Und: Die Dauer des eigentlichen Restore-Vorgangs aus den Backups kann nahezu beliebig sein, ohne wesentlich das RTO zu gefährden.

4. Vorhersagbare Skalierung statt unberechenbarer Wiederanlauf

Im Notfall geht es nicht darum, ob das Backup läuft, sondern wie schnell man wieder auf ausreichende Kapazität kommt. Wer Skalierung auf Abruf plant, kennt die Antwort: „In X Minuten / Stunden ist die Leistung verfügbar.“ Das macht den Unterschied zwischen Chaos und geplanter Resilienz.

5. Kosteneffizienz und Compliance in einem

Statt teurer Standby-Infrastruktur entsteht ein Modell, das laufend minimale Kosten verursacht, aber im Ernstfall sofort hochskaliert. Dazu kommt: Unternehmen erfüllen so auch regulatorische Anforderungen nach nachweisbarer Wiederanlauffähigkeit.

Fazit: Backups sind nur der Anfang. Erst wenn Restore und Disaster Recovery gemeinsam gedacht werden – mit kontinuierlichem Minimal-Restore und skalierbarer Infrastruktur – entsteht echte Business-Kontinuität.

Textbearbeitung auf der Shell: grep, cut, awk, sed und tr

written by Martin Häcker on

`tr` in aktion

Diese fünf kleinen Tools sind echte Arbeitspferde für jeden, der mit Text auf der Kommandozeile arbeitet. Sie haben viele Optionen – aber man braucht nicht alles zu kennen. Schon mit ein paar Grundbefehlen kann man 80 % der typischen Aufgaben lösen.

1. grep – Zeilen finden

grep filtert Textzeilen anhand von Mustern. Praktisch, wenn man in langen Outputs schnell das Relevante sehen will.

Aufgabe Befehl Beispiel
Textzeilen ausgeben, die ein Wort enthalten grep PATTERN `cat protokoll.txt grep "Fehler"`
Zeilen ausschließen grep -v PATTERN grep -v "^#" protokoll.txt
In einem ganzen Projektbaum nach grep -r PATTERN DIR grep -r "TODO" src/

Tipps

  • -i ignoriert Groß/Kleinschreibung
  • -e aktiviert Reguläre Ausdrücke (unter linux gibt es noch -P für Perl-kompatible Regex)

Kombinationen über Pipes machen grep besonders stark:

grep -r -P "def \w*\(" . --after-context 30 | grep "foo" | grep -v "bar"

Für große Projekte lohnt sich ripgrep (rg), weil es schneller ist, .gitignore respektiert und reguläre Ausdrücke standardmäßig aktiviert.

2. cut – Spalten und Zeichen

cut schneidet Spalten oder Zeichen aus Textzeilen heraus. Funktioniert am besten, wenn der Trenner ein einzelnes Zeichen ist.

Aufgabe Befehl Beispiel
Spalte 2 eine CSV 1 -d ',' -f 2 cut -d ',' -f 2 daten.csv
Zeichen 5‑10 einer Zeile -c 5-10 cut -c 5-10 string.txt
Mehrere Felder -f 1,3,5 cut -d ';' -f 1,3,5 daten.txt

Bei Whitespace-getrennten Daten stößt cut an Grenzen – da ist awk besser.

3. awk – Felder und Muster

awk versteht Texte als Felder, getrennt durch Whitespace oder ein angegebenes Zeichen. Wenn man möchte, kann man mit awk Text-Dateien fast wie Datenbanken abfragen, aber für den Anfang:

Aufgabe Befehl Beispiel
Spalten ausgeben {print $1, $3} awk '{print $1, $3}' daten.txt
Letzte Spalte ausgeben {print $NF} awk '{print $NF}' daten.txt
Summe einer Spalte {s+=$2} END {print s} awk '{s+=$2} END {print s}' zahlen.txt

Nützliche Variablen

  • NF = Anzahl Felder in der Zeile
  • NR = aktuelle Zeilennummer

4. sed – Suchen und Ersetzen

sed ist ein Stream-Editor: ideal für Ersetzungen und einfache Transformationen.

Aufgabe Befehl Beispiel
Ersetzen s/alt/neu/g sed 's/Fehler/Warning/g' log.txt

Vorsicht: Nie direkt in die gleiche Datei schreiben mit > – sonst überschreibst du sie, bevor sie gelesen wurde. DAMIT HABE ICH SCHON DATEN VERLOREN Stattdessen:

sed -i 's/alt/neu/g' datei.txt   # Direkt in der Datei ändern

5. tr – Zeichen übersetzen

Aufgabe Befehl Beispiel
Groß→Klein tr [:upper:][:lower:] tr '[:upper:]' '[:lower:]' < input.txt
Leerzeichen löschen tr -d ' ' tr -d ' ' < file
ROT13 (Caesar‑Shift) tr 'a-z' 'n-za-m' tr 'a-z' 'n-za-m' < text.txt
Mehrere Zeichen gleichzeitig tr 'abc' 'ABC' tr 'abc' 'ABC' < input.txt

Beispiel: $PATH lesbarer machen:

 echo $PATH | tr ':' '\n' | nl

Fazit

  • grep: Zeilen nach Mustern filtern
  • cut: Spalten oder Zeichen ausschneiden.
  • awk: Felder flexibel verarbeiten und berechnen.
  • sed: Ersetzen und transformieren
  • tr: Zeichen umwandeln oder löschen

👉 Zusammengeschaltet mit Pipes (|) werden diese Tools zu einem Schweizer Taschenmesser für Textbearbeitung – schnell, skriptbar und überall verfügbar.


  1. CSV = Comma-Separated Values, kann natürlich auch maskierte Kommas in dem Wert enthalten, was dieser Befehl geflissentlich ignoriert oder falsch macht. Verwendet zum CSV-Parsen also bitte nicht diesen Shell-Befehl. Dieser Befehle sind dafür da, auf der Shell schnell mal in eine Datei hineinzuschauen. Wenn das Ergebnis gut genug ist, kann man es auch weiter verarbeiten.

Arbeiten mit JSON & YAML auf der Shell

written by Martin Häcker on

yaml ganz angenem auf der Shell

Arbeiten mit strukturierten Datenformaten auf der Shell kann anstrengend sein – wenn man noch nicht die richtigen Tools dafür hat. Ob JSON aus einer API, YAML aus Kubernetes-Manifests oder riesige Logfiles – ohne passende Helferlein endet man schnell bei unübersichtlichem grep, less und Copy&Paste.

Zum Glück gibt es eine Reihe von Werkzeugen, die genau dafür gemacht sind:

In diesem Beitrag stelle ich euch diese Tools vor – mit Beispielen, die ihr direkt in eurer eigenen Shell ausprobieren könnt.


jq – der Klassiker für JSON

Das vermutlich bekannteste und am weitesten verbreitete Tool für JSON ist jq. Es ist so etwas wie der Schweizer Taschenmesser für JSON:

  • formatiert unlesbare Minified-JSON-Dateien,
  • extrahiert gezielt Werte,
  • filtert und transformiert Daten,
  • eignet sich für einmalige Ad-hoc-Analysen genauso wie für Skripte.

Ein einfaches Beispiel: JSON schön formatieren:

$ cat data.json
{"user":{"id":42,"name":"Alice"},"active":true}
$ cat data.json | jq # oder `jq < data.json`
{
  "user": {
    "id": 42,
    "name": "Alice"
  },
  "active": true
}

So schnell wird aus einem unübersichtlichen Einzeiler eine lesbare Struktur.

Mann kann es aber auch super in scripten verwenden um Daten aus json zu extrahieren:

get_current_gitlab_token() {
    kubectl get secret $SECRETNAME -n $INITIALNAMESPACE -o json |
        jq -r '.data[".dockerconfigjson"]' 2>/dev/null |
        base64 --decode |
        jq -r '.auths["registry.gitlab.com"].password' 2>/dev/null
}

Oder um in einem JSON-File Daten einzutragen:

kubectl get secrets shipa-certificates -o json \
        | jq ".data[\"ca.pem\"] |= \"$CA_CERT\"" \
        | kubectl apply -f -

jq kann noch viel mehr. Wer das Werkzeug noch nicht kennt sollte unbedingt ein paar Minuten investieren um das Tutorial querzulesen, und dann bei der nächsten Einsatzmöglichkeit gezielt nach der Syntax schauen. Pro tip: KI's können diese Syntax prima, und jless (kommt gleich) kann diese auch generieren.


jo – JSON in der Shell erzeugen

Während jq ideal zum Lesen und Transformieren ist, eignet sich jo perfekt, um JSON direkt in der Shell zu erstellen. Damit lassen sich Testdaten oder API-Payloads schnell zusammenbauen.

Ein einfaches Beispiel:

jo name=Alice age=30 active=true

Ausgabe:

{"name":"Alice","age":30,"active":true}

Auch Arrays sind möglich:

jo -a red green blue

["red","green","blue"]

Verschachtelte Objekte:

jo user=$(jo name=Alice id=42) project=Demo

{"user":{"name":"Alice","id":42},"project":"Demo"}

Das eignet sich sehr gut für schnelle curl-Requests, aber insbesondere auch für shell scripte in denen inline json sonst sehr schnell sehr unübersichtlich wird:

curl -X POST -H "Content-Type: application/json" \
     -d "$(jo username=dev password=secret)" \
     https://example.com/api/login

yq – YAML lesen und bearbeiten

YAML verwenden wir überall, am meisten habe ich in Kubernetes damit zu tun. yq ist das Werkzeug der Wahl, um YAML-Dateien zu lesen, zu durchsuchen und zu editieren. Die Syntax ist sehr nahe an jq, das Werkzeug kann neben YAML aber auch JSON, TOML und XML verarbeiten, und zwischen diesesn Konvertieren. Im wesentlichen kann es das gleiche wie jq, eben auch für YAML.


jless & yless – interaktiv stöbern

Wenn Dateien zu groß oder zu komplex werden, helfen jless und yless (ein alias yless=jless --yaml. Sie bieten eine interaktive Ansicht für JSON und YAML – mit:

  • Syntax-Highlighting,
  • Falten und Aufklappen von Strukturen,
  • komfortabler Navigation und Suche.

Beispiel:

kubectl get pods -oyaml | yless

Das schöne: Man kann hier auf der Shell wunderbar uninteressante textblöcke einklappen um schnell die wichtigen Informationen zu fokussieren, und dann über Tastaturkommandos werte, oder jq/yq filter auf das aktuell ausgewählte Element kopieren, um das z.B. dann auf alle Pods in einem Namespace anzuwenden.


Fazit

Mit diesen Tools – jq, jo, yq, jless und yless – wird das Arbeiten mit JSON und YAML auf der Shell deutlich angenehmer. Sehr gut investierte Zeit diese Werkzeuge (eines nach dem Anderen) zu lernen.

Mit llm RAG einfach mal ausprobieren – direkt von der Shell

written by Martin Häcker on

Ablaufdiagramm Einer der coolsten Aha-Momente der letzten Tage bei mir war, wie einfach und niederschweflig das Arbeiten mit Retrieval-Augmented Generation (RAG) von der Shell inzwischen sein kann – ganz ohne spezielle Infrastruktur, Vektor-Datenbank oder Server-Backend. Das Python-Tool llm macht das möglich: RAG direkt aus der Kommandozeile, mit SQLite als Backend und einfachen Kommandos, die sich hervorragend in Shell-Workflows integrieren lassen.

Ein Power Tool für Sprachmodelle in der Shell

llm kann natürlich noch viel mehr und ist ein Power Tool, das den Einsatz von KI direkt im Terminal erlaubt. Es erlaubt mit beliebigen API-Providern oder lokalen Modellen zu sprechen und integriert diese damit nahtlos in eigene Skripte. Use Cases: Daten hinein pipen und mit dem LLM bearbeiten. Ob zusammenfassen, erklären, übersetzen, mit einem aufwendigen Prompt aus einer Datei beackern… Da geht so viel. Egal was Ihr aus diesem Artikel mitnehmt, zumindest sollte es sein das Ihr llm in euren Workflow aufnehmt und installiert.

Der RAG-Workflow von der Shell aus

Ein kompletter RAG-Workflow funktioniert mit llm in wenigen Schritten. Diese Demo hier hab ich von Simon Willison geklaut, dem Autor von llm. Hier wird semantische Suche in den Python Enhancement Proposals demonstriert. Dieses Tutorial verwendet einen lokalen LLM-Server, man kann das aber natürlich auch mit GitHub Copilot oder ähnlichen Modellen machen. Herauszufinden wie die dort heißen und zu verbinden sind bleibt ein Exercise für den Leser.

1. Dateien vorbereiten

Wir erstellen gekürzte Versionen von Textdateien:

mkdir -p peps-truncated
for f in peps/*.rst; do
  head -c 8000 "$f" > "peps-truncated/$(basename "$f")"
done

Das ist natürlich streng genommen falsch, weil wir ganz viel Daten wegschmeißen. Einiges spricht aber trotzdem dafür:

  1. Gerade lokale Modelle haben gerne nicht so große Kontext-Fenster und da muss das Dokument reinpassen.
  2. Meistens steht am Anfang eines Dokuments worum es geht. Für unsere Zwecke also eine gute Näherung.

Um das später in ein Produkt umzuwandeln müssten wir uns noch weitere Strategien anschauen, z.B. mit einem Sliding Window über die Dokumente zu gehen und für jeden Abschnitt ein Embedding zu erzeugen. Grundsätzlich ist es aber eine gute Idee verschiedene Indexe zu erzeugen die unterschiedliche Zwecke erfüllen.

2. Vektor-Index erstellen

llm embed-multi peps \
    -m mxbai-embed-large \
    --files peps-truncated 'pep-3*.rst' \
    -d peps.db \
    --store

Das erzeugt eine SQLite-Datenbank mit eingebetteten Vektoren. Wichtig: Die Datenbank speichert auch, mit welchem Modell die Einbettung erfolgte – bei weiteren Operationen auf der gleichen Collection peps ignoriert llm den Modell-Parameter ohne Fehlermeldung!

Seiten-Notiz: Embeddings, was ist das? Ein Embedding ist eine Umwandlung von einem Text in eine Zahlenreihe (Mathematisch: Einen Vektor). Jeder dieser Vektoren beschreibt eine Koordinate in einem Hoch-Dimensionalen Raum, mit der Eigenschaft, das Koordinaten die sich Nahe sind von einem Text kommen der semantisch Ähnlich ist. Darin kann man sogar Rechnen, ein etwas überstrapaziertes Beispiel wäre z.B. der Vektor für König + Weiblich = Königin. Mehr gibts hier

3. Abfragen stellen

llm similar -c "What do string templates look like?" \
  -d peps.db -n 2 peps \
  | llm -s "Answer the question: What do string templates look like?" \
    -m devstral -o num_ctx 256_00

Hier wird erst ähnliche Inhalte zur Frage gesucht. Wichtige Details: Das Kontext-Fenster des Sprachmodells das antwortet muss groß genug sein, das es alle Antworten in seinen Kontext aufnehmen kann. Ansonsten wird gerne der Anfang abgeschnitten - und da steht natürlich der Treffer mit dem besten Score.

4. Automatisieren

Wir können sogar direkt ein Skript generieren lassen:

llm '
Build me a bash script like this:
./pep-qa.sh "What do string templates look like?"
It should first run:
llm similar -c $question -d peps.db peps
Then it should pipe the output from that to:
llm -s "Answer the question: $question" -m gpt-4.1-mini
That last command should run so the output is visible as it runs.
' -x > pep-qa.sh

Fazit

Was früher nach viel Setup aussah (Vektor-Datenbank, Backend-API, RAG-Pipeline), ist heute in wenigen Shell-Befehlen machbar. SQLite funktioniert dabei erstaunlich gut – llm führt einfach einen Full-Table-Scan durch und berechnet die Vektor-Abstände direkt. Das ist nicht hyper-skalierbar, aber bis 10.000 bis 100.000 Einträge durchaus brauchbar.

Und das Beste: llm lässt sich überall in bestehende Shell-Skripte und Workflows einbauen – sogar mit piped Input. Wer also mal eben eine intelligente Suche oder ein Sprachmodell in seinen CLI-Workflow integrieren will, findet hier ein extrem mächtiges Toolset.

Ich kann nur empfehlen ein Shell-Werkzeug wie llm zu lernen.

DevOpsCon Keynote – Security ist jetzt Teil von DevOps?

written by Martin Häcker on

Metapher auf Teams die jeweils Sicherheits-Interessierte enthalten, die einfach von Spezialisten Unterstützung erhalten können

Ich konnte auf der DevOpsCon der Keynote "From Static to Strategic: Reimagining Application Security for a DevOps World" von John D. Wood nicht entgehen. Um ehrlich zu sein: Die Keynote hat mich nicht begeistert. Am ehesten noch fand ich interessant das er Zahlen hatte, das die meisten Sicherheitslücken (auch nach Bekanntwerden) noch über ein Dreiviertel-Jahr offen sind. Aua. Ein Satz am Ende ist bei mir hängengeblieben:

"DevOps bekommt jetzt auch Security."

Was simpel klingt, ist in der Realität alles andere als einfach – und betrifft uns ganz konkret. In einer Zeit, in der wir als Unternehmen KRITIS-relevant geworden sind, ist es für viele Risiken nicht mehr möglich sie einfach zu übernehmen (AKA aussitzen). Wir müssen sie aktiv mitigieren – und das gilt besonders auch für Security-Risiken.

Security: Aus DevOps wird "DevSecOps"?

Dev ist schon schwer. Ops noch schwieriger. Und jetzt kommt Security obendrauf. Nicht jedes Team kann oder will alle drei Disziplinen gleich gut abdecken. Und dennoch ist klar: Es kommt mehr Arbeit auf uns zu.

Wir werden bei bestimmten Anwendungen in Zukunft viel stärker darauf achten müssen:

  • Dass Betriebssysteme (in Docker-Containern) aktuell sind,
  • Dass Projektabhängigkeiten regelmäßig gepflegt werden,
  • Dass wir verstehen, welche Sicherheitslücken für uns relevant sind – nicht nur, weil ein Scanner sie anmeckert, sondern weil unsere eigene Risikoabwägung sie als kritisch einstuft.

Wunsch-Szenario: Expertise teilen, nicht duplizieren

Mein Wunsch wäre, dass nicht jedes Team die komplette Security-Expertise selbst aufbauen muss. Stattdessen sollten sich diejenigen, die sich besonders für das Thema interessieren, tiefer einarbeiten können – und jederzeit unkompliziert Zugang zu Spezialist\:innen haben. Idealerweise entsteht daraus eine Art Mentoring-Beziehung.

Das Ziel: Schnell und unbürokratisch Expertise bekommen, wenn man merkt, dass man sie braucht. Entwickler\:innen sollen lernen, Security-relevante Situationen zu erkennen und wissen, wo sie gezielt und niederschwellig Unterstützung finden.

Compliance-Checkbox oder echte Verteidigung?

Ein starkes Bild aus dem Vortrag war die Kritik an klassischen Security-Prozessen: Wöchentliche Scans, lange Backlogs mit offenen CVEs, endlos False Positives, wenig konkreter Nutzen. All das erzeugt ungeplante Arbeit, und widerspricht damit dem DevOps-Grundsatz "No unplanned work" – und hilft im Ernstfall wenig. Letztlich müssen wir ja die Daten unserer Versicherten Schützen, dass der Bug den ein Angreifer bei uns Ausgenützt hat schon lange bekannt war, hilft uns nicht weiter.

Fazit

Ich nehme aus dem Talk vor allem eines mit: Security können wir in Zukunft nicht länger als "Problem von jemand anderem" verstehen. Sie ist jetzt (oder wird es bald) integraler Teil von DevOps – ob uns das passt oder nicht.

Und wir müssen Wege finden, wie wir das gemeinsam schultern können – weil wir eben nicht jeden Entwickler zum Security-Experten machen können.

Die bittere Realität: Container-Sicherheit ist Kaputt

written by Martin Häcker on

Container Security Vor einigen Wochen war ich auf der DevOpsCon in Berlin. Einer der spannenderen Vorträge war für mich: "Supply Chain Security and the real world: Lessons from Incidents". Offiziell ging es um Sicherheit in Container-Umgebungen. Inoffiziell war es eine Abrechnung mit dem Chaos, das wir im Alltag mit Docker-Containern erleben.

Wir alle lieben Docker Hub: Schnell ein Image ziehen, starten, fertig. Aber was dabei oft vergessen wird: Selbst die als "offiziell" markierten Container bieten keinerlei Garantien. Weder über die Aktualität noch über Sicherheit. Fast alle dieser Container werden monatelang nicht aktualisiert, obwohl es CVEs für enthaltene Komponenten gibt.

Wenn man das ernst nimmt, müsste man:

  • Alle Container regelmäßig selbst neu bauen und dabei Updates der darin enthaltenen Distributionen installieren
  • Bei jeder Abhängigkeit überwachen, ob es CVEs oder Updates gibt
  • Die Upstream-Projekte verstehen, ihre Update-Kanäle abonnieren
  • Alle Dockerfiles durchdringen (insbesondere bizarr manuell installierter Binaries)

Kurz: Wer Container sicher betreiben will, muss eigentlich selbst zur Distribution werden.

Die Lösung: "Start Left" statt "Shift Left"

Die Firma Chainguard hat in dem Vortrag ihre Alternative vorgestellt: Ein Repository von sicherheitsoptimierten, rootlosen Containern, die alle:

  • ohne bekannte CVEs ausgeliefert werden
  • auf einem selbstgebauten, deterministischen OS basieren ("Wolfi", im wesentlichen Alpine mit GLibC statt Musl)
  • reproduzierbar gebaut werden (jede\:r kann sie nachbauen, wenn auch nicht Bit für Bit)
  • mit Software-Bill-of-Materials (SBOM) ausgeliefert werden
  • Auch im Container als normale Nutzer ausgeführt werden anstatt als `root`
  • und bei neuen Upstream-Vulnerabilities innerhalb von 7 Tagen gepatcht werden

Die Container sind FIPS-kompatibel, in zwei Varianten (Production vs. Dev - mit mehr Tools) verfügbar und mit bekannten Tools wie trivy problemlos scannbar. Offen Statistiken zeigen beeindruckend wie viel weniger CVEs sieh in Ihren Containern haben. Nämlich in der Regel gar keine.

Warum das für viele ein Game-Changer sein könnte

Wie in vielen Unternehmen, ist auch bei uns die Nutzung von Docker-Containern ein "Free for All": Jeder zieht, was er braucht, hauptsache es läuft. Sicherheitsrichtlinien? Nicht vorhanden. Updates? Machen wir Manuell alle Jubeljahre. Genau hier setzen die Container von Chainguard (oder auch Dockers eigene "Hardened Images") an:

Man könnte sagen:

Was sonst niemand zuverlässig macht, macht ChainGuard automatisch.

Und das Beste: Die Basis-Container sind sogar kostenlos (und damit z.B. auch für Open Source Projekte) verwendbar. Damit kann man ohne großen Aufwand den Produktivbetrieb auf eine wesentlich sicherere Basis stellen.

Gerade wenn man – wie bei KRITIS-Vorgegeben – innerhalb eines festen Zeitrahmens Sicherheitsupdates einspielen muss, ist eine verlässliche Update-Garantie Gold wert.

Mein Fazit

Container sind kein Selbstzweck – und sie haben einen großen Nachteil gegenüber Veränderbarer Infrastruktur: Man verliert die automatisch installierten Updates der Linux Distributionen. Wer diesen Aufwand nicht selbst stemmen will, braucht Alternativen. Die gehärteten Container von ChainGuard oder Docker sind ein vielversprechender Weg, um mit minimalem Aufwand viel weniger CVEs und fehlender Updates, sowie weit mehr Transparenz gewinnen kann.

Probiert es doch mal aus. Ich würde mich sehr über Feedback freuen wo die Grenzen und Probleme dieses Ansatzes liegen. Offensichtlich ist schon mal, dass dort nicht so viele Container gibt.

Grundsätzlich aber immer: Bitte Entscheidet euch bewusst für Images, die gepflegt werden. Und nicht für das erste, das die Suche auf Docker Hub zurück gibt.

Nützliche Shell-Kommandos: sort, uniq

written by Martin Häcker on

Set Operations Weil ich es heute wiederholt nachschlagen musste, hier noch eine Erinnerung an mich selbst, wie einfach es ist auf der Shell set Operationen durchzuführen.

In unserem Beispiel: ~10k Datenbank-IDs hier, ~15k Datenbank-IDs da, und die Frage welche davon nur in der einen Liste enthalten sind. Das ist dann einfach zu beantworten, wenn man die auf eine ID pro Zeile ausgibt, und dann einfach mit set_difference bearbeitet.

set_union () {
   sort $1 $2 | uniq
}

set_intersection () {
   sort $1 $2 | uniq --repeated
}

set_difference () {
   sort $1 $2 $2 | uniq --unique
}

set_symmetric_difference() {
   sort $1 $2 | uniq --unique
}

Quelle

DevOpsCon: Stolperfallen beim Aufbau interner Developer Platforms (IDP)

written by Martin Häcker on

Heute gibts den Start meines Berichts von der DevOpsCon 25. So viel allgemeines vorweg: War schön. Druckbetankung wie es sich gehört.

Los ging es mit einer Keynote über interne Entwickler-Plattformen und was dabei gerne schief geht.

1 | Reality-Check: 90 % der Devs nutzen schon eine IDP – wir auch

  • Laut Jessica arbeiten rund 90 % aller Entwickler:innen mittlerweile mit einer internen Plattform – oft, ohne es zu merken.
  • Wir auch? Was ist unsere Plattform?

2 | Das theoretische Fundament

Jessica empfahl vier Bücher, die jede:r Platform‑Builder kennen sollte. (Lustigerweise gab Sie zu, das Sie selbst noch nicht alle davon gelesen komplett gelesen hat.)

Buch Kernaussage für IDP Mein Take‑away
Transformed Von Silos zu empowereden Produkt‑Teams Auch Plattform‑Teams brauchen Produktdenken und Produkt-Manager.
Team Topologies Team‑Typen & Schnittstellen (u. a. Platform & Enabling Teams) Klingt sehr ähnlich zu dem wie wir organisiert sind. Unterschiede: Komplizierte Subsysteme brauchen eigene Teams. Enabling‑Teams sind hier fast immer temporär.
Accelerate Deploy‑Freq., Lead‑Time, Failure‑Rate, MTTR DORA‑Scores = Proxy für Platform-Team‑Erfolg. Nugget: Im Raum war niemand der alle 4 Metriken einsetzt, oder jemanden kennt der das tut…
Platform Engineering Plattform ersetzt Glue‑Code & schafft Self‑Service Eine IDP ist Software, kein Ops‑Team.

3 | Typische Fallstricke

  1. Menschen im Plattform-Team bringen ihre Erfahrungen mit
    • „It’s faster if I just do it.“ Sorgt gerne dafür das das Ergebnis auch nur Sie benutzen können.
    • Damit erzeugt man ein neues Ops-Silo. Dringend zu vermeiden. Das Ziel ist, das andere Teams sich selbst helfen können.
  2. Rudis Resterampe (Scope Creep)
    • Plattform‑Team sammelt alles, was sonst niemand machen will.
    • Konsequenz: Keine Zeit mehr für strategische Funktionen.
    • Braucht eine klare Vision, und insbesondere By-In von Leadership. Dann Iterationen und viel Kommunikation.
  3. Alles sofort lösen wollen (Rudis Resterampe 2.0)
    • Minimalismus ist King 👑. Nicht jedes Problem muss gleich gelöst werden.
    • Priorisiere Onboarding, Self‑Service & Empowerment.
  4. Das falsche Problem Lösen
    1. Es ist sehr einfach das falsche Problem zu lösen. Schließlich sind wir alle Entwickler und wissen was wir brauchen.
    2. Aber jedes Team ist anders und hat andere Aufgaben. Daher ist es unglaublich wichtig mit den Menschen intensiv zu sprechen die man beglücken will.
    3. Plattform-Engineering braucht genauso Produkt-Management und einen Produkt-Manager wie andere Themen. Wenn das Budget dafür nicht vorhanden ist, muss man diese Aufgaben trotzdem erfüllen.
  5. Plattform‑Migrationen unterschätzen
    • Jede Migration ist Schmerzhaft und kostet Vertrauen (Vertrauen ist wie eine Währung. Wenn man es ausgegeben hat, ists wech).
    • Daher so wenig Migrationen wie möglich, und diese gut vorbereiten, auch wenn es viel Aufwand erzeugt.
    • Ziel: Automatisierte, Low‑Impact‑Migrationspfade.

4 | Fragen an den Leser

  1. Was ist deine aktuelle Plattform?
  2. Wo klemmt es eigentlich derzeit?
  3. Macht es Sinn die DORA‑Baselines zu messen?

5 | Fazit

Ein internes Developer‑Platform‑Team ist kein Sonder‑Ops‑Team, sondern ein Produkt‑Team mit klarer Vision, fokussiertem Scope und messbarem Impact. Je einfacher, desto besser – und Vertrauen ist kostbar.

„Minimalismus ist King – löse die wichtigsten 20 % zuerst, die den Teams 80 % des Schmerzes nehmen.“ – Jessica Anderson

Die Freuden einer gut eingerichteten Shell: fzf

written by Martin Häcker on

fzf

Nachdem es bisher in der Serie um die grundlegende Einrichtung der Shell, einen guten Prompt und funktionierende autoomatische Vervollständigung ging, geht es jetzt eine Weile um Werkzeuge um mit der Shell effizient zu navigieren und Dateien und Inhalte zu finden.

Einleitung

Hier geht es mir darum das die Arbeit auf der Shell (auf dem eigenen Rechner vor allem) nur dann schnell und Effizient ist, wenn man schnel und einfach in die Ordner kommt in denen man arbeiten möchte, und die Dateien findet in denen etwas interessantes steht das man entweder lesen oder verändern möchte.

Und natürlich ist das Skillset auch auf beliebige Server transferierbar, weil man alle diese Werkzeuge (oder deren etwas primitivere Variante, dazu später mehr) auch auf einem Server, oder in einem Docker-Container, gerne auch auf einem Kubernetes-Cluster in Produktion einsetzen kann, wo man sonst halt nicht so viele Werkzeuge hat, und schon gar nicht seine IDE anschließen kann um zu versuchen dort Herr der Lage zu werden.

Dazu möchte ich euch die Tools zoxide, grep/ripgrep, fzf, less/cat/bat und direnv vorstellen.

Diese Tools erleichtern viele täglich oft wiederholte Arbeitsabläufe dramatisch, und sie ermöglichen viele Use-Cases, die viele grafischen IDEs und Text-Editoren gar nicht unterstützen. Los geht es mit dem flauschigsten der Tools, fzf.

Zackig die richtige Datei finden: fzf

fzf frei Eingezangendeutscht "der flauschige Kommandozeilen Finder", ist ein werkzeug um Dateien (aber auch beliebige andere Dinge) anhand von teilen des Namens oder Mustern wie jeder erste Buchstabe der Wörter ihres Namens zu finden. Dazu bietet dieses Werkzeug eine Oberfläche die interaktiv die Liste der Auswahlmöglichkeiten filtert während man tippt. Die meisten IDEs bieten so eine Funktion irgendwo mehr oder weniger gut versteckt an, und dieses Werkzeug portiert diese Funktionalität als generisches Werkzeug in die Shell.

Als beispiel: Ich möchte einen bestimmten UnitTest ausführen:

$ bin/run_tests_in_docker.sh $(fzf)

Mit diesem Kommando, wird zuerst fzf aufgerufen (wegen $(fzf)) was dann eine oberfläche präsentiert, mit der man interktiv die richtige oder die richtigen Dateien auswählen kann.

# ich verwende die fish shell, daher brauch ich das $ nicht
❯ bin/run_tests_in_docker.sh (fzf)
  src/models/dokumente/tests/document_distribution_test.py
  src/controller/process_distribution/tests/models_test.py
  src/models/dokumente/tests/dokumente_test.py
  src/integration/d3/api/test/models_test.py
▌ src/models/tests/kontaktdaten_test.py
  5/448 ────────────────────────────────
> models 'test.py

In dem Interface kann man auch mit den Pfeiltasten navigieren, oder einen Eintrag anklicken. Der von mir eingegebene Suchstring "models 'test.py" bedeutet, dass 'models' irgendwo in dem Treffer diese Buchstaben in dieser Reihenfolge vorkommen müssen, während "'test.py" erzwingt das der exakte String 'test.py' vorkommen muss.

Wenn man die fzf-Integration mit der eigenen Shell aktiviert, kriegt man viele weitere Integrationen in die Shell dazu. Zwei Beispiele:

  • ⌃-T sucht (mit Vorschau!) nach Dateien unterhalb des aktuellen Verzeichnisses. Das ist immer dann Praktisch wenn man für ein Kommando eine Datei aus dem aktuellen Projekt als Argument übergeben muss, und spart das tippen von $(fzf). Klar, mit Auto-Vervollständigung kommt man auch ans Ziel, aber das ist soo viel schneller. Insbesondere wenn man nicht genau im Kopf hat wo die Datei liegt, aber noch weiß was in Ihrem Namen oder Pfad vorkommen muss. Das verwende ich die ganze Zeit.

  • ⌃-R sucht mit fzf in der Shell-Historie. Das funktioniert viel besser als die Standard-Suche, die nur nach direkt zusammenhängenden Buchstaben suchen kann. Ein Beispiel: Das Wenn ich das Kommando helm template extensions ./k8s/extensions/ --values ./k8s/extensions/values.dev.yaml | yq aus meiner historie suchen möchte, müsste ich ohne fzf den exakten Text schreiben der in dem Kommando vorkommt.

~
Search History> helmtemplateexten
  76/32637 (0)
  02-17 18:07:03 │ helm template extensions ./k8s/extensions/ --values ./k8s/extensions/values.dev.yaml
  02-17 18:06:10 │ helm template extensions ./k8s/extensions/ --values ./k8s/extensions/values.dev.yaml | yq
  02-17 17:59:53 │ helm template extensions ./k8s/extensions/ --values ./k8s/extensions/values-dev.yaml
  02-17 20:22:18 │ helm template  extensions ./k8s/extensions/ --values ./k8s/extensions/values.dev.yaml
  02-17 18:15:27 │ helm template --debug extensions ./k8s/extensions/ --values ./k8s/extensions/values.dev.yaml
  02-17 17:59:42 │ helm template --dry-run --debug extensions ./k8s/extensions/ --values ./k8s/extensions/values-dev.yaml
▌ 02-17 17:59:29 │ helm template --dry-run --debug  ./k8s/extensions/ --values ./k8s/extensions/values-dev.yaml
  02-17 17:59:36 │ helm template --dry-run --debug foo ./k8s/extensions/ --values ./k8s/extensions/values-dev.yaml
╭──────────────────────────────────────────────────────────────────────────────────────────────╮
│ helm template --dry-run --debug ./k8s/extensions/ --values ./k8s/extensions/values-dev.yaml  │
╰──────────────────────────────────────────────────────────────────────────────────────────────╯

Wenn ich oft, wenn ich ein neues Terminal öffne in die gleichen Projekte navigiere, dann geht das prima über die Shell-History:

# ctrl-r für history suche
Search History> cdmkkapi
  352/32638 (0) ──────────────────────────
  08-12 11:56:19 │ cd mkk/api
  08-24 19:05:13 │ cd ../mkk/api
▌ 05-26 08:39:19 │ cd Code/Projekte/mkk/api
  07-29 17:02:48 │ cd Code/Projekte/mkk/api_infra/
  02-15 08:37:01 │ cd Code/Projekte/mkk/api_infra/monitoring/
╭──────────────────────────╮
│ cd Code/Projekte/mkk/api │
╰──────────────────────────╯

Mit Zoxide geht das noch besser, aber dazu später mehr.

So habe ich meine fzf Integration konfiguriert:

# configure key-bindings for fzf-fish
# ctrl-f directory search
# ctrl-r history search
# ctlr-v variables search
# ctrl-l git log search
# ctrl-s git status search
# ctrl-p processes pid search
fzf_configure_bindings --git_log=\f --directory=\cF --git_status=\cS --processes=\cP

Das geniale an fzf ist, dass es sich so wunderbar in andere Tools integrieren lässt. Hat man es installiert wird es z.B. von KubeCTX verwendet um in kubectx die liste der verbundenen Kubernetes Cluster zu filtern. Oder von kubens um die Liste der Namespaces. Tatsächlich verwenden viele Werkzeuge intern fzf wenn es instaliert ist. Für mich immer wieder eine schöne Überrachung, wenn ein weiteres Werkzeug das ich gerne einsetze fzf verwendet.

Die Freuden einer gut eingerichteten Shell: Autocomplete

written by Martin Häcker on

Was ist schlechte Vervollständigung?

Um zu verstehen was ich mit guter Auto-Completion für Shells meine, brauchen wir erst einmal eine Baseline wie eine schlechte Completion aussieht. Das lässt sich sehr gut mit Docker demonstrieren: 

docker run --rm -it --hostname shell-completion-demo debian

Erstes Experiment: ls⇥⇥ (kein Leerzeichen vor den Tabs!)

Automatische Vervollständigung von Kommandos - unkonfiguriert

Zeigt alle Kommandos die mit ls anfangen Zweites Experiment: ls ⇥⇥ Das zeigt bei mir:

Automatische Vervollständigung von Dateien - unkonfiguriert

Schon mal gut, denn hier werden die Dateien im aktuellen Ordner vervollständigt.

Nächste Schwierigkeitsstufe - kurze und lange Optionen: ls -⇥⇥ (Minus vor dem Tab) Das zeigt hier nichts, genauso für lange Optionen ls --⇥⇥ (zwei mal Minus vor dem Tab)

Keine Ausgabe. ls ist eigentlich so ungefähr das einfachste Programm das jeder Shell beiliegt. Wenn automatische Vervollständigung also irgend etwas kann, dann sollte ls gut funktionieren.

Was ist gute Vervollständigung?

Dagegen mal ein Beispiel von meinem System:

ls⇥ zeigt die Kommandos die mit ls anfangen, mit einer Kurzbeschreibung was diese Kommandos tun.

Vervollständigung von Kommandos mit der Fish-Shell

Schon mit einem Tab sehe ich die Dateien, und zusätzlich sehe ich als Vorschlag den letzten Befehl den ich mit ls abgesetzt habe und kann diesen mit ⌃→ im ganzen, oder mit wortweise akzeptieren kann.

Automatische Vervollständigung von Dateien mit der Fish Shell

Ein ls -⇥ergibt sofort eine Optionsliste - kurz und lang - mit einer Kurzbeschreibung was dieses Schalter tun. Ein zweites Minus und Tabls --⇥` zeigt nur noch die langen Optionen an:

Vervollständigung von Optionen mit der Fish-Shell Vervollständigung von langen Optionen mit der Fish-Shell

Natürlich kann ich mit den Pfeiltasten oder mit Tab eine der Optionen auswählen - natürlich mit ordentlichem Highlighting. So macht arbeiten auf der Shell Spaß!

Falls Ihr verwirrt seid das mein ls andere Optionen anbietet als eures, dann liegt das daran das ich ls durch exa ersetzt habe.

Wie könnt Ihr das bei euch nutzen?

Ich nutze die Fish-Shell, da diese von Haus aus eine sehr gute Autocompletion anbietet. Das ist aber nicht für jede, denn die Syntax der Fish Shell ist etwas anders als bei Bash/ZSH - eben nicht posix kompatibel. Ich mag das Weil es logischer und Kürzer ist, aber ich komme auch nicht durcheinander mit den verschiedenen Shell-Syntaxen da ich sie schon so lange verwende.

Fast alle Shell Konfigrurations-Frameworks wie oh-my-zsh oder Prezto bieten zumindest etwas an das diesem Nahe kommen. Alle automatische Konfiguration stößt aber irgendwann an Ihre Grenzen wenn es um die Kommandos geht, die wir täglich benutzen. docker vervollständigt dann nicht compose und oder kennt die Unterkommandos davon nicht oder nur unvollständig, kubectl und helm sind notorische Kandidaten für die man sich selber kümmern muss.

Jetzt könnte man natürlich versuchen automatisch aus der Hilfsausgabe dieser Kommandos etwas zu generieren (das macht z.B. die Fish shell von sich aus) oder man schreibt selber etwas (argh).

Oder man wendet sich vertrauensvoll an das tool carapace, mit dem man die Completion für Programme komfortabel für alle Shells nachrüsten kann. Als Beispiel um die die Autocompletions für kubectl nachzurüsten, einfach source <(carapace kubectl zsh) oder carapace kubectl fish | source (je nach shell) eingeben und ausprobieren ob es gefällt, und wenn ja, diese Zeile in die User-Konfiguration deiner shell eintragen und viel glücklicher sein.

Obacht: Man kann mit so einem Snippet

# ~/.zshrc 
export CARAPACE_BRIDGES='zsh,fish,bash,inshellisense' # optional
zstyle ':completion:*' format $'\e[2;37mCompleting %d\e[m'
source <(carapace _carapace)

in seiner Shell-Konfiguration alle completer des Carapace Projekts aktivieren. Das hat mir allerdings nicht gefallen,, da ich manche der eingebauten Completer der Fish-Shell noch etwas besser finde als das was Carapace bereit stellt. Aber um Lücken zu ergänzen? Perfekt!

Meine Shell-Completion Konfiguration (fish!) sieht daher so aus:

# enable shell completions
set --global --export CARAPACE_BRIDGES 'zsh,fish,bash,inshellisense'
# I didn't have much luck enabling all carapace completions, but I do like some of them - especially if there is no built in fish completion for them
# carapace _carapace | source
carapace fd | source
carapace bat | source
carapace brew | source
carapace rg | source
carapace docker | source
uv generate-shell-completion fish | source
yq completion fish | source