Positionierung
Linux ist seit Jahrzehnten die dominierende Plattform für Serverinfrastrukturen, Automatisierungsaufgaben und Datenverarbeitungsprozesse in Unternehmen jeder Größe. Meine Erfahrung mit Linux und Unix reicht von frühen AIX/KSH-Projekten in Grosskonzernumgebungen bis zu modernen Debian- und Ubuntu-Infrastrukturen, die ich heute für Eigenbetrieb, Kundenprojekte und hybride Szenarien betreibe und administriere. In dieser Zeit habe ich gelernt, dass solide Linux-Automatisierung nicht aus einzelnen Skripten besteht, sondern aus einem Gesamtsystem: Scheduler, robuste Fehlerbehandlung, Logging, Monitoring, Backup und Dokumentation.
Was mich in diesem Bereich von reinen Infrastrukturspezialisten unterscheidet, ist die Verbindung mit der Datenwelt. Ich bringe Linux-Automatisierungskenntnisse mit einem tiefen Verständnis von SQL Server, Data Warehouses, ETL-Prozessen und BI-Strecken zusammen. Lade- und Entladestrecken, die unter Linux laufen und Daten an SQL-Server-Datenbanken liefern; Connect:Direct-Dateitransfer-Jobs, die Shell-Skripte als Wrapper benutzen; Python-Skripte, die DWH-Daten extrahieren und transformieren – das ist das Umfeld, in dem ich mich seit Jahren bewege.
Hinzu kommt der Eigenbetrieb: Ich betreibe und warte eine eigene Proxmox-basierte Infrastruktur mit mehreren Standorten, die NGINX Reverse-Proxy, WireGuard-VPN, LXC-Container, Docker-Dienste, Authelia-SSO und automatisierte Backups umfasst. Diese Praxis ist kein Nebenprodukt – sie ist der Beweis, dass ich die beschriebenen Technologien tatsächlich produktiv einsetze und ihre Fallstricke aus eigener Erfahrung kenne.
Spektrum der Linux-Automatisierung
Linux-Automatisierung umfasst ein breites Spektrum an Technologien und Aufgaben. Auf der untersten Ebene stehen Shell-Skripte, die wiederkehrende Aufgaben automatisieren: Datei-Transfers, Log-Rotation, Reportgenerierung, Job-Steuerung. Auf der nächsten Ebene stehen Scheduling-Mechanismen wie cron und systemd Timer, die diese Skripte zeitgesteuert oder ereignisbasiert ausführen. Darüber liegt die Ebene der Prozessautomatisierung, auf der Python-Skripte, ETL-Trigger und Datenbankextraktionen laufen.
Shell-Scripting: Bash und KSH
Bash ist die Standard-Shell auf den meisten Linux-Distributionen; KSH (Korn Shell) ist das Pendant auf AIX und älteren Unix-Systemen. Ich beherrsche beide und kenne die Unterschiede: Array-Syntax, Arithmetic-Expansion, Prozess-Substitution und die Fallstricke bei der Portabilität. In Unternehmensumgebungen setze ich konsequent auf defensive Programmierung: set -euo pipefail als Basis, trap für Cleanup bei Fehlern, strukturiertes Logging mit Timestamps und klar definierte Exit-Codes für die Überwachung durch Scheduler oder Monitoring-Systeme.
Python als Ergänzung zu Shell
Python ergänzt Shell-Skripte dort, wo komplexere Datenverarbeitung, strukturierte Fehlerbehandlung oder externe Bibliotheken benötigt werden. Datei-Watcher mit watchdog, DB-Verbindungen über pyodbc, REST-API-Zugriffe, Datentransformationen mit pandas – all das lässt sich in Python deutlich sauberer und wartbarer abbilden als in Shell-Code. Ich kombiniere beide Welten: Shell für Systemintegration, Prozesssteuerung und einfache Transformationen; Python für Datenverarbeitung, komplexe Logik und externe Integrationen.
Virtualisierung und Container
Proxmox VE ist meine bevorzugte Virtualisierungsplattform für on-premise Infrastrukturen. LXC-Container bieten den Mittelweg zwischen VMs und Docker: weniger Overhead als vollständige VMs, mehr Isolation als reine Prozesse. Docker setze ich ein, wo Anwendungen bereits containerisiert vorliegen oder wo schnelle Portabilität gefragt ist. Die Kombination Proxmox-Host + LXC für Systemprozesse + Docker für Applikationen ist ein bewährtes Muster für mittelgrosse Infrastrukturen.
- Debian/Ubuntu, SUSE Linux, AIX – Distribution und Shell angepasst an Projektumgebung
- Bash/KSH-Scripting: set -euo pipefail, trap, Logging, Exit-Codes
- Python-Automatisierung: pyodbc, watchdog, pandas, paramiko, REST-APIs
- Cron und systemd Timer: Scheduling, Dependency-Management, Journaling
- Proxmox VE: LXC-Container, VM-Management, Cluster-Betrieb
- Docker: Compose-Stacks, Volume-Verwaltung, Update-Automatisierung
- NGINX: Reverse-Proxy, SSL-Terminierung, Rate-Limiting, Let's Encrypt
- WireGuard-VPN: Site-to-Site, Road-Warrior, Key-Management
- rsync/rclone: Backup, Synchronisierung, Offsite-Replikation
- Hardening: fail2ban, iptables/nftables, SSH-Härtung, Firewall
Robustes Bash-Scripting mit Error-Handling und Logging
Bash-Skripte werden in der Praxis häufig im Eiltempo geschrieben und dann jahrelang produktiv betrieben, ohne je überarbeitet zu werden. Das führt zu Skripten, die bei Fehlern still scheitern, kein Logging hinterlassen und deren Fehlerzustand niemand überwacht. Ich folge in jedem Produktionsskript einem festen Grundmuster, das robuste Fehlerbehandlung, strukturiertes Logging und klare Aufräumlogik kombiniert.
Die wichtigsten Bausteine: set -euo pipefail bricht das Skript sofort bei einem Fehler ab (statt still fortzumachen), ungesetzte Variablen werden als Fehler behandelt, und Pipe-Fehler werden sichtbar gemacht. trap ERR und trap EXIT sorgen dafür, dass Ressourcen freigegeben und Fehlerzustände protokolliert werden, egal auf welchem Weg das Skript endet. Timestamps im Logging ermöglichten die nachtträgliche Rekonstruktion von Abläufen.
Typische Linux-Automatisierungs-Pipeline: Ein Scheduler (cron oder systemd Timer) löst ein Shell- oder Python-Skript aus, das Daten verarbeitet und an ein Ziel überträgt (DWH oder Connect:Direct). Logging und Monitoring sind auf jeder Stufe verankert.
Skript-Template mit set -euo pipefail und trap
#!/usr/bin/env bash
# Produktionsskript fuer Lade-/Dateiverarbeitungsaufgaben
# Anforderungen: set -euo pipefail, Logging, Trap fuer Cleanup
set -euo pipefail
# ── Konfiguration ──────────────────────────────────────────────────────────
SCRIPT_NAME="$(basename "$0" .sh)"
LOG_DIR="/var/log/etl"
LOG_FILE="${LOG_DIR}/${SCRIPT_NAME}_$(date +%Y%m%d).log"
LOCK_FILE="/var/run/${SCRIPT_NAME}.lock"
SOURCE_DIR="/data/eingang"
TARGET_DIR="/data/verarbeitet"
ARCHIVE_DIR="/data/archiv"
MAX_ALTER_TAGE=30 # Archivdateien aelter als 30 Tage loeschen
# ── Logging-Funktion ───────────────────────────────────────────────────────
log() {
local level="$1"; shift
echo "$(date '+%Y-%m-%d %H:%M:%S') [${level}] $*" | tee -a "${LOG_FILE}"
}
# ── Fehler-Handler und Cleanup ─────────────────────────────────────────────
cleanup() {
local exit_code=$?
if [[ -f "${LOCK_FILE}" ]]; then
rm -f "${LOCK_FILE}"
log "INFO" "Lock-Datei entfernt"
fi
if [[ $exit_code -ne 0 ]]; then
log "ERROR" "Skript mit Exit-Code ${exit_code} beendet – Zeile: ${BASH_LINENO[0]}"
# Optional: Benachrichtigung per Mail oder Monitoring
# echo "ETL-Fehler in ${SCRIPT_NAME}" | mail -s "FEHLER: ${SCRIPT_NAME}" admin@example.com
else
log "INFO" "Skript erfolgreich beendet (Exit 0)"
fi
}
trap cleanup EXIT
trap 'log "ERROR" "Fehler in Zeile $LINENO (Befehl: $BASH_COMMAND)"; exit 1' ERR
# ── Voraussetzungen pruefen ────────────────────────────────────────────────
mkdir -p "${LOG_DIR}" "${TARGET_DIR}" "${ARCHIVE_DIR}"
# Laufende Instanz verhindern (flock-basiertes Lock)
if [[ -f "${LOCK_FILE}" ]]; then
log "WARN" "Skript laeuft bereits (Lock: ${LOCK_FILE}) – Abbruch"
exit 0
fi
echo $$ > "${LOCK_FILE}"
log "INFO" "=== ${SCRIPT_NAME} gestartet (PID $$) ==="
# ── Hauptlogik ─────────────────────────────────────────────────────────────
shopt -s nullglob # Kein Fehler wenn Glob keine Treffer liefert
dateien=("${SOURCE_DIR}"/*.csv)
if [[ ${#dateien[@]} -eq 0 ]]; then
log "INFO" "Keine Eingabedateien gefunden – nichts zu tun"
exit 0
fi
log "INFO" "${#dateien[@]} Datei(en) gefunden"
for datei in "${dateien[@]}"; do
dateiname="$(basename "${datei}")"
log "INFO" "Verarbeite: ${dateiname}"
# Zieldatei erzeugen (Transformation per sed/awk/python moeglich)
cp "${datei}" "${TARGET_DIR}/${dateiname}"
# Quelldatei archivieren
mv "${datei}" "${ARCHIVE_DIR}/${dateiname%.csv}_$(date +%Y%m%d%H%M%S).csv"
log "INFO" "Archiviert: ${dateiname}"
done
# Alte Archivdateien entfernen
find "${ARCHIVE_DIR}" -name "*.csv" -mtime "+${MAX_ALTER_TAGE}" -delete
log "INFO" "Archivbereinigung: Dateien aelter als ${MAX_ALTER_TAGE} Tage entfernt"
log "INFO" "=== Verarbeitung abgeschlossen ==="
Dieses Muster verwende ich als Basis für alle produktiven Bash-Skripte: set -euo pipefail, Logging mit Timestamps, flock-basiertes Lock gegen Doppelläufe und trap-basiertes Cleanup. Der Exit-Code wird von Monitoring und Scheduler ausgewertet.
Cron vs. systemd Timer: Wann was verwenden
Cron ist der klassische Linux-Scheduler und in jedem System verfügbar. Für einfache, zeitgesteuerte Aufgaben ist Cron ausreichend. systemd Timer bieten mehr: Abhängigkeiten zwischen Units, automatisches Logging via journald, präzise Kalender-Ausdrücke, monotone Timer (relativ zum letzten Start) und die Möglichkeit, den Timer-Status mit systemctl status zu prüfen. In modernen Debian- und Ubuntu-Umgebungen bevorzuge ich systemd Timer für neue Aufgaben, weil sie besser in das Betriebssystem integriert sind.
systemd: Services, Timer und Dependency-Management
systemd hat den Betrieb von Linux-Diensten grundlegend verändert. Wo frühher init-Skripte und Cron-Einträge notwendig waren, bietet systemd eine einheitliche, deklarative Schnittstelle: Service-Units definieren, wie ein Prozess gestartet, überwacht und bei Absturz neugestartet wird. Timer-Units ersetzen Cron für zeitgesteuerte Aufgaben, mit besserer Journalisierung und flexibleren Zeitausdrücken. Und das Dependency-Management von systemd stellt sicher, dass ein Dienst erst startet, wenn seine Voraussetzungen erfuellt sind.
In meiner eigenen Infrastruktur und in Kundenprojekten verwalte ich Dutzende von systemd-Units: Datenbankdienste, ETL-Trigger, Backup-Jobs, Monitoring-Agenten und Reverse-Proxy-Konfigurationen. Das Zusammenspiel zwischen Service-Units, Timer-Units und Target-Units ermöglicht präzise Abhängigkeitsketten, die sicherstellen, dass Prozesse in der richtigen Reihenfolge starten und bei Fehler sauber gestoppt werden.
Typische Proxmox-Multi-Site-Infrastruktur: Standort A betreibt NGINX, Datenbank und Docker-Dienste als LXC-Container. WireGuard-VPN verbindet Standort A mit Standort B, der Backup-Ziel und Monitoring (Grafana) beheimatet. DNS und Authelia-SSO ermöglichen zentrales Zugriffs-Management.
# /etc/systemd/system/etl-ladevorgang.service
# Beschreibung: Startet den ETL-Ladeprozess fuer Tagesabschluss
# Voraussetzungen: Netzwerk muss verfuegbar sein, PostgreSQL-Dienst muss laufen
[Unit]
Description=ETL Tagesabschluss Ladevorgang
Documentation=https://wiki.intern/etl-prozesse
After=network-online.target postgresql.service
Requires=network-online.target
Wants=postgresql.service
[Service]
Type=oneshot
# Ausfuehrender Benutzer (kein Root benoetigt)
User=etl-user
Group=etl-group
# Umgebungsvariablen aus sicherer Datei laden
EnvironmentFile=/etc/etl/etl-ladevorgang.env
# Hauptprozess
ExecStart=/opt/etl/bin/etl_ladevorgang.sh
# Ressourcenlimits: CPU-Quota und Speicher begrenzen
CPUQuota=50%
MemoryMax=512M
# Standardausgabe in Journal schreiben
StandardOutput=journal
StandardError=journal
SyslogIdentifier=etl-ladevorgang
# Neustart bei unerwarteter Beendigung (nicht bei Exit 0)
Restart=on-failure
RestartSec=60s
[Install]
WantedBy=multi-user.target
# ─────────────────────────────────────────────────────────────────────────────
# /etc/systemd/system/etl-ladevorgang.timer
# Zeitgesteuerte Ausfuehrung: werktaeglich um 22:00 Uhr
[Unit]
Description=Timer fuer ETL Tagesabschluss Ladevorgang
Requires=etl-ladevorgang.service
[Timer]
# Kalenderdefinition: Mo-Fr um 22:00 Uhr
OnCalendar=Mon-Fri 22:00:00
# Wenn Ausfuehrung verpasst wurde (System war aus), sofort nachholen
Persistent=true
# Zufaellige Verschiebung bis 5 Minuten (Last-Verteilung bei vielen Timern)
RandomizedDelaySec=5min
[Install]
WantedBy=timers.target
# ─────────────────────────────────────────────────────────────────────────────
# Aktivierung und Statusabfrage:
# systemctl daemon-reload
# systemctl enable --now etl-ladevorgang.timer
# systemctl status etl-ladevorgang.timer
# journalctl -u etl-ladevorgang.service -f # Live-Log verfolgen
Service-Unit und Timer-Unit arbeiten als Paar: Der Timer löst den Service aus. Der Service läuft als oneshot-Prozess, schreibt Logging via journald und meldet Fehler über den Exit-Code an den Timer. systemctl list-timers zeigt nächste Ausführungszeit und vergangene Läufe.
Dependency-Management in Komplexen Ketten
Wenn mehrere ETL-Schritte in einer bestimmten Reihenfolge ablaufen müssen, lässt sich das mit systemd elegant lösen: Jeder Schritt ist eine eigene Service-Unit, die nach dem vorherigen Schritt gestartet wird (After=). Mit OnSuccess= und OnFailure= in systemd 246+ können Nachfolge-Units bedingt gestartet werden – zum Beispiel eine Benachrichtigungs-Unit bei Fehler oder eine Archivierungs-Unit bei Erfolg. Diese deklarativen Abhängigkeiten sind wesentlich übersichtlicher als verschachtelte if-Ketten in Shell-Skripten.
- Type=oneshot für Batch-Skripte, Type=simple/notify für langlebige Dienste
- EnvironmentFile für sichere Übergabe von Zugangsdaten (nicht in Unit-Datei)
- Restart=on-failure mit RestartSec für Automatische Neustartstrategie
- CPUQuota und MemoryMax verhindern Ressourcenraub durch ETL-Jobs
- Persistent=true im Timer: verspätete Ausführung wird nachgeholt
- journalctl -u
-f für Echtzeit-Log; --since für historische Analyse
Python-Automatisierung unter Linux
Python ist die ideale Ergänzung zu Bash für Automatisierungsaufgaben, die über einfache Systemkommandos hinausgehen. Datei-Watcher, die auf neue Eingabedateien reagieren; ETL-Trigger, die Datenbankabfragen ausführen und Ergebnisse als CSV oder Parquet ablegen; REST-API-Aufrufe, die Daten aus externen Systemen holen und in lokale Strukturen übersetzen – all das ist in Python präziser, testbarer und wartbarer abgebildet als in reinem Shell-Code.
In meinen Projekten habe ich Python-Automatisierung eingesetzt, um Datenbankextraktionen aus SQL Server unter Linux zu realisieren (sqlcmd/bcp oder pyodbc mit ODBC-Treiber), um Batch-Verarbeitungen zu steuern und zu protokollieren und um Benachrichtigungen bei Fehlern oder abgeschlossenen Ladevorgängen auszulösen. Die Integration von Python-Skripten in systemd-Units ermöglicht zuverlässiges Scheduling mit vollem Journaling.
#!/usr/bin/env python3
# Datei-Watcher: Neue CSV-Dateien im Eingangsordner ausloesen ETL-Prozess
# Abhaengigkeiten: watchdog, pyodbc, pandas, logging (alle Standard/pip)
import sys
import time
import logging
import pathlib
import shutil
import pandas as pd
import pyodbc
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
# ── Konfiguration ──────────────────────────────────────────────────────────
EINGANG_DIR = pathlib.Path("/data/eingang")
VERARBEITET = pathlib.Path("/data/verarbeitet")
FEHLER_DIR = pathlib.Path("/data/fehler")
LOG_FILE = pathlib.Path("/var/log/etl/datei_watcher.log")
# ODBC-Verbindungsstring (Treiber: ODBC Driver 18 for SQL Server)
CONN_STR = (
"DRIVER={ODBC Driver 18 for SQL Server};"
"SERVER=db-server.intern;"
"DATABASE=DWH_Staging;"
"Trusted_Connection=no;"
"UID=etl_user;PWD=__aus_env__;" # Passwort aus Umgebungsvariable
)
# ── Logging einrichten ─────────────────────────────────────────────────────
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(message)s",
handlers=[
logging.FileHandler(LOG_FILE),
logging.StreamHandler(sys.stdout),
],
)
log = logging.getLogger(__name__)
def verarbeite_csv(pfad: pathlib.Path) -> None:
# Laedt eine CSV-Datei, transformiert und schreibt in SQL Server Staging.
log.info("Verarbeite Datei: %s", pfad.name)
try:
df = pd.read_csv(pfad, sep=";", encoding="utf-8", dtype=str)
# Bereinigung: fuehrende/nachfolgende Leerzeichen in allen Spalten
df = df.applymap(lambda x: x.strip() if isinstance(x, str) else x)
df["ladezeit"] = pd.Timestamp.now()
# In SQL Server Staging-Tabelle schreiben
conn = pyodbc.connect(CONN_STR, timeout=30)
cursor = conn.cursor()
cursor.fast_executemany = True
cols = ", ".join(df.columns)
platzhalter = ", ".join(["?"] * len(df.columns))
sql = f"INSERT INTO staging.csv_eingang ({cols}) VALUES ({platzhalter})"
cursor.executemany(sql, df.itertuples(index=False, name=None))
conn.commit()
conn.close()
log.info("Erfolgreich geladen: %d Zeilen aus %s", len(df), pfad.name)
# Erfolgreich verarbeitete Datei verschieben
shutil.move(str(pfad), str(VERARBEITET / pfad.name))
except Exception as fehler:
log.error("Fehler bei %s: %s", pfad.name, fehler, exc_info=True)
# Fehlerhafte Datei zur manuellen Pruefung verschieben
shutil.move(str(pfad), str(FEHLER_DIR / pfad.name))
raise # Damit systemd den Fehler-Exit-Code erfaesst
class EingangHandler(FileSystemEventHandler):
# Reagiert auf neue Dateien im Eingangsordner.
def on_created(self, event):
if event.is_directory:
return
pfad = pathlib.Path(event.src_path)
if pfad.suffix.lower() == ".csv":
# Kurze Pause: Sicherstellen, dass Datei vollstaendig geschrieben
time.sleep(0.5)
verarbeite_csv(pfad)
if __name__ == "__main__":
for d in (EINGANG_DIR, VERARBEITET, FEHLER_DIR, LOG_FILE.parent):
d.mkdir(parents=True, exist_ok=True)
log.info("Datei-Watcher gestartet: %s", EINGANG_DIR)
observer = Observer()
observer.schedule(EingangHandler(), str(EINGANG_DIR), recursive=False)
observer.start()
try:
while True:
time.sleep(5)
except KeyboardInterrupt:
observer.stop()
observer.join()
log.info("Watcher beendet")
Dieser Datei-Watcher lässt sich als systemd-Service betreiben (Type=simple, Restart=on-failure). Er reagiert auf neue CSV-Dateien, lädt sie in SQL Server Staging und verschiebt Dateien in Verarbeitet- oder Fehler-Ordner. Pyodbc mit dem Microsoft ODBC Driver 18 läuft auf Debian/Ubuntu ohne Probleme.
Python und Shell als Team
Die sauberste Architektur kombiniert Shell-Skripte für Systemintegration und Python für Datenlogik. Ein Shell-Wrapper startet das Python-Skript, prüft den Exit-Code, schreibt eine Zusammenfassung ins Systemlog und benachrichtigt bei Fehler. Das Python-Skript konzentriert sich auf die Datenverarbeitung. Diese Trennung macht beide Teile einzeln testbar und unabhängig wartbar.
Proxmox VE und LXC-Virtualisierung
Proxmox Virtual Environment ist eine Open-Source-Virtualisierungsplattform, die KVM-Virtualisierung und LXC-Container auf einer gemeinsamen Managementoberfläche vereint. Ich betreibe Proxmox produktiv in meiner eigenen Infrastruktur und bringe diese Erfahrung in Kundenprojekte mit, die On-Premise-Virtualisierung ohne teure Vendor-Lock-in-Lösungen benötigen.
LXC-Container sind das bevorzugte Deployment-Format für Serverprozesse auf Proxmox: Sie starten in Sekunden, verbrauchen deutlich weniger Ressourcen als vollständige VMs und sind durch Proxmox-Snapshots einfach zu sichern und wiederherzustellen. Für jeden Dienst ein eigener Container – NGINX, Datenbank, Monitoring, Backup-Agent – ermöglicht saubere Isolation und einfache Wartung.
Cluster und Hochverfügbarkeit
Proxmox unterstützt Clustering mit bis zu mehreren Dutzend Knoten und bietet integrierte Hochverfügbarkeit für VMs und Container. In einer Zwei-Knoten-Konfiguration mit einem externen Quorum-Device lässt sich einfache HA ohne grosse Komplexität realisieren. Für kritische Dienste bedeutet das automatisches Failover bei Knotenausfall innerhalb von Sekunden bis Minuten.
Backup und Snapshotting
Proxmox Backup Server (PBS) ist das natürliche Pendant zu Proxmox VE: inkrementelle, deduplizierte Backups von VMs und Containern mit Prüfprotokolle-Verifikation. Konfigurierbare Backup-Jobs laufen nachtens, PBS prüft Intergrität der Backup-Daten periodisch. Zusätzlich setze ich Guest-Level-Backups mit restic oder rclone ein, um Anwendungsdaten selektiv zu sichern.
- Proxmox VE als On-Premise-Hypervisor: KVM und LXC auf einer Plattform
- LXC-Container: schnelle Provisionierung, ressourceneffizient, Snapshot-fähig
- Proxmox Cluster: HA, Live-Migration, zentrales Management mehrerer Knoten
- Proxmox Backup Server: inkrementelle, deduplizierte Backups mit Prüfung
- Netzwerk-Konfiguration: Linux Bridges, VLANs, Open vSwitch für komplexe Topologien
- Storage: ZFS für Produktionsdaten mit Snapshots und Checksummen
- Automatisierung: Proxmox API und pvesh für skriptgesteuerte VM/LXC-Verwaltung
Docker und Container-Dienste
Docker ist in meiner Infrastruktur der Standard für Anwendungsdienste, die als Container vorliegen oder für die fertige Images verfügbar sind. Auf Proxmox laufen Docker-Dienste typischerweise innerhalb eines dedizierten LXC-Containers (nested containers), was die Isolation von Systemdiensten und Anwendungsdiensten ermögliche. Docker Compose verwaltet Stacks aus mehreren Diensten und deren Abhängigkeiten deklarativ.
Automatisierte Updates sind ein wichtiger Aspekt des Docker-Betriebs: Watchtower oder ein eigenes Update-Skript prüfen periodisch auf neue Image-Versionen und aktualisieren Container nach einer definierten Strategie. Volumes werden ausserhalb des Containers gespeichert und in Backup-Routinen einbezogen. Netzwerk-Policies stellen sicher, dass Container nur auf die Dienste zugreifen können, die sie benötigen.
- Docker Compose: Deklarative Stack-Definition, Abhängigkeitsverwaltung
- Volume-Verwaltung: benannte Volumes oder Bind-Mounts in sicher gesicherte Pfade
- Netzwerk-Isolation: eigene Bridge-Netzwerke pro Stack, kein unnötiger Zugriff
- Automatisierte Updates: Watchtower oder skriptbasierte Update-Routinen
- Health-Checks: restart: unless-stopped, healthcheck-Direktive im Compose-File
- NGINX als Reverse-Proxy vor Docker-Containern: SSL-Terminierung, Rate-Limiting
NGINX Reverse-Proxy, WireGuard-VPN und Netzwerkkonfiguration
NGINX ist in meiner Infrastruktur der zentrale Eintrittspunkt für alle webbasierten Dienste. Als Reverse-Proxy terminiert NGINX TLS-Verbindungen (Let's Encrypt via Certbot oder acme.sh), leitet Anfragen an interne Container weiter und implementiert Rate-Limiting, Authentifizierung (via Authelia) und Access-Logging. Diese Zentralisierung vereinfacht Zertifikatsverwaltung und Sicherheits-Policies erheblich.
WireGuard ist mein VPN der Wahl für Site-to-Site-Verbindungen und Road-Warrior-Szenarien. Im Vergleich zu OpenVPN bietet WireGuard deutlich einfachere Konfiguration, hohere Performance und einen kleineren Code-Footprint. In meiner Infrastruktur verbindet WireGuard mehrere Proxmox-Standorte und ermöglicht sicheren Fernzugriff auf interne Dienste ohne öffentlich exponierte Ports.
# /etc/nginx/sites-available/app-intern
# Reverse-Proxy fuer interne Webanwendung mit SSL und Authentifizierung
# Rate-Limiting-Zone definieren (20 Anfragen/Sekunde pro IP)
limit_req_zone $binary_remote_addr zone=app_limit:10m rate=20r/s;
server {
listen 80;
server_name app.example.de;
# Alle HTTP-Anfragen auf HTTPS umleiten
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name app.example.de;
# SSL-Zertifikate (Let's Encrypt via certbot)
ssl_certificate /etc/letsencrypt/live/app.example.de/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app.example.de/privkey.pem;
# Moderne SSL-Konfiguration: nur TLS 1.2 und 1.3
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;
# Sicherheits-Header
add_header Strict-Transport-Security "max-age=63072000" always;
add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options SAMEORIGIN;
# Rate-Limiting anwenden
limit_req zone=app_limit burst=50 nodelay;
# Authelia-Authentifizierungs-Endpunkt
location /authelia {
internal;
proxy_pass http://127.0.0.1:9091/api/verify;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
}
location / {
# Authentifizierung ueber Authelia pruefen
auth_request /authelia;
auth_request_set $user $upstream_http_remote_user;
# Anfrage an internen Dienst weiterleiten (Docker/LXC)
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
}
# Access-Log mit Zeitstempel und Response-Code
access_log /var/log/nginx/app.example.de_access.log combined;
error_log /var/log/nginx/app.example.de_error.log warn;
}
Diese NGINX-Konfiguration kombiniert SSL-Terminierung, modernes TLS, Rate-Limiting und Authelia-basierte SSO-Authentifizierung. Alle internen Dienste sind hinter diesem Reverse-Proxy versteckt; nach aussen ist nur Port 443 öffentlich.
WireGuard: Site-to-Site und Road-Warrior
WireGuard-Konfigurationen sind minimal und lesbar. Ein Interface hat einen privaten Schlüssel und eine IP-Adresse im VPN-Netz; jeder Peer bekommt seinen öffentlichen Schlüssel und die erlaubten Ziel-IP-Ranges. In einer Site-to-Site-Konfiguration werden ganze Subnets geroutet; für Road-Warrior-Clients wird 0.0.0.0/0 als erlaubtes Netz konfiguriert, sodass der gesamte Traffic durch den VPN-Tunnel läuft. DNS-Konfiguration im WireGuard-Interface stellt sicher, dass interne Hostnamen korrekt aufgelöst werden.
Backup-Automatisierung mit rsync und rclone
Backup-Automatisierung ist einer der wichtigsten, aber häufig vernachlässigten Aspekte des Linux-Betriebs. Viele Systeme haben Backup-Skripte, die zwar laufen, aber nie auf ihre Integrität getestet wurden oder deren Rotationsstrategie zu einer wachsenden Datenmenge ohne ausreichende Aufbewahrungsfrist führt. Ich implementiere Backup-Systeme, die zuverlässig laufen, ihren Status melden und periodisch getestet werden.
rsync ist das Werkzeug der Wahl für lokale und SSH-basierte Synchronisierung: inkrementell, effizient, weitgehend universell verfügbar. rclone ergänzt rsync für Cloud-Ziele: S3-kompatible Storage, Azure Blob, Backblaze B2, SFTP und Dutzende weitere Backends werden mit derselben CLI-Schnittstelle angesprochen. Die Kombination aus lokalem rsync-Backup und Cloud-Offsite-Replikation via rclone realisiert eine 3-2-1-Backup-Strategie ohne proprietäre Lösungen.
Der Backup-Flow umfasst drei Stufen: rsync sichert Daten vom Quell-Server auf ein lokales Backup-Ziel; rclone repliziert selektiv in die Cloud oder einen Offsite-Standort; ein Monitoring-Prozess überwacht Exit-Codes und sendet Alerts bei Fehlern oder ausgebliebenem Backup.
#!/usr/bin/env bash
# Backup-Skript: rsync lokal + rclone Offsite-Replikation
# Rotationsstrategie: 7 taeglich, 4 woechentlich, 12 monatlich
set -euo pipefail
# ── Konfiguration ──────────────────────────────────────────────────────────
QUELLE="/srv/data" # Quell-Verzeichnis
BACKUP_BASIS="/backup" # Lokales Backup-Basisverzeichnis
RCLONE_ZIEL="b2:mein-backup" # rclone-Ziel (Backblaze B2 oder S3)
MONITORING_URL="https://hc-ping.com/XXXX" # Healthcheck-URL (optional)
LOG="/var/log/backup/backup.log"
DATUM=$(date +%Y%m%d)
WOCHENTAG=$(date +%u) # 1=Montag, 7=Sonntag
TAG_IM_MONAT=$(date +%d) # 01-31
mkdir -p "${BACKUP_BASIS}"/{taeglich,woechentlich,monatlich} "$(dirname "${LOG}")"
log() { echo "$(date '+%Y-%m-%d %H:%M:%S') $*" | tee -a "${LOG}"; }
# ── Healthcheck: Backup-Start melden ──────────────────────────────────────
[[ -n "${MONITORING_URL:-}" ]] && curl -fsS "${MONITORING_URL}/start" -o /dev/null || true
log "INFO Backup gestartet: ${DATUM}"
# ── Taeglich: rsync mit Hardlinks fuer platzsparende History ─────────────
TAGESZIEL="${BACKUP_BASIS}/taeglich/${DATUM}"
LETZTES=$(ls -1d "${BACKUP_BASIS}/taeglich"/20* 2>/dev/null | tail -1 || echo "")
if [[ -n "${LETZTES}" && "${LETZTES}" != "${TAGESZIEL}" ]]; then
# Inkrementelles Backup: unveraenderte Dateien per Hardlink, keine Kopie
rsync -avz --delete \
--link-dest="${LETZTES}" \
--exclude-from="/etc/backup/exclude.list" \
"${QUELLE}/" "${TAGESZIEL}/"
else
# Erstes Backup oder gleicher Tag: vollstaendige Kopie
rsync -avz --delete \
--exclude-from="/etc/backup/exclude.list" \
"${QUELLE}/" "${TAGESZIEL}/"
fi
log "INFO Tagesbackup abgeschlossen: ${TAGESZIEL}"
# ── Woechentlich: Kopie des Tagesbackups (jeden Sonntag) ──────────────────
if [[ "${WOCHENTAG}" == "7" ]]; then
KW=$(date +%Y_KW%V)
cp -al "${TAGESZIEL}" "${BACKUP_BASIS}/woechentlich/${KW}"
log "INFO Wochenbackup erstellt: ${KW}"
fi
# ── Monatlich: Kopie des Tagesbackups (1. jedes Monats) ───────────────────
if [[ "${TAG_IM_MONAT}" == "01" ]]; then
MONAT=$(date +%Y_%m)
cp -al "${TAGESZIEL}" "${BACKUP_BASIS}/monatlich/${MONAT}"
log "INFO Monatsbackup erstellt: ${MONAT}"
fi
# ── Rotation: Alte Backups loeschen ───────────────────────────────────────
find "${BACKUP_BASIS}/taeglich" -maxdepth 1 -type d -mtime +7 -exec rm -rf {} +
find "${BACKUP_BASIS}/woechentlich"-maxdepth 1 -type d -mtime +28 -exec rm -rf {} + 2>/dev/null || true
find "${BACKUP_BASIS}/monatlich" -maxdepth 1 -type d -mtime +365 -exec rm -rf {} + 2>/dev/null || true
log "INFO Rotation abgeschlossen"
# ── Offsite-Replikation via rclone ────────────────────────────────────────
rclone sync "${BACKUP_BASIS}/monatlich" "${RCLONE_ZIEL}/monatlich" \
--progress --transfers=4 --checkers=8 \
--log-file="${LOG}" --log-level=INFO
log "INFO Offsite-Replikation abgeschlossen"
# ── Healthcheck: Backup-Erfolg melden ─────────────────────────────────────
[[ -n "${MONITORING_URL:-}" ]] && curl -fsS "${MONITORING_URL}" -o /dev/null || true
log "INFO Backup vollstaendig erfolgreich"
Dieses Skript implementiert eine vollständige 3-2-1-Backup-Strategie: Täglich-Backups mit Hardlinks für Platzeffizienz, automatische Wochenbackups am Sonntag, Monatsbackups am Monatsersten und Offsite-Replikation via rclone. Healthcheck-Ping bestätigt den Erfolg oder löst bei Ausbleiben einen Alert aus.
Konfigurationssicherung
Neben Datensicherung ist die Konfigurationssicherung essenziell: /etc, Crontabs, systemd-Units, NGINX-Konfigurationen und Anwendungs-Konfigurationsdateien sollten regelmäßig gesichert und versioniert werden. Git eignet sich hervorragend für die Versionierung von Konfigurationsdateien; etckeeper automatisiert dies für /etc. In Verbindung mit rsync/rclone wird die komplette Systemkonfiguration als eigenständiges Backup-Artefakt behandelt.
Hardening und Sicherheit
Ein Linux-System, das produktiv im Einsatz ist, muss gehärtet werden. Hardening bedeutet: die Angriffsfläcke minimieren, Brute-Force-Versuche abwehren, Netzwerkzugriffe auf notwendige Ports beschränken und den Zugriff auf das System protokollieren. Die wichtigsten Massnahmen sind weit bekannt und dennoch in der Praxis häufig nicht konsequent umgesetzt.
SSH-Härtung
SSH ist der Hauptzugriffsweg zu Linux-Servern und damit das häufigste Angriffsziel. Grundlegende Massnahmen: Passwort-Authentifizierung deaktivieren und ausschliesslich Public-Key-Authentifizierung zulassen; Root-Login per SSH verbieten; SSH auf einem nicht-standardmäßigen Port betreiben (erschwert automatisierte Scans); AllowUsers oder AllowGroups nutzen, um nur berechtigte Konten zuzulassen. fail2ban überwacht SSH-Login-Versuche und sperrt IPs nach wiederholten Fehlversuchen.
Firewall mit iptables/nftables
Jeder Server, der öffentlich erreichbar ist, benötigt eine Firewall-Konfiguration, die nur die tatsächlich benötigen Ports zulässt. iptables ist der klassische Ansatz; nftables ist der modernere Nachfolger mit einheitlicherer Syntax. Für einfache Konfigurationen setze ich ufw (Uncomplicated Firewall) auf Ubuntu ein; für komplexere Topologien mit Source-Based-Routing oder Port-Forwarding direkt nftables. Die Grundregel: Alles sperren, nur benotiges erlauben.
Automatische Sicherheitsupdates
Ungepatchte Systeme sind das größte Sicherheitsrisiko. unattended-upgrades auf Debian/Ubuntu aktualisiert Sicherheitspatches automatisch und meldet Ergebnisse per Mail. Für Kernel-Updates ohne Reboot ermöglicht kpatch oder livepatch (bei Ubuntu) Live-Patching. Zumindest die automatische Installation von Sicherheits-Updates sollte auf jedem produktiven Server aktiviert sein.
- SSH: Nur Public-Key, kein Root-Login, AllowUsers, fail2ban aktiv
- Firewall: nftables/iptables mit Default-Deny, nur notwendige Ports offen
- unattended-upgrades: automatische Sicherheitspatches ohne manuellen Eingriff
- Dienste minimieren: nur notwendige Dienste aktiv (systemctl list-units --type=service)
- Datei-Berechtigungen: keine World-Writable-Dateien ausserhalb /tmp
- fail2ban: SSH, NGINX, weitere Dienste je nach Exponierung schutzen
- Audit-Logging: auditd für sicherheitsrelevante Systemereignisse
Brücke Linux zu SQL Server und Data Warehouse
Ein wesentlicher Teil meiner Projekterfahrung liegt genau an dieser Schnittstelle: Linux-basierte Verarbeitungsprozesse, die Daten an SQL-Server-Datenbanken liefern oder aus DWH-Systemen extrahieren. Microsoft stellt sqlcmd und bcp seit Jahren für Linux zur Verfügung; der ODBC Driver 17/18 für SQL Server läuft stabil auf Debian, Ubuntu und SUSE. Diese Verfügbarkeit ermöglicht es, ETL-Pipelines vollständig unter Linux zu betreiben, ohne Windows-Server als Zwischenschicht zu benötigen.
In Logistik- und Versicherungsprojekten habe ich Shell-basierte Lade- und Entladestrecken entwickelt, die unter UNIX/AIX/KSH laufen und Daten per Connect:Direct oder FTP an zentrale DWH-Systeme übertragen. Perl-Wrapper steuerten Teradata FastLoad-Jobs aus Shell-Skripten heraus. Diese Erfahrung mit heterogenen Umgebungen macht mich zu einem Ansprechpartner für alle Cross-Plattform-Szenarien.
sqlcmd und bcp unter Linux
sqlcmd ermöglicht interaktive und skriptbasierte T-SQL-Ausführung auf SQL Server direkt aus der Linux-Shell. bcp (Bulk Copy Program) exportiert und importiert grosse Datenmengen effizient. Beide Werkzeuge sind in Bash-Skripte integrierbar, benötigen keine Windows-Umgebung und funktionieren mit Windows-Authentifizierung via Kerberos oder mit SQL-Server-Authentifizierung. In Kombination mit dem ODBC-Treiber und pyodbc lässt sich ein kompletter ETL-Stack vollständig unter Linux aufbauen.
Connect:Direct Dateitransfer-Steuerung
Connect:Direct (Sterling File Gateway/IBM MQ File Transfer) ist in Grossunternehmensumgebungen, besonders in Versicherung und Logistik, die Standardlösung für zuverlässigen Dateitransfer zwischen Plattformen. Die Steuerung von Connect:Direct-Jobs aus Shell-Skripten heraus – Einreichung von Process-Files, Überwachung des Transferstatus, Fehlerbehandlung und Logging – ist ein Kernbestandteil solcher Automatisierungsumgebungen, den ich aus mehreren Projekten kenne.
- sqlcmd/bcp unter Linux: T-SQL-Ausführung und Bulk-Import/-Export
- ODBC Driver 17/18 für SQL Server auf Debian/Ubuntu/SUSE
- pyodbc: Python-basierte DB-Verbindung zu SQL Server unter Linux
- Connect:Direct: Process-File-Einreichung und Transfer-Monitoring per Shell
- Cross-Plattform-ETL: AIX/KSH -> SQL Server / Teradata als Praxiserfahrung
- Perl-Wrapper für Legacy-Jobs (Teradata FastLoad, Informatica-Aufrufe)
Vorgehen und Betriebsdokumentation
Der Einstieg in ein Linux-Automatisierungsprojekt beginnt immer mit Bestandsaufnahme: Welche Skripte laufen bereits? Wo werden sie geplant (cron, systemd, manuell)? Welche Fehlerbehandlung existiert? Gibt es Logging? Wer überwacht die Prozesse? Diese Bestandsaufnahme zeigt schnell, ob ein System betriebssicher oder ein undokumentierter 'shared-nothing'-Zustand ist.
Betriebsdokumentation ist für mich kein nachgelagerter Schritt, sondern Teil des Lieferumfangs. Jeder Automatisierungsprozess bekommt eine Runbook-Seite mit: Zweck, Ausführungsfrequenz, Abhängigkeiten, Fehlerbehebungsanleitung und Kontaktperson. Diese Dokumentation ist in Markdown geschrieben, versioniert im Git-Repository und idealerweise als statische Site für alle Beteiligten zugänglich.
Konfigurationsmanagement
Für Infrastruktur, die aus mehr als einem Server besteht, lohnt sich ein Konfigurationsmanagement-System. Ansible ist meine erste Wahl: agentenloses, YAML-basiertes Playbook-System, das Konfigurationszustände deklarativ beschreibt und idempotent anwendet. Playbooks für NGINX-Konfiguration, systemd-Units, fail2ban-Regeln und User-Management läufen bei jeder Infrastruktureänderung und stellen sicher, dass alle Server denselben gewünschten Zustand haben.
- Bestandsaufnahme: Skripte, Scheduler, Logging, Monitoring, Fehlerbehandlung
- Priorisierung: Fehlende Fehlerbehandlung und fehlendes Monitoring zürst
- Umsetzung: Schrittweise, mit Tests in nicht-produktiver Umgebung
- Dokumentation: Runbooks in Markdown, versioniert, zugänglich
- Konfigurationsmanagement: Ansible für reproduzierbare Serverconfig
- Übergabe: Team-Training und Wissenstransfer als Teil des Projekts
In meinem Eigenbetrieb halte ich eine zentrale Betriebsdokumentation, die alle laufenden Dienste, ihre Konfigurationen, ihre Abhängigkeiten und ihre Backup-Situationen beschreibt. Diese Dokumentation ist kein statisches Dokument, sondern wird bei jeder Änderung aktualisiert. Diesen Ansatz bringe ich auch in Kundenprojekte mit.
Typische Leistungen im Bereich Linux-Automatisierung
Das Spektrum meiner Linux-Automatisierungsleistungen reicht von der kurzfristigen Unterstützung bei einem konkreten Skriptproblem bis zur vollständigen Konzeption und Implementierung einer Automatisierungsinfrastruktur. Je nach Projektphase und Bedarf übernehme ich einzelne Bereiche oder das vollständige Spektrum.
- Bash/KSH-Skript-Entwicklung und -Härtung (set -euo pipefail, Logging, Trap)
- systemd-Unit-Entwicklung: Services, Timer, Dependency-Ketten
- Python-Automatisierung: Datei-Watcher, ETL-Trigger, DB-Extraktion
- Proxmox VE: Setup, LXC-Container-Management, Backup-Strategie
- Docker: Compose-Stacks, Update-Automatisierung, Volume-Backup
- NGINX Reverse-Proxy: SSL, Rate-Limiting, Authelia-SSO-Integration
- WireGuard-VPN: Site-to-Site, Road-Warrior, Key-Management
- DNS-Dienste: Pi-hole, AdGuard Home, Unbound als Resolver
- Backup-Automatisierung: rsync, rclone, 3-2-1-Strategie, Restore-Tests
- Hardening: SSH, fail2ban, nftables/iptables, unattended-upgrades
- Cross-Plattform-ETL: sqlcmd/bcp unter Linux, pyodbc, Connect:Direct
- Monitoring: Prometheus/Grafana, Alertmanager, Exit-Code-Überwachung
- Betriebsdokumentation: Runbooks, Markdown, Git-Versionierung, Ansible
Diese Leistungsbreite ermöglicht es mir, Projekte ohne Schnittstellenverluste zu begleiten: Von der Infrastrukturplanung über die Automatisierungsentwicklung bis zur Betriebsdokumentation arbeite ich als Einheit und spare dem Auftraggeber den Koordinationsaufwand zwischen verschiedenen Spezialisten.
Besonders wertvoll ist diese Breite in hybriden Umgebungen: Linux-Infrastruktur, die mit SQL-Server- oder Azure-Systemen interagiert, braucht jemanden, der auf allen Ebenen zuhause ist – von der Shell bis zur Datenbank, vom Container bis zur Cloud.
Ausgewählte anonymisierte Referenzprojekte
Versicherung / Rückversicherung
Entwicklung und Betreuung von Shell-basierten Lade- und Entladestrecken unter AIX und Bash, die Dateitransfers per Connect:Direct steuerten und mit PL/1- sowie COBOL-basierten Host-Systemen interagierten. Perl-Wrapper für die Steuerung von Batch-Jobs, strukturiertes Logging und Exit-Code-Überwachung durch zentrales Monitoring. Datenmigrations-Projekte im Leben-Bereich unter Einbindung von Host-Copybooks und clientseitigem Datenbank-Mapping.
Logistik / Konzern
Aufbau und Weiterentwicklung von Shell-basierten Verarbeitungsstrecken unter UNIX/AIX, die Daten für Teradata FastLoad und Informatica PowerCenter vorbereiteten und nachverarbeiteten. Perl-basierte Job-Steuerung, KSH-Skripte für Dateitransfer und Monitoring, AIX-spezifische Besonderheiten im Datei-Handling und Prozessmanagement.
Eigenbetrieb / Infrastruktur
Aufbau und Betrieb einer eigenen Proxmox-basierten Infrastruktur mit mehreren Standorten, LXC-Containern und Docker-Diensten. Zentrales Konfigurationsmanagement, NGINX Reverse-Proxy mit Authelia-SSO und Let's Encrypt, WireGuard-VPN für Site-to-Site-Verbindungen, vollautomatisierte Backup-Pipeline mit rsync und rclone (3-2-1-Strategie), Monitoring mit Prometheus/Grafana, zentrale Betriebsdokumentation in Markdown/Git.
Öffentlicher Auftraggeber / Forschungsbereich
Unterstützung bei der Automatisierung von ETL-Prozessen in einer Linux-basierten DWH-Umgebung. Shell-Skripte für Datenladeprozesse, systemd-Unit-Entwicklung für Scheduling und Monitoring-Integration. Betriebsdokumentation für übergebene Prozesse.
Häufige Fragen zur Linux-Automatisierung
Was unterscheidet ein produktionstaugliches Bash-Skript von einem einfachen Script?
set -euo pipefail, trap für Cleanup und Fehlerprotokollierung, strukturiertes Logging mit Timestamps, flock-basiertes Lock gegen Doppelläufe, klar definierte Exit-Codes für Monitoring und Scheduler. Diese Bausteine machen den Unterschied zwischen einem Skript, das einmal funktioniert hat, und einem, das zuverlässig im Produktionsbetrieb läuft.
Cron oder systemd Timer – wann empfehlen Sie was?
Für neue Aufgaben auf modernen Debian/Ubuntu-Systemen bevorzuge ich systemd Timer: besseres Journaling, abfragbarer Status per systemctl, flexible Kalenderausdrücke und Persistent-Modus für verpasste Ausführungen. Cron ist dann sinnvoll, wenn Portabilität auf ältere Systeme oder AIX/KSH-Umgebungen gefragt ist.
Können Sie Proxmox VE für eine kleine bis mittlere Unternehmensinfrastruktur aufbauen?
Ja. Ich habe Proxmox produktiv für Eigenbetrieb und Kundenprojekte eingerichtet und betrieben: LXC-Container für Dienste, KVM für Windows-VMs, Proxmox Backup Server für Sicherungen, und Clustering für einfache HA. Proxmox bietet Enterprise-Funktionen ohne proprietäre Lizenzkosten.
Wie integrieren Sie Linux-Automatisierung mit SQL Server?
Über sqlcmd, bcp und pyodbc mit dem Microsoft ODBC Driver 18 für Linux, der stabil auf Debian und Ubuntu läuft. ETL-Pipelines, die komplett unter Linux entwickelt sind und Daten in SQL-Server-Datenbanken laden, sind ein Standard-Szenario in meinen Projekten. Kerberos-Authentifizierung für Windows-integrierte Anmeldung ist ebenfalls konfigurierbar.
Was ist Ihre Backup-Empfehlung für einen Linux-Server?
Drei-Stufen-Strategie: rsync für inkrementelle lokale Backups mit Hardlinks (platzsparend und schnell); rclone für Offsite-Replikation in S3, Azure Blob oder Backblaze B2; Proxmox Backup Server wenn Proxmox eingesetzt wird. Exit-Code-Überwachung und Healthchecks stellen sicher, dass Backup-Fehler sofort auffallen.
Können Sie bestehende Legacy-Shell-Skripte überarbeiten und absichern?
Ja, das ist eine häufige Aufgabe. Audit des bestehenden Skripts, Identifikation von Fehlerquellen (fehlende Fehlerbehandlung, ungeschützte Pipelines, kein Logging), schrittweise Härtung ohne Funktionsänderung, dokumentierte Tests. Das Ergebnis ist ein Skript, das dasselbe tut – aber im Fehlerfall nicht mehr still scheitert.
In welchen Linux-Distributionen haben Sie Erfahrung?
Hauptsächlich Debian und Ubuntu (Schwerpunkt in eigenem Betrieb und neueren Projekten), SUSE Linux in Enterprise-Umgebungen sowie AIX (IBM) mit KSH in Grosskonzernprojekten aus dem Logistik- und Versicherungsbereich. Die grundlegenden Konzepte und Werkzeuge sind distributionsübergreifend, aber Distribution-spezifische Paketmanager, Init-Systeme und Pfade kenne ich aus der jeweiligen Praxis.
Wie integrieren Sie Monitoring in Automatisierungsprozesse?
Exit-Codes sind die einfachste und zuverlässigste Methode: Jedes Skript meldet Erfolg (Exit 0) oder Fehler (Exit !=0) an den Scheduler. Healthcheck-Services wie healthchecks.io oder Prometheus Pushgateway empfangen Status-Pings und lösen Alerts aus, wenn Pings ausbleiben. Grafana-Dashboards visualisieren Läufe, Laufzeiten und Fehlerquoten über Zeit.