first commit

This commit is contained in:
duffyduck
2026-08-20 10:33:59 +02:00
commit 75fba0229a
14 changed files with 3076 additions and 0 deletions
+930
View File
@@ -0,0 +1,930 @@
# Checkmk im Container Docker (Debian) & Podman (RHEL) mit NGINX Proxy Manager
Vollständige, produktionstaugliche Container-Installation von **Checkmk** mit
**NGINX Proxy Manager** als HTTPS-Terminierung (Let's Encrypt *oder* selbstsigniert),
inklusive Werkzeugen für **Backup**, **Restore** und die **Migration bestehender
Sites von einer Direktinstallation** ohne dass die Agents neu eingerichtet
werden müssen.
---
## Inhaltsverzeichnis
1. [Überblick & Architektur](#1-überblick--architektur)
2. [Verzeichnisstruktur](#2-verzeichnisstruktur)
3. [Voraussetzungen & Ports](#3-voraussetzungen--ports)
4. [Installation auf Debian/Ubuntu (Docker)](#4-installation-auf-debianubuntu-docker)
5. [Installation auf RHEL/Rocky/Alma/Fedora (Podman)](#5-installation-auf-rhelrockyalmafedora-podman)
6. [Autostart nach einem Neustart des Hosts (Podman)](#6-autostart-nach-einem-neustart-des-hosts-podman)
7. [Konfiguration (`.env`)](#7-konfiguration-env)
8. [Erststart](#8-erststart)
9. [HTTPS einrichten Let's Encrypt oder selbstsigniert](#9-https-einrichten--lets-encrypt-oder-selbstsigniert)
10. [Firewall](#10-firewall)
11. [Verwaltung: `cmk-manage.sh` (CLI und ncurses-Oberfläche)](#11-verwaltung-cmk-managesh)
12. [Backup](#12-backup)
13. [Restore](#13-restore)
14. [Migration einer Direktinstallation (`migrate_creator.sh`)](#14-migration-einer-direktinstallation-migrate_creatorsh)
15. [Mehrere Sites in einem Container](#15-mehrere-sites-in-einem-container)
16. [Checkmk aktualisieren](#16-checkmk-aktualisieren)
17. [Fehlersuche](#17-fehlersuche)
18. [Sicherheitshinweise](#18-sicherheitshinweise)
19. [Deinstallation](#19-deinstallation)
---
## 1. Überblick & Architektur
```
Internet / LAN
|
:80 :443 :81
|
+-----------------------------------+
| NGINX Proxy Manager (Container) | network_mode: host
| - Let's Encrypt ODER self-signed |
| - Weboberfläche auf Port 81 |
+-----------------------------------+
| http://127.0.0.1:5000
v
+-----------------------------------+
| Checkmk / OMD (Container) | network_mode: host
| :5000 Web-GUI (Apache) |
| :8000 Agent-Receiver | <-- Agents (cmk-agent-ctl)
| :6557 Livestatus (optional) |
+-----------------------------------+
|
Bind-Mount: ./data/checkmk/sites -> /omd/sites
```
**Bewusste Entwurfsentscheidungen**
| Punkt | Umsetzung | Grund |
|---|---|---|
| Checkmk im Host-Netzwerk | `network_mode: host`, **keine** `ports:`-Angaben | Agents, SNMP-Traps, ICMP und der Agent-Receiver arbeiten ohne NAT; die Quell-IP der überwachten Hosts bleibt erhalten |
| NGINX Proxy Manager ebenfalls im Host-Netzwerk | `network_mode: host` | erreicht das Checkmk-Backend direkt über `127.0.0.1:5000`; kein Bridge-Netz, keine `host-gateway`-Tricks nötig funktioniert unter Docker **und** Podman identisch |
| Keine Named Volumes | ausschließlich **Bind-Mounts** nach `./data/` | alle Daten liegen sichtbar im Projektverzeichnis, einfach zu sichern, zu kopieren und zu prüfen |
| Eine Compose-Datei für beides | `docker compose` und `podman-compose` | identische Bedienung auf Debian und RHEL |
| `:Z` an den Bind-Mounts | SELinux-Relabeling | nötig unter RHEL; unter Debian/Docker wirkungslos (kein Fehler) |
---
## 2. Verzeichnisstruktur
```
.
├── README.md
├── docker-compose.yml # für Docker UND Podman
├── .env.example # Vorlage -> .env
├── install.sh # Installation Docker (Debian) / Podman (RHEL)
├── scripts/
│ ├── cmk-manage.sh # Stack, Backup, Restore, Import + ncurses-GUI
│ ├── migrate_creator.sh # -> auf den QUELLSERVER kopieren
│ └── lib/
│ └── common.sh # gemeinsame Funktionen + TUI-Helfer
├── systemd/
│ ├── checkmk-stack.service.example # Compose-basierte Unit (Autostart)
│ └── quadlet/
│ ├── checkmk.container # Podman-Quadlet (Alternative)
│ └── nginx-proxy-manager.container
├── data/ # ALLE Nutzdaten (Bind-Mounts)
│ ├── checkmk/sites/ # -> /omd/sites (komplette OMD-Sites)
│ └── npm/
│ ├── data/ # -> /data (NPM-Konfiguration/DB)
│ ├── letsencrypt/ # -> /etc/letsencrypt (Zertifikate)
│ └── custom-certs/ # selbstsignierte Zertifikate zum Hochladen
└── backups/ # Backups und Migrations-Bundles (*.tar.gz)
```
---
## 3. Voraussetzungen & Ports
* 64-Bit-Linux, mind. 2 CPU-Kerne, 4 GB RAM (Empfehlung: 4 Kerne, 8 GB)
* Plattenplatz: Faustregel 1 GB je 100 überwachte Hosts und Jahr, plus Backups
* root-Rechte
* Ausgehender Internetzugang für den Image-Download
### Belegte Ports (Host-Netzwerk!)
| Port | Dienst | Erreichbar von |
|---|---|---|
| 80/tcp | NGINX Proxy Manager HTTP, Let's-Encrypt-Challenge | Internet/LAN |
| 443/tcp | NGINX Proxy Manager HTTPS | Internet/LAN |
| 81/tcp | NGINX Proxy Manager **Adminoberfläche** | nur Admin-Netz! |
| 5000/tcp | Checkmk Apache (Backend) | **nur localhost** nicht freigeben |
| 8000/tcp | Checkmk Agent-Receiver (TLS-registrierte Agents) | überwachte Hosts |
| 6557/tcp | Livestatus (nur bei `CMK_LIVESTATUS_TCP=on`) | verteilte Sites |
> **Wichtig:** Da beide Container im Host-Netzwerk laufen, dürfen die Ports 80,
> 443 und 81 auf dem Host nicht bereits belegt sein (z. B. durch einen
> installierten Apache/NGINX). Prüfen mit `ss -tlnp | grep -E ':(80|81|443|5000)\b'`.
---
## 4. Installation auf Debian/Ubuntu (Docker)
```bash
# Projekt an den gewünschten Ort legen
sudo mkdir -p /opt/checkmk-stack
sudo cp -r ./* ./.env.example /opt/checkmk-stack/
cd /opt/checkmk-stack
# Docker CE + Compose-Plugin installieren, Projekt vorbereiten
sudo ./install.sh --with-systemd
```
`install.sh` erledigt dabei:
1. Docker-Repository von `download.docker.com` einbinden (GPG-Key + Sources-Liste)
2. `docker-ce`, `docker-ce-cli`, `containerd.io`, `docker-buildx-plugin`,
`docker-compose-plugin` installieren
3. `dialog`, `openssl`, `tar`, `curl` installieren (für die ncurses-Oberfläche)
4. `systemctl enable --now docker`
5. Verzeichnisse unter `data/` und `backups/` anlegen, `.env` aus `.env.example` erzeugen
6. optional die systemd-Unit `checkmk-stack.service` einrichten
Anschließend:
```bash
sudo $EDITOR /opt/checkmk-stack/.env # mindestens CMK_PASSWORD und CMK_SITE_ID
sudo ./scripts/cmk-manage.sh up
sudo ./scripts/cmk-manage.sh status
```
Unter Docker sorgt bereits `restart: unless-stopped` dafür, dass die Container
nach einem Reboot wieder starten (der Docker-Daemon startet sie). Die
systemd-Unit ist trotzdem sinnvoll, weil sie zusätzlich **alle** Sites startet
(siehe [Kapitel 15](#15-mehrere-sites-in-einem-container)).
---
## 5. Installation auf RHEL/Rocky/Alma/Fedora (Podman)
Auf Red-Hat-Systemen steht Docker nicht zur Verfügung hier wird **Podman**
verwendet. Die Compose-Datei ist dieselbe.
```bash
sudo mkdir -p /opt/checkmk-stack
sudo cp -r ./* ./.env.example /opt/checkmk-stack/
cd /opt/checkmk-stack
sudo ./install.sh --with-systemd
```
`install.sh` installiert dabei:
1. `podman`, `tar`, `dialog`, `openssl`
2. `podman-compose` in dieser Reihenfolge versucht:
Distributions-Repository → EPEL → `pip3 install podman-compose`
3. richtet die systemd-Unit ein (bei Podman **immer**, siehe nächstes Kapitel)
Manuell entspricht das:
```bash
sudo dnf install -y podman podman-compose tar dialog openssl
# Falls podman-compose nicht im Repo ist:
sudo dnf install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm
sudo dnf install -y podman-compose
```
Starten:
```bash
sudo $EDITOR /opt/checkmk-stack/.env
sudo ./scripts/cmk-manage.sh up # nutzt automatisch podman-compose
sudo ./scripts/cmk-manage.sh status
```
### SELinux
Die Bind-Mounts sind in `docker-compose.yml` mit `:Z` versehen, Podman setzt
dadurch automatisch den Kontext `container_file_t`. Falls Zugriffe dennoch
scheitern:
```bash
sudo semanage fcontext -a -t container_file_t "/opt/checkmk-stack/data(/.*)?"
sudo restorecon -Rv /opt/checkmk-stack/data
sudo ausearch -m avc -ts recent # AVC-Meldungen prüfen
```
---
## 6. Autostart nach einem Neustart des Hosts (Podman)
> **Das ist bei Podman der wichtigste Unterschied zu Docker:** Podman hat
> *keinen Daemon*. Die Angabe `restart: unless-stopped` in der Compose-Datei
> wirkt deshalb **nicht** über einen Reboot hinweg. Der Autostart muss über
> systemd erfolgen. Es gibt zwei erprobte Wege.
### Variante A Compose-basierte systemd-Unit (empfohlen, identisch zu Docker)
Wird von `./install.sh --with-systemd` automatisch erzeugt und aktiviert.
Manuell:
```bash
sudo tee /etc/systemd/system/checkmk-stack.service >/dev/null <<'EOF'
[Unit]
Description=Checkmk + NGINX Proxy Manager (Container-Stack)
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/opt/checkmk-stack
EnvironmentFile=/opt/checkmk-stack/.env
TimeoutStartSec=0
ExecStart=/bin/sh -c '/usr/bin/podman-compose up -d'
ExecStartPost=/opt/checkmk-stack/scripts/cmk-manage.sh start-all-sites
ExecStop=/bin/sh -c '/usr/bin/podman-compose down'
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now checkmk-stack.service
sudo systemctl status checkmk-stack.service
```
Prüfen, dass es einen Reboot übersteht:
```bash
sudo systemctl is-enabled checkmk-stack # -> enabled
sudo reboot
# nach dem Neustart:
sudo podman ps
sudo /opt/checkmk-stack/scripts/cmk-manage.sh status
```
`ExecStartPost` ruft `start-all-sites` auf. Das ist nötig, weil der
Checkmk-Entrypoint nur die in `CMK_SITE_ID` eingetragene Site startet bei
mehreren Sites blieben die übrigen sonst gestoppt.
### Variante B Podman-Quadlet (ab Podman 4.4, ohne Compose)
Quadlet erzeugt aus einfachen Beschreibungsdateien echte systemd-Units.
Vorteil: kein `podman-compose` nötig, saubere Abhängigkeiten, natives Podman.
```bash
sudo ./install.sh --quadlet
```
Das schreibt (mit den Werten aus der `.env`) nach `/etc/containers/systemd/`:
* `checkmk.container`
* `nginx-proxy-manager.container`
Danach:
```bash
sudo systemctl daemon-reload
sudo systemctl start checkmk nginx-proxy-manager
sudo systemctl status checkmk
```
Die Units werden durch `WantedBy=multi-user.target default.target` beim
Systemstart automatisch aktiviert ein separates `systemctl enable` ist bei
Quadlet nicht nötig (und auch nicht möglich, da die Units generiert werden).
Vorlagen liegen unter [systemd/quadlet/](systemd/quadlet/).
### Variante C rootless Podman (optional, erhöhter Aufwand)
```bash
sudo ./install.sh --rootless
sudo loginctl enable-linger checkmk # Dienstbenutzer
# Unit als Benutzer-Unit:
mkdir -p ~/.config/systemd/user
cp systemd/checkmk-stack.service.example ~/.config/systemd/user/checkmk-stack.service
systemctl --user daemon-reload
systemctl --user enable --now checkmk-stack
```
`--rootless` setzt `net.ipv4.ip_unprivileged_port_start=80`, damit die Ports
80/443 ohne root gebunden werden dürfen. `loginctl enable-linger` sorgt dafür,
dass die Benutzer-Units auch ohne Anmeldung nach einem Reboot starten.
> Hinweis: Im rootless-Betrieb werden die UIDs der Site-Benutzer über
> User-Namespaces abgebildet. Die Dateien unter `data/checkmk/sites` gehören
> dann Subordinate-UIDs (z. B. `165536`). Das ist normal, erschwert aber die
> Arbeit mit den Dateien vom Host aus. Für Migrationen wird der **rootful**
> Betrieb empfohlen.
---
## 7. Konfiguration (`.env`)
```bash
cp .env.example .env
chmod 600 .env
$EDITOR .env
```
Die wichtigsten Schalter:
| Variable | Bedeutung |
|---|---|
| `CMK_RUNTIME` | leer = automatische Erkennung, sonst `docker` oder `podman` |
| `CMK_IMAGE_REPO` | `checkmk/check-mk-raw` (frei) oder `checkmk/check-mk-enterprise` |
| `CMK_IMAGE_TAG` | **Version** bei einer Migration muss sie zur Quell-Site passen! |
| `CMK_SITE_ID` | Site, die der Container anlegt bzw. beim Start hochfährt |
| `CMK_PASSWORD` | Startpasswort für den Benutzer `cmkadmin` (nur bei Neuanlage) |
| `CMK_LIVESTATUS_TCP` | `on` öffnet Port 6557 für verteilte Überwachung |
| `MAIL_RELAY_HOST` | SMTP-Relay für Benachrichtigungen |
| `TZ` | Zeitzone, z. B. `Europe/Berlin` |
| `BACKUP_DIR` | Ablage für Backups/Bundles (Vorgabe `./backups`) |
| `CERT_DOMAIN`, `CERT_DAYS` | Vorgaben für selbstsignierte Zertifikate |
---
## 8. Erststart
```bash
sudo ./scripts/cmk-manage.sh up
sudo ./scripts/cmk-manage.sh status
```
Ausgabe (Beispiel):
```
Container
checkmk läuft
nginx-proxy-manager läuft
Sites im Container
SITE VERSION STATUS
cmk 2.3.0p23.cre läuft
Zugang
Checkmk (direkt) : http://10.0.0.5:5000/cmk/
Checkmk (über Proxy) : https://<FQDN>/cmk/
Agent-Receiver : 10.0.0.5:8000
NGINX Proxy Manager : http://10.0.0.5:81/ (Erstlogin: admin@example.com / changeme)
```
Anmeldung an Checkmk: Benutzer `cmkadmin`, Passwort aus `CMK_PASSWORD`.
---
## 9. HTTPS einrichten Let's Encrypt oder selbstsigniert
### 9.1 NGINX Proxy Manager vorbereiten
1. `http://<server>:81` aufrufen
2. Erstanmeldung: **admin@example.com** / **changeme**
3. Sofort E-Mail-Adresse und Passwort ändern (wird beim ersten Login erzwungen)
### 9.2 Proxy-Host für Checkmk anlegen
**Hosts → Proxy Hosts → Add Proxy Host**
| Feld | Wert |
|---|---|
| Domain Names | `checkmk.example.com` (bzw. der interne Name) |
| Scheme | `http` |
| Forward Hostname / IP | `127.0.0.1` |
| Forward Port | `5000` |
| Block Common Exploits | ein |
| Websockets Support | **ein** (für die Livedaten der Oberfläche) |
Reiter **Advanced** (empfohlen, damit Checkmk die echte Client-IP sieht und
große Agent-Uploads funktionieren):
```nginx
client_max_body_size 0;
proxy_read_timeout 300s;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
```
### 9.3 Variante A Let's Encrypt (öffentlich erreichbarer Server)
Voraussetzungen: der DNS-Name zeigt öffentlich auf diesen Server und **Port 80
ist aus dem Internet erreichbar** (HTTP-01-Challenge).
Im Proxy-Host, Reiter **SSL**:
* SSL Certificate → **Request a new SSL Certificate**
* *Force SSL* ein
* *HTTP/2 Support* ein
* *HSTS Enabled* nach Bedarf
* E-Mail-Adresse angeben, Nutzungsbedingungen bestätigen → **Save**
Die Erneuerung übernimmt der Proxy Manager automatisch; die Zertifikate liegen
im Bind-Mount `data/npm/letsencrypt/` und sind damit im Projektverzeichnis
gesichert.
Bei interner DNS-Zone ohne öffentlichen Zugang: **DNS-Challenge** verwenden
(SSL → *Use a DNS Challenge* → Provider auswählen, API-Token hinterlegen).
### 9.4 Variante B Selbstsigniertes Zertifikat (interne Umgebung)
Erzeugen:
```bash
sudo ./scripts/cmk-manage.sh cert \
--domain checkmk.intern.example \
--alt 10.0.0.5 --alt checkmk \
--days 3650
```
Ergebnis in `data/npm/custom-certs/checkmk.intern.example/`:
* `privkey.pem` privater Schlüssel
* `fullchain.pem` Zertifikat (mit SAN für alle angegebenen Namen/IPs)
Einbinden:
1. **SSL Certificates → Add SSL Certificate → Custom**
2. Name vergeben, `privkey.pem` als *Certificate Key*, `fullchain.pem` als
*Certificate* hochladen
3. Im Proxy-Host unter **SSL** dieses Zertifikat auswählen, *Force SSL* und
*HTTP/2* aktivieren
> Damit die Checkmk-Agents und Browser dem Zertifikat vertrauen, muss
> `fullchain.pem` auf den Clients als vertrauenswürdiges Zertifikat installiert
> werden (Windows: Computerzertifikate → Vertrauenswürdige Stammzertifizierungsstellen;
> Linux: `/usr/local/share/ca-certificates/` + `update-ca-certificates`).
> Für die reine Agent-Kommunikation über Port 8000 ist das **nicht** nötig
> dort verwendet Checkmk eine eigene, site-interne CA.
Die ncurses-Oberfläche bietet denselben Ablauf unter *„Selbstsigniertes
Zertifikat erzeugen“*.
---
## 10. Firewall
**firewalld (RHEL/Rocky/Alma):**
```bash
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --permanent --add-port=8000/tcp # Agent-Receiver
sudo firewall-cmd --permanent --add-rich-rule='rule family=ipv4 source address=10.0.0.0/24 port port=81 protocol=tcp accept'
sudo firewall-cmd --reload
```
**ufw (Debian/Ubuntu):**
```bash
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 8000/tcp
sudo ufw allow from 10.0.0.0/24 to any port 81 proto tcp
sudo ufw enable
```
Port **5000 bleibt geschlossen** der Zugriff erfolgt ausschließlich über den
Proxy. Port **81** nur aus dem Administrationsnetz freigeben.
---
## 11. Verwaltung: `cmk-manage.sh`
Das Skript ist gleichzeitig Kommandozeilenwerkzeug **und** ncurses-Oberfläche.
### 11.1 ncurses-Oberfläche (wie im Midnight Commander)
```bash
sudo ./scripts/cmk-manage.sh gui
```
```
┌──────────────── Checkmk Stack Manager ─────────────────┐
│ Projekt : /opt/checkmk-stack │
│ Runtime : podman Standard-Site: cmk │
│ Backups : /opt/checkmk-stack/backups │
│ │
│ status Status von Containern und Sites │
│ stack Stack steuern (Start/Stop/Logs/Update) │
│ backup Backup erstellen │
│ restore Backup wiederherstellen │
│ import Site von Direktinstallation importieren │
│ manage Backups verwalten (Liste/Details/Löschen) │
│ sites Sites im Container verwalten │
│ cert Selbstsigniertes Zertifikat erzeugen │
│ env Konfiguration (.env) ansehen/bearbeiten │
│ shell Root-Shell im Checkmk-Container │
│ quit Beenden │
│ < OK > <Cancel> │
└────────────────────────────────────────────────────────┘
```
Bedienung: Pfeiltasten, **Leertaste** markiert in Auswahllisten (z. B. mehrere
Sites für ein Backup), **Enter** bestätigt, **ESC/Cancel** geht zurück.
Es wird `dialog` bevorzugt; ist nur `whiptail` vorhanden, wird dieses genutzt.
Eine Dateiauswahl mit Verzeichnisnavigation ist eingebaut.
### 11.2 Kommandozeile
```bash
./scripts/cmk-manage.sh --help
```
| Kommando | Wirkung |
|---|---|
| `up` / `down` / `restart` / `pull` | Stack steuern |
| `status` | Container, Sites, Zugangsadressen |
| `logs [-f]` | Container-Logs |
| `sites` | Sites im Container auflisten |
| `site-start SITE` / `site-stop SITE` | einzelne Site steuern |
| `start-all-sites` | alle Sites starten (wird von systemd aufgerufen) |
| `backup …` | Backup als `.tar.gz`-Bundle |
| `list` / `verify` / `prune --keep N` | Backups verwalten |
| `restore …` | Backup zurückspielen |
| `import …` | Migrations-Bundle einspielen |
| `cert …` | selbstsigniertes Zertifikat erzeugen |
| `shell` | Root-Shell im Checkmk-Container |
| `omd …` | beliebiges `omd`-Kommando im Container |
Globale Optionen: `--env-file DATEI`, `-y/--yes` (keine Rückfragen, z. B. für
Cronjobs), `-v/--verbose`.
---
## 12. Backup
Backups werden mit `omd backup` erstellt und als **Bundle** verpackt:
```
cmkbackup_<site>_<version>_<zeitstempel>.tar.gz
└── cmkbundle/
├── MANIFEST Site, Version, Quelle, Prüfsumme …
├── INFO.txt lesbare Zusammenfassung
├── site.tar.gz das eigentliche omd-Backup
└── site.tar.gz.sha256
```
Dadurch weiß das Restore-/Import-Werkzeug immer, **welche Site** und **welche
Checkmk-Version** im Archiv steckt, und kann die Integrität prüfen.
```bash
# eine Site
sudo ./scripts/cmk-manage.sh backup --site cmk
# alle Sites, ohne Logdateien, mit Kennzeichnung
sudo ./scripts/cmk-manage.sh backup --all --no-logs --label vor-update
# konsistentestes Backup: Site wird kurz gestoppt
sudo ./scripts/cmk-manage.sh backup --site cmk --stop
# ohne Performance-Daten (deutlich kleiner)
sudo ./scripts/cmk-manage.sh backup --site cmk --no-rrds --dir /mnt/nas/checkmk
```
Auflisten und prüfen:
```bash
./scripts/cmk-manage.sh list
./scripts/cmk-manage.sh verify --file backups/cmkbackup_cmk_2.3.0p23.cre_20260820-030000.tar.gz
./scripts/cmk-manage.sh prune --keep 7
```
### Nächtliches Backup per Cron
```bash
sudo crontab -e
```
```cron
15 2 * * * /opt/checkmk-stack/scripts/cmk-manage.sh -y backup --all --no-logs >>/var/log/cmk-backup.log 2>&1
30 3 * * 0 /opt/checkmk-stack/scripts/cmk-manage.sh -y prune --keep 14 >>/var/log/cmk-backup.log 2>&1
```
---
## 13. Restore
```bash
# Einfachster Fall: Site existiert nicht mehr
sudo ./scripts/cmk-manage.sh restore --file backups/cmkbackup_cmk_2.3.0p23.cre_20260820-030000.tar.gz
# Vorhandene Site überschreiben (laufende Prozesse beenden)
sudo ./scripts/cmk-manage.sh restore --file backups/… --reuse --kill
# Unter anderem Namen zurückspielen (z. B. für einen Test)
sudo ./scripts/cmk-manage.sh restore --file backups/… --as-site test
```
Ablauf des Skripts:
1. Archivstruktur und SHA256-Prüfsumme kontrollieren
2. prüfen, ob die **Checkmk-Version des Backups im Container vorhanden** ist
3. bei vorhandener Site Rückfrage/`--reuse`, Site stoppen
4. Archiv in den Container kopieren und `omd restore` ausführen
5. `TMPFS` der Site abschalten (im Container nicht nutzbar)
6. Site starten und Hinweise ausgeben
> **`--as-site` ändert den Site-Namen** registrierte Agents zeigen dann ins
> Leere und müssten neu registriert werden. Für eine Migration also **nicht**
> verwenden.
Ein rohes `omd backup`-Archiv (ohne Bundle-Hülle) wird ebenfalls akzeptiert;
der Site-Name wird dann aus dem Archiv gelesen.
---
## 14. Migration einer Direktinstallation (`migrate_creator.sh`)
Ziel: eine bestehende Checkmk-Installation auf Blech/VM in den Container
übernehmen **ohne die Agents anzufassen**.
### 14.1 Warum die Agents weiterlaufen
Ein `omd backup` enthält die komplette Site inklusive
`etc/ssl` (site-eigene CA und Zertifikate der registrierten Agents),
`etc/check_mk` (Regeln, Hosts, Passwörter) und der Monitoring-Historie.
Wird die Site **unter demselben Namen** wiederhergestellt und ist der Server
unter **derselben IP-Adresse** erreichbar, dann
* finden Push-/Pull-Agents (`cmk-agent-ctl`) ihre Registrierung wieder,
* bleibt die TLS-Verbindung zum Agent-Receiver auf **Port 8000** gültig,
* stimmen alle URLs (`https://<ip>/<site>/check_mk/…`) weiterhin,
* bleiben Performance-Daten und Historie erhalten.
Voraussetzung ist deshalb, dass das Zielsystem die **IP des Quellservers
übernimmt** (bzw. der DNS-Name darauf umgestellt wird) das ist im geplanten
Vorgehen ohnehin so.
### 14.2 Schritt 1 Skript auf den Quellserver kopieren
```bash
scp scripts/migrate_creator.sh root@alt-server:/root/
ssh root@alt-server 'chmod +x /root/migrate_creator.sh'
```
Das Skript ist **eigenständig** es braucht auf dem Quellserver nur `bash`,
`tar` und `omd` (für die Oberfläche zusätzlich `dialog` oder `whiptail`).
### 14.3 Schritt 2 Bundle auf dem Quellserver erzeugen
Vorhandene Sites anzeigen:
```bash
sudo ./migrate_creator.sh --list
```
```
SITE VERSION STATUS GROESSE
prod 2.3.0p23.cre laeuft 4.7 GiB
test 2.3.0p23.cre gestoppt 318.2 MiB
```
**Mit Oberfläche** (Sites per Leertaste auswählen):
```bash
sudo ./migrate_creator.sh --gui
```
Der Dialog fragt nacheinander ab: Sites → Optionen (Site stoppen, RRDs/Logs
auslassen) → Zielverzeichnis → Kennzeichnung → optionale Übertragung per `scp`.
**Mit Parametern:**
```bash
# eine Site, Site während des Backups stoppen (konsistenteste Variante)
sudo ./migrate_creator.sh --site prod --stop --out /var/tmp
# alle Sites, ohne Logs, direkt auf das Zielsystem übertragen
sudo ./migrate_creator.sh --all --no-logs \
--scp root@10.0.0.5:/opt/checkmk-stack/backups/
```
Ergebnis:
```
/var/tmp/cmkmigrate_prod_2.3.0p23.cre_20260820-101500.tar.gz
```
Das Bundle enthält zusätzlich zum omd-Backup ein `MANIFEST` mit Site-Name,
Checkmk-Version, Quell-Hostname, **Quell-IP-Adressen**, Quell-Betriebssystem
und SHA256-Prüfsumme sowie ein Verzeichnis `siteinfo/` mit `omd config show`,
`omd status` und der IP-Konfiguration des Altsystems zur späteren Kontrolle.
### 14.4 Schritt 3 Zielsystem auf die richtige Version bringen
`omd restore` kann eine Site **nur wiederherstellen, wenn genau deren
Checkmk-Version im Zielsystem installiert ist.** Im Container kommt die Version
aus dem Image also muss der Image-Tag passen:
```bash
grep CMK_VERSION <(tar -xzOf backups/cmkmigrate_prod_*.tar.gz cmkbundle/MANIFEST)
# CMK_VERSION=2.3.0p23.cre -> Image-Tag: 2.3.0p23
sudo $EDITOR .env
# CMK_IMAGE_TAG=2.3.0p23
sudo ./scripts/cmk-manage.sh up
```
Das Import-Skript prüft das selbst und bricht mit einer entsprechenden Meldung
ab, wenn die Version fehlt.
> **Edition beachten:** `.cre` = Raw Edition → `checkmk/check-mk-raw`.
> Bei `.cee` / `.cce` / `.cme` wird das entsprechende Enterprise-/Cloud-Image
> benötigt (`CMK_IMAGE_REPO=checkmk/check-mk-enterprise`, Zugang über die
> Checkmk-Subskription).
### 14.5 Schritt 4 Bundle importieren
Mit Oberfläche:
```bash
sudo ./scripts/cmk-manage.sh gui # -> "import"
```
Mit Parametern:
```bash
sudo ./scripts/cmk-manage.sh import \
--file backups/cmkmigrate_prod_2.3.0p23.cre_20260820-101500.tar.gz \
--set-default-site
```
Das Skript
1. erkennt das Migrations-Bundle und zeigt Quell-Host und Quell-IP an,
2. **warnt, wenn die IP dieses Hosts von der Quell-IP abweicht** (dann müssten
die Agents nachgeführt werden),
3. prüft Prüfsumme, Version und Edition,
4. fragt bei bereits vorhandener Site nach (`--reuse` überschreibt),
5. führt `omd restore` unter dem **unveränderten Site-Namen** aus,
6. setzt auf Wunsch `CMK_SITE_ID` in der `.env` (`--set-default-site`), damit
der Container die Site auch nach einem Reboot automatisch startet,
7. startet die Site und gibt die nächsten Schritte aus.
### 14.6 Schritt 5 Umschalten und kontrollieren
```bash
# 1) Alten Server abschalten bzw. dessen Site stoppen (kein Doppel-Monitoring!)
ssh root@alt-server 'omd stop prod'
# 2) Zielsystem auf die IP/den DNS-Namen des Altsystems umstellen
# (statische IP übernehmen oder DNS-Eintrag umbiegen)
# 3) Container neu erstellen, falls CMK_SITE_ID geändert wurde
sudo ./scripts/cmk-manage.sh up
sudo ./scripts/cmk-manage.sh status
# 4) Kontrolle in Checkmk
sudo ./scripts/cmk-manage.sh omd su prod
OMD[prod]:~$ cmk -O # Konfiguration aktivieren
OMD[prod]:~$ omd status
OMD[prod]:~$ exit
```
Danach in der Oberfläche prüfen:
* **Monitor → All hosts** melden sich die Agents wieder?
* Bei TLS-Agents: der Service *„Check_MK Agent“* darf keine
Registrierungsfehler melden.
* Ein Agent lässt sich vom überwachten Host aus testen mit
`cmk-agent-ctl status` dort muss die Site `prod` mit dem Zielserver und
„Connection: TLS“ erscheinen.
### 14.7 Checkliste Migration
- [ ] `migrate_creator.sh` auf den Quellserver kopiert
- [ ] Bundle erzeugt (möglichst mit `--stop`)
- [ ] Bundle auf das Zielsystem kopiert (`backups/`)
- [ ] `CMK_IMAGE_TAG` auf die Quellversion gesetzt, Stack neu erstellt
- [ ] `import --set-default-site` ausgeführt, Site läuft
- [ ] Alte Site gestoppt / alter Server abgeschaltet
- [ ] IP bzw. DNS auf das Zielsystem umgestellt
- [ ] Ports 80/443/8000 in der Firewall offen
- [ ] Proxy-Host im NGINX Proxy Manager mit Zertifikat eingerichtet
- [ ] Agents melden sich, `cmk -O` ausgeführt
- [ ] Backup des neuen Systems eingerichtet (Cron)
---
## 15. Mehrere Sites in einem Container
Der Checkmk-Entrypoint legt beim Start nur die Site aus `CMK_SITE_ID` an und
startet auch nur diese. Weitere Sites (z. B. mehrere importierte) existieren im
Bind-Mount, werden aber nach einem Neustart nicht automatisch hochgefahren.
Deshalb:
```bash
sudo ./scripts/cmk-manage.sh start-all-sites
```
Dieses Kommando wird von der systemd-Unit (`ExecStartPost`) bzw. der
Quadlet-Unit automatisch nach dem Containerstart ausgeführt und startet alle
vorhandenen Sites.
> **Empfehlung:** Für sauber getrennte Umgebungen lieber je Site ein eigenes
> Projektverzeichnis mit eigener `.env` betreiben. Da beide Container im
> Host-Netzwerk laufen, kann pro Host allerdings nur **ein** Checkmk-Container
> die Ports 5000/8000 belegen für echte Mehrfachinstanzen dann getrennte
> Hosts oder eine Bridge-Netzwerk-Variante verwenden.
---
## 16. Checkmk aktualisieren
Da `/omd/sites` ein Bind-Mount ist, überleben die Daten den Austausch des
Images. Die Checkmk-Software selbst steckt im Image unter `/omd/versions`.
```bash
# 1) IMMER zuerst sichern
sudo ./scripts/cmk-manage.sh backup --all --label vor-update
# 2) neuen Tag eintragen
sudo $EDITOR .env # z.B. CMK_IMAGE_TAG=2.3.0p28
# 3) Image holen und Container neu erstellen
sudo ./scripts/cmk-manage.sh pull
sudo ./scripts/cmk-manage.sh up
# 4) Site aktualisieren (interaktiv, Konflikte bestätigen)
sudo ./scripts/cmk-manage.sh omd stop cmk
sudo ./scripts/cmk-manage.sh omd update cmk
sudo ./scripts/cmk-manage.sh omd start cmk
```
Bei einem Versionssprung (z. B. 2.2 → 2.3) unbedingt vorher die
Checkmk-Werksnotizen (Werks/Release Notes) lesen.
---
## 17. Fehlersuche
| Symptom | Ursache / Lösung |
|---|---|
| `Port 80/443 already in use` | Auf dem Host läuft bereits ein Webserver: `ss -tlnp \| grep ':80'``systemctl disable --now apache2 nginx httpd` |
| Nach Reboot laufen die Container nicht (Podman) | systemd-Unit fehlt: `./install.sh --with-systemd` bzw. `--quadlet`, danach `systemctl is-enabled checkmk-stack` |
| `omd restore`: *version is not installed* | `CMK_IMAGE_TAG` in der `.env` auf die Version aus dem `MANIFEST` setzen und `up` ausführen |
| Import bricht mit Editions-Warnung ab | Enterprise-Backup benötigt das Enterprise-Image (`CMK_IMAGE_REPO`) |
| Proxy zeigt `502 Bad Gateway` | Checkmk-Container läuft nicht oder Weiterleitung nicht auf `127.0.0.1:5000` gesetzt: `./scripts/cmk-manage.sh status`, `curl -I http://127.0.0.1:5000/` |
| Let's Encrypt schlägt fehl | Port 80 aus dem Internet nicht erreichbar oder DNS falsch; Alternative: DNS-Challenge oder selbstsigniertes Zertifikat |
| Agents melden sich nach der Migration nicht | Site-Name geändert (`--as-site`), IP nicht übernommen oder Port 8000 blockiert; auf dem Agent prüfen: `cmk-agent-ctl status` |
| Permission denied auf `data/` unter RHEL | SELinux: `restorecon -Rv /opt/checkmk-stack/data`, `:Z` an den Mounts prüfen |
| `podman-compose` nicht gefunden | EPEL aktivieren oder auf Quadlet umstellen (`./install.sh --quadlet`) |
| TUI startet nicht | `dialog` installieren: `apt-get install -y dialog` bzw. `dnf install -y dialog` |
| Site startet nicht, `tmp`-Fehler | `./scripts/cmk-manage.sh omd config <site> set TMPFS off`, danach Site starten |
Nützliche Kommandos:
```bash
./scripts/cmk-manage.sh logs -f # Container-Logs
./scripts/cmk-manage.sh omd status cmk # Dienste der Site
./scripts/cmk-manage.sh shell # Root-Shell im Container
docker exec -it checkmk omd su cmk # bzw. podman exec …
```
---
## 18. Sicherheitshinweise
* `CMK_PASSWORD` sofort nach der Installation in der Oberfläche ändern und aus
der `.env` entfernen (wird nur bei der Neuanlage ausgewertet);
`chmod 600 .env` ist gesetzt.
* Standardzugang des Proxy Managers (`admin@example.com` / `changeme`) beim
ersten Login zwingend ändern.
* Port **81** (Adminoberfläche) nur aus dem Administrationsnetz erreichbar
machen, Port **5000** gar nicht nach außen öffnen.
* Backups enthalten **alle** Passwörter und Zertifikate der Site verschlüsselt
ablegen bzw. Zugriffsrechte eng fassen (`chmod 700 backups`).
* Regelmäßig `./scripts/cmk-manage.sh pull` + `up` für Sicherheitsupdates der
Images einplanen.
* Host-Netzwerk bedeutet: der Container teilt sich den Netzwerk-Stack des Hosts.
Es gibt keine zusätzliche Netzwerkisolierung die Absicherung erfolgt über
die Firewall des Hosts.
---
## 19. Deinstallation
```bash
sudo ./scripts/cmk-manage.sh down
# systemd
sudo systemctl disable --now checkmk-stack.service
sudo rm -f /etc/systemd/system/checkmk-stack.service
# bzw. Quadlet
sudo rm -f /etc/containers/systemd/checkmk.container \
/etc/containers/systemd/nginx-proxy-manager.container
sudo systemctl daemon-reload
# Images entfernen
docker image rm checkmk/check-mk-raw:2.3.0-latest jc21/nginx-proxy-manager:2
# podman image rm …
# Daten (ACHTUNG: löscht die komplette Überwachung!)
sudo rm -rf /opt/checkmk-stack/data
```
---
## Lizenz / Hinweise
Dieses Projekt enthält lediglich Konfiguration und Hilfsskripte.
Checkmk (Raw Edition, GPLv2) und NGINX Proxy Manager (MIT) unterliegen den
Lizenzbedingungen der jeweiligen Hersteller.
View File
View File
View File
View File
View File
+65
View File
@@ -0,0 +1,65 @@
# =====================================================================
# Checkmk + NGINX Proxy Manager
# Funktioniert identisch mit: docker compose | podman-compose
#
# - Beide Dienste laufen im Host-Netzwerk -> keine "ports:"-Definition
# - Ausschliesslich Bind-Mounts nach ./data -> keine named volumes
# =====================================================================
services:
# -------------------------------------------------------------------
# Checkmk (OMD) - im Host-Netzwerk
# Port 5000 = Web-GUI (wird vom NGINX Proxy Manager als Backend genutzt)
# Port 8000 = Agent-Receiver (cmk-agent-ctl / TLS-registrierte Agents)
# Port 6557 = Livestatus (nur wenn CMK_LIVESTATUS_TCP=on)
# -------------------------------------------------------------------
checkmk:
image: ${CMK_IMAGE_REPO:-checkmk/check-mk-raw}:${CMK_IMAGE_TAG:-2.3.0-latest}
container_name: ${CMK_CONTAINER_NAME:-checkmk}
hostname: ${CMK_HOSTNAME:-checkmk}
network_mode: host
restart: unless-stopped
stop_grace_period: 120s
environment:
CMK_SITE_ID: ${CMK_SITE_ID:-cmk}
CMK_PASSWORD: ${CMK_PASSWORD:-}
CMK_LIVESTATUS_TCP: ${CMK_LIVESTATUS_TCP:-off}
MAIL_RELAY_HOST: ${MAIL_RELAY_HOST:-}
TZ: ${TZ:-Europe/Berlin}
volumes:
# Kompletter OMD-Datenbestand als Bind-Mount im Projektverzeichnis
- ${CMK_DATA_DIR:-./data/checkmk/sites}:/omd/sites:Z
- /etc/localtime:/etc/localtime:ro
ulimits:
nofile:
soft: 65536
hard: 65536
# ---------------------------------------------------------------
# Optional: tmpfs fuer das Site-tmp-Verzeichnis (Performance).
# Bewusst deaktiviert, weil uid/gid der Site nach einem Import
# abweichen koennen und das tmpfs dann falsch gemountet waere.
# Bei einer frisch angelegten Site ist uid/gid=1000 korrekt:
# ---------------------------------------------------------------
# tmpfs:
# - /opt/omd/sites/${CMK_SITE_ID:-cmk}/tmp:uid=1000,gid=1000
# -------------------------------------------------------------------
# NGINX Proxy Manager - ebenfalls Host-Netzwerk
# Port 80 = HTTP + Let's-Encrypt HTTP-01 Challenge
# Port 443 = HTTPS
# Port 81 = Admin-Oberflaeche (unbedingt per Firewall einschraenken!)
# -------------------------------------------------------------------
nginx-proxy-manager:
image: ${NPM_IMAGE:-jc21/nginx-proxy-manager:2}
container_name: ${NPM_CONTAINER_NAME:-nginx-proxy-manager}
network_mode: host
restart: unless-stopped
environment:
DISABLE_IPV6: ${NPM_DISABLE_IPV6:-true}
TZ: ${TZ:-Europe/Berlin}
volumes:
- ${NPM_DATA_DIR:-./data/npm/data}:/data:Z
- ${NPM_LE_DIR:-./data/npm/letsencrypt}:/etc/letsencrypt:Z
depends_on:
- checkmk
Executable
+307
View File
@@ -0,0 +1,307 @@
#!/usr/bin/env bash
# =====================================================================
# install.sh - Installation des Checkmk-/NGINX-Proxy-Manager-Stacks
#
# Debian/Ubuntu : Docker CE + Compose-Plugin
# RHEL/Rocky/Alma/CentOS/Fedora : Podman + podman-compose
#
# Zusaetzlich: dialog (ncurses-Oberflaeche), openssl, tar, curl
# =====================================================================
set -euo pipefail
PROJECT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
. "$PROJECT_DIR/scripts/lib/common.sh"
WITH_SYSTEMD=""
WITH_QUADLET=""
SKIP_RUNTIME=""
ROOTLESS=""
usage() {
cat <<'EOF'
install.sh - Installation des Checkmk-Container-Stacks
OPTIONEN
--with-systemd systemd-Unit einrichten, damit der Stack nach einem
Neustart des Hosts automatisch wieder startet
(Vorgabe bei Podman)
--quadlet statt der Compose-Unit Podman-Quadlet-Dateien
(/etc/containers/systemd/) einrichten
--rootless Podman rootless vorbereiten (linger + Ports ab 80)
--skip-runtime Docker/Podman nicht installieren (nur Projekt einrichten)
-h, --help Hilfe
EOF
}
while [ $# -gt 0 ]; do
case "$1" in
--with-systemd) WITH_SYSTEMD=1; shift ;;
--quadlet) WITH_QUADLET=1; WITH_SYSTEMD=1; shift ;;
--rootless) ROOTLESS=1; shift ;;
--skip-runtime) SKIP_RUNTIME=1; shift ;;
-h|--help) usage; exit 0 ;;
*) die "Unbekannte Option: $1" ;;
esac
done
[ "$(id -u)" -eq 0 ] || die "Bitte als root ausfuehren: sudo ./install.sh"
# ---------------------------------------------------------------------
# Distribution erkennen
# ---------------------------------------------------------------------
[ -r /etc/os-release ] || die "/etc/os-release nicht lesbar - Distribution unbekannt."
# shellcheck disable=SC1091
. /etc/os-release
OS_ID="${ID:-unknown}"
OS_LIKE="${ID_LIKE:-}"
OS_NAME="${PRETTY_NAME:-$OS_ID}"
FAMILY=""
case "$OS_ID" in
debian|ubuntu|linuxmint|raspbian|pop) FAMILY=debian ;;
rhel|centos|rocky|almalinux|fedora|ol|oracle|scientific) FAMILY=rhel ;;
*)
case " $OS_LIKE " in
*debian*) FAMILY=debian ;;
*rhel*|*fedora*|*centos*) FAMILY=rhel ;;
esac
;;
esac
[ -n "$FAMILY" ] || die "Nicht unterstuetzte Distribution: $OS_NAME"
printf '\n%s=== Checkmk Container-Stack - Installation ===%s\n\n' "$C_BLD" "$C_RST"
log "System : $OS_NAME"
log "Familie : $FAMILY"
log "Projektpfad : $PROJECT_DIR"
# ---------------------------------------------------------------------
# Runtime installieren
# ---------------------------------------------------------------------
install_debian() {
log "Installiere Docker CE aus dem offiziellen Docker-Repository ..."
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y ca-certificates curl gnupg tar dialog openssl
if [ "$OS_ID" = debian ] || [ "$OS_ID" = ubuntu ]; then
install -m 0755 -d /etc/apt/keyrings
if [ ! -f /etc/apt/keyrings/docker.asc ]; then
curl -fsSL "https://download.docker.com/linux/$OS_ID/gpg" -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc
fi
local codename="${VERSION_CODENAME:-}"
[ -n "$codename" ] || codename="$(. /etc/os-release && echo "${UBUNTU_CODENAME:-stable}")"
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/$OS_ID $codename stable" \
>/etc/apt/sources.list.d/docker.list
apt-get update -qq
apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
else
warn "Abgeleitete Distribution ($OS_NAME) - verwende die Distributionspakete."
apt-get install -y docker.io docker-compose
fi
systemctl enable --now docker
ok "Docker installiert: $(docker --version)"
}
install_rhel() {
log "Installiere Podman ..."
local PM=dnf
command -v dnf >/dev/null 2>&1 || PM=yum
$PM install -y podman tar dialog openssl || die "Podman-Installation fehlgeschlagen."
# podman-compose besorgen (Reihenfolge: Distro-Repo -> EPEL -> pip)
if ! command -v podman-compose >/dev/null 2>&1; then
log "Suche podman-compose ..."
if ! $PM install -y podman-compose 2>/dev/null; then
if [ "$OS_ID" != fedora ]; then
local rel; rel="${VERSION_ID%%.*}"
log "Aktiviere EPEL ${rel} ..."
$PM install -y "https://dl.fedoraproject.org/pub/epel/epel-release-latest-${rel}.noarch.rpm" 2>/dev/null || true
$PM install -y podman-compose 2>/dev/null || true
fi
fi
fi
if ! command -v podman-compose >/dev/null 2>&1; then
log "Versuche podman-compose per pip ..."
$PM install -y python3-pip 2>/dev/null || true
pip3 install --quiet podman-compose 2>/dev/null || true
fi
if command -v podman-compose >/dev/null 2>&1; then
ok "podman-compose vorhanden: $(podman-compose --version 2>&1 | head -n1)"
else
warn "podman-compose konnte nicht installiert werden.
Alternative: Installation ueber Quadlet ( ./install.sh --quadlet ),
dafuer wird kein Compose benoetigt."
fi
ok "Podman installiert: $(podman --version)"
# SELinux: Bind-Mounts brauchen den passenden Kontext (:Z ist im Compose gesetzt)
if command -v getenforce >/dev/null 2>&1 && [ "$(getenforce)" = "Enforcing" ]; then
log "SELinux ist aktiv - die Bind-Mounts sind im Compose mit ':Z' versehen."
fi
}
if [ -z "$SKIP_RUNTIME" ]; then
case "$FAMILY" in
debian) install_debian ;;
rhel) install_rhel ;;
esac
else
log "Runtime-Installation uebersprungen (--skip-runtime)."
fi
# ---------------------------------------------------------------------
# Projekt vorbereiten
# ---------------------------------------------------------------------
log "Bereite Projektverzeichnis vor ..."
mkdir -p "$PROJECT_DIR/data/checkmk/sites" \
"$PROJECT_DIR/data/npm/data" \
"$PROJECT_DIR/data/npm/letsencrypt" \
"$PROJECT_DIR/data/npm/custom-certs" \
"$PROJECT_DIR/backups"
chmod +x "$PROJECT_DIR"/scripts/*.sh "$PROJECT_DIR/install.sh" 2>/dev/null || true
if [ ! -f "$PROJECT_DIR/.env" ]; then
cp "$PROJECT_DIR/.env.example" "$PROJECT_DIR/.env"
chmod 600 "$PROJECT_DIR/.env"
ok ".env aus .env.example erzeugt - bitte Passwort und Site-Namen anpassen!"
else
log ".env ist bereits vorhanden - bleibt unveraendert."
fi
load_env "$PROJECT_DIR/.env"
detect_runtime
ok "Verwendete Runtime: $RUNTIME"
# Podman: Autostart nach Reboot ohne systemd-Unit funktioniert nicht ->
# Unit standardmaessig einrichten.
if [ "$RUNTIME" = podman ] && [ -z "$WITH_SYSTEMD" ]; then
warn "Podman hat keinen Daemon: ohne systemd-Unit starten die Container
nach einem Reboot NICHT automatisch. Die Unit wird jetzt eingerichtet."
WITH_SYSTEMD=1
fi
# ---------------------------------------------------------------------
# Rootless-Vorbereitung (optional)
# ---------------------------------------------------------------------
if [ -n "$ROOTLESS" ]; then
log "Bereite rootless-Betrieb vor ..."
echo 'net.ipv4.ip_unprivileged_port_start=80' >/etc/sysctl.d/99-checkmk-rootless.conf
sysctl -q --system || true
ok "Unprivilegierte Ports ab 80 erlaubt."
warn "Zusaetzlich noetig: loginctl enable-linger <benutzer>
und die Unit als '--user'-Unit einrichten (siehe README)."
fi
# ---------------------------------------------------------------------
# systemd
# ---------------------------------------------------------------------
install_compose_unit() {
local compose_bin
detect_compose
compose_bin="${COMPOSE_CMD[*]}"
log "Richte systemd-Unit 'checkmk-stack.service' ein ..."
cat >/etc/systemd/system/checkmk-stack.service <<EOF
[Unit]
Description=Checkmk + NGINX Proxy Manager (Container-Stack)
Documentation=file://$PROJECT_DIR/README.md
Wants=network-online.target
After=network-online.target
$([ "$RUNTIME" = docker ] && echo "Requires=docker.service" || true)
$([ "$RUNTIME" = docker ] && echo "After=docker.service" || true)
[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=$PROJECT_DIR
EnvironmentFile=$PROJECT_DIR/.env
TimeoutStartSec=0
ExecStart=/bin/sh -c '$compose_bin up -d'
ExecStartPost=$PROJECT_DIR/scripts/cmk-manage.sh start-all-sites
ExecStop=/bin/sh -c '$compose_bin down'
ExecReload=/bin/sh -c '$compose_bin up -d'
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable checkmk-stack.service
ok "Unit aktiviert. Steuerung: systemctl start|stop|status checkmk-stack"
}
install_quadlet_units() {
log "Richte Podman-Quadlet-Units ein (/etc/containers/systemd/) ..."
[ -d /etc/containers/systemd ] || mkdir -p /etc/containers/systemd
local f
for f in "$PROJECT_DIR"/systemd/quadlet/*.container; do
sed -e "s|__PROJECT_DIR__|$PROJECT_DIR|g" \
-e "s|__CMK_IMAGE__|${CMK_IMAGE_REPO:-checkmk/check-mk-raw}:${CMK_IMAGE_TAG:-2.3.0-latest}|g" \
-e "s|__NPM_IMAGE__|${NPM_IMAGE:-jc21/nginx-proxy-manager:2}|g" \
-e "s|__CMK_SITE_ID__|${CMK_SITE_ID:-cmk}|g" \
-e "s|__CMK_CONTAINER_NAME__|${CMK_CONTAINER_NAME:-checkmk}|g" \
-e "s|__NPM_CONTAINER_NAME__|${NPM_CONTAINER_NAME:-nginx-proxy-manager}|g" \
"$f" >"/etc/containers/systemd/$(basename "$f")"
done
systemctl daemon-reload
ok "Quadlet-Units geschrieben. Start: systemctl start checkmk nginx-proxy-manager"
log "Die Units starten durch 'WantedBy=multi-user.target' automatisch nach einem Reboot."
}
if [ -n "$WITH_SYSTEMD" ]; then
if [ -n "$WITH_QUADLET" ]; then
[ "$RUNTIME" = podman ] || die "--quadlet ist nur mit Podman moeglich."
install_quadlet_units
else
install_compose_unit
fi
fi
# ---------------------------------------------------------------------
# Firewall-Hinweise
# ---------------------------------------------------------------------
print_firewall_hint() {
if command -v firewall-cmd >/dev/null 2>&1 && firewall-cmd --state >/dev/null 2>&1; then
cat <<'EOF'
Firewall (firewalld) - empfohlene Regeln:
firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=https
firewall-cmd --permanent --add-port=8000/tcp # Agent-Receiver
# Admin-Oberflaeche des Proxy Managers nur aus dem Admin-Netz:
firewall-cmd --permanent --add-rich-rule='rule family=ipv4 source address=10.0.0.0/24 port port=81 protocol=tcp accept'
# Checkmk-Port 5000 NICHT nach aussen oeffnen (nur ueber den Proxy)
firewall-cmd --reload
EOF
elif command -v ufw >/dev/null 2>&1; then
cat <<'EOF'
Firewall (ufw) - empfohlene Regeln:
ufw allow 80/tcp
ufw allow 443/tcp
ufw allow 8000/tcp # Agent-Receiver
ufw allow from 10.0.0.0/24 to any port 81 proto tcp # Proxy-Manager-Admin
# Port 5000 (Checkmk direkt) bleibt zu - Zugriff nur ueber den Proxy
EOF
fi
}
print_firewall_hint
cat <<EOF
${C_BLD}Installation abgeschlossen.${C_RST}
Naechste Schritte:
1) Konfiguration anpassen:
\$EDITOR $PROJECT_DIR/.env (CMK_SITE_ID, CMK_PASSWORD, TZ ...)
2) Stack starten:
$PROJECT_DIR/scripts/cmk-manage.sh up
3) NGINX Proxy Manager oeffnen:
http://$(primary_ip):81 Erstlogin: admin@example.com / changeme
4) Verwaltung / Backup / Migration - ncurses-Oberflaeche:
$PROJECT_DIR/scripts/cmk-manage.sh gui
Alles Weitere steht in der README.md.
EOF
+1019
View File
File diff suppressed because it is too large Load Diff
+290
View File
@@ -0,0 +1,290 @@
#!/usr/bin/env bash
# =====================================================================
# common.sh - gemeinsame Funktionen fuer cmk-manage.sh
# Wird per "source" eingebunden, nicht direkt ausgefuehrt.
# =====================================================================
# ---------------------------------------------------------------------
# Ausgabe / Logging
# ---------------------------------------------------------------------
if [ -t 1 ] && [ -z "${NO_COLOR:-}" ]; then
C_RED=$'\033[31m'; C_GRN=$'\033[32m'; C_YEL=$'\033[33m'
C_BLU=$'\033[34m'; C_BLD=$'\033[1m'; C_RST=$'\033[0m'
else
C_RED=; C_GRN=; C_YEL=; C_BLU=; C_BLD=; C_RST=
fi
log() { printf '%s[ %s ]%s %s\n' "$C_BLU" "INFO" "$C_RST" "$*"; }
ok() { printf '%s[ OK ]%s %s\n' "$C_GRN" "$C_RST" "$*"; }
warn() { printf '%s[ WARN ]%s %s\n' "$C_YEL" "$C_RST" "$*" >&2; }
err() { printf '%s[FEHLER]%s %s\n' "$C_RED" "$C_RST" "$*" >&2; }
die() { err "$*"; exit 1; }
debug() { [ -n "${VERBOSE:-}" ] && printf ' > %s\n' "$*" >&2 || true; }
# ---------------------------------------------------------------------
# .env laden
# ---------------------------------------------------------------------
load_env() {
local envfile="${1:-$PROJECT_DIR/.env}"
if [ -f "$envfile" ]; then
set -a
# shellcheck disable=SC1090
. "$envfile"
set +a
ENV_FILE="$envfile"
else
warn "Keine .env gefunden ($envfile) - es gelten die Standardwerte."
ENV_FILE=""
fi
# Defaults
CMK_CONTAINER_NAME="${CMK_CONTAINER_NAME:-checkmk}"
NPM_CONTAINER_NAME="${NPM_CONTAINER_NAME:-nginx-proxy-manager}"
CMK_SITE_ID="${CMK_SITE_ID:-cmk}"
BACKUP_DIR="${BACKUP_DIR:-$PROJECT_DIR/backups}"
case "$BACKUP_DIR" in
/*) : ;;
*) BACKUP_DIR="$PROJECT_DIR/${BACKUP_DIR#./}" ;;
esac
}
# .env-Wert dauerhaft setzen
env_set() {
local key="$1" val="$2" f="${ENV_FILE:-$PROJECT_DIR/.env}"
[ -f "$f" ] || die "Keine .env vorhanden - kann '$key' nicht setzen."
if grep -qE "^[#[:space:]]*${key}=" "$f"; then
sed -i -E "s|^[#[:space:]]*${key}=.*|${key}=${val}|" "$f"
else
printf '%s=%s\n' "$key" "$val" >>"$f"
fi
export "$key=$val"
}
# ---------------------------------------------------------------------
# Container-Runtime erkennen (docker / podman)
# ---------------------------------------------------------------------
detect_runtime() {
if [ -n "${CMK_RUNTIME:-}" ]; then
command -v "$CMK_RUNTIME" >/dev/null 2>&1 \
|| die "CMK_RUNTIME='$CMK_RUNTIME' ist nicht installiert."
RUNTIME="$CMK_RUNTIME"
elif command -v docker >/dev/null 2>&1 && docker info >/dev/null 2>&1; then
RUNTIME=docker
elif command -v podman >/dev/null 2>&1; then
RUNTIME=podman
elif command -v docker >/dev/null 2>&1; then
RUNTIME=docker # vorhanden, aber Daemon evtl. gestoppt
else
die "Weder docker noch podman gefunden. Bitte zuerst ./install.sh ausfuehren."
fi
debug "Runtime: $RUNTIME"
}
# Compose-Kommando ermitteln (als Array COMPOSE_CMD)
detect_compose() {
COMPOSE_CMD=()
if [ "$RUNTIME" = docker ]; then
if docker compose version >/dev/null 2>&1; then
COMPOSE_CMD=(docker compose)
elif command -v docker-compose >/dev/null 2>&1; then
COMPOSE_CMD=(docker-compose)
fi
else
if command -v podman-compose >/dev/null 2>&1; then
COMPOSE_CMD=(podman-compose)
elif podman compose version >/dev/null 2>&1; then
COMPOSE_CMD=(podman compose)
fi
fi
[ ${#COMPOSE_CMD[@]} -gt 0 ] || die "Kein Compose-Kommando gefunden (docker compose / podman-compose)."
debug "Compose: ${COMPOSE_CMD[*]}"
}
compose() {
detect_compose
( cd "$PROJECT_DIR" && "${COMPOSE_CMD[@]}" "$@" )
}
# ---------------------------------------------------------------------
# Container-Helfer
# ---------------------------------------------------------------------
container_exists() { $RUNTIME container inspect "$1" >/dev/null 2>&1; }
container_running() { [ "$($RUNTIME container inspect -f '{{.State.Running}}' "$1" 2>/dev/null)" = "true" ]; }
require_cmk_running() {
container_exists "$CMK_CONTAINER_NAME" \
|| die "Container '$CMK_CONTAINER_NAME' existiert nicht. Zuerst: $0 up"
container_running "$CMK_CONTAINER_NAME" \
|| die "Container '$CMK_CONTAINER_NAME' laeuft nicht. Zuerst: $0 up"
}
# Kommando im Checkmk-Container als root ausfuehren
ct() { $RUNTIME exec "$CMK_CONTAINER_NAME" "$@"; }
# ... mit stdin
cti() { $RUNTIME exec -i "$CMK_CONTAINER_NAME" "$@"; }
# ---------------------------------------------------------------------
# OMD-Helfer (im Container)
# ---------------------------------------------------------------------
cmk_sites() {
ct sh -c 'ls -1 /omd/sites 2>/dev/null' | sed '/^$/d' || true
}
cmk_site_exists() {
cmk_sites | grep -qxF "$1"
}
cmk_site_version() {
ct omd version -b "$1" 2>/dev/null | tr -d '\r' || true
}
# 0 = laeuft komplett, 1 = teilweise, 2 = gestoppt
cmk_site_state() {
local rc=0
ct omd status --bare "$1" >/dev/null 2>&1 || rc=$?
echo "$rc"
}
cmk_site_state_text() {
case "$(cmk_site_state "$1")" in
0) echo "laeuft" ;;
1) echo "teilweise" ;;
*) echo "gestoppt" ;;
esac
}
cmk_versions_installed() {
ct omd versions -b 2>/dev/null | tr -d '\r' || ct sh -c 'ls -1 /omd/versions | grep -v "^default$"' 2>/dev/null || true
}
# ---------------------------------------------------------------------
# Diverses
# ---------------------------------------------------------------------
human_size() {
local b="${1:-0}"
if [ "$b" -ge 1073741824 ] 2>/dev/null; then awk -v b="$b" 'BEGIN{printf "%.1f GiB", b/1073741824}'
elif [ "$b" -ge 1048576 ] 2>/dev/null; then awk -v b="$b" 'BEGIN{printf "%.1f MiB", b/1048576}'
elif [ "$b" -ge 1024 ] 2>/dev/null; then awk -v b="$b" 'BEGIN{printf "%.1f KiB", b/1024}'
else echo "${b} B"; fi
}
file_size() { stat -c %s "$1" 2>/dev/null || echo 0; }
confirm() {
[ -n "${ASSUME_YES:-}" ] && return 0
local prompt="${1:-Fortfahren?} [j/N] " a
read -r -p "$prompt" a </dev/tty || return 1
case "$a" in [jJyY]*) return 0 ;; *) return 1 ;; esac
}
primary_ip() {
ip -4 route get 1.1.1.1 2>/dev/null | awk '{for(i=1;i<=NF;i++) if($i=="src"){print $(i+1);exit}}' \
|| hostname -I 2>/dev/null | awk '{print $1}'
}
# =====================================================================
# TUI-Helfer (dialog bevorzugt, sonst whiptail) - ncurses wie im mc
# =====================================================================
UI_BACKTITLE="Checkmk Stack Manager - Container-Runtime: ${RUNTIME:-?}"
ui_init() {
[ -n "${UI_BIN:-}" ] && return 0
if command -v dialog >/dev/null 2>&1; then UI_BIN=dialog
elif command -v whiptail >/dev/null 2>&1; then UI_BIN=whiptail
else UI_BIN=""; fi
UI_H="${UI_H:-22}"; UI_W="${UI_W:-78}"; UI_MH="${UI_MH:-13}"
export DIALOGRC="${DIALOGRC:-}"
}
ui_available() { ui_init; [ -n "$UI_BIN" ]; }
ui_require() {
ui_available || die "Fuer die TUI wird 'dialog' oder 'whiptail' benoetigt.
Debian/Ubuntu : apt-get install -y dialog
RHEL/Rocky : dnf install -y dialog"
UI_BACKTITLE="Checkmk Stack Manager - Runtime: $RUNTIME"
}
ui_menu() { # title text tag item [tag item ...] -> gewaehlter tag auf stdout
local title="$1" text="$2"; shift 2
"$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$title" \
--menu "$text" "$UI_H" "$UI_W" "$UI_MH" "$@" 3>&1 1>&2 2>&3
}
ui_checklist() { # title text tag item status ...
local title="$1" text="$2"; shift 2
"$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$title" \
--checklist "$text" "$UI_H" "$UI_W" "$UI_MH" "$@" 3>&1 1>&2 2>&3
}
ui_radiolist() {
local title="$1" text="$2"; shift 2
"$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$title" \
--radiolist "$text" "$UI_H" "$UI_W" "$UI_MH" "$@" 3>&1 1>&2 2>&3
}
ui_input() { # title text default
"$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$1" \
--inputbox "$2" 10 "$UI_W" "${3:-}" 3>&1 1>&2 2>&3
}
ui_password() {
"$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$1" \
--passwordbox "$2" 10 "$UI_W" 3>&1 1>&2 2>&3
}
ui_msg() { # title text
"$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$1" --msgbox "$2" 16 "$UI_W"
}
ui_yesno() { # title text -> exit 0 = ja
"$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$1" --yesno "$2" 14 "$UI_W"
}
ui_text() { # title file
"$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$1" --textbox "$2" "$UI_H" "$UI_W"
}
ui_info() { # title text (ohne OK-Button)
"$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$1" --infobox "$2" 8 "$UI_W"
}
# Kommando ausfuehren, Ausgabe live in Datei, danach in Textbox anzeigen
ui_run() { # title cmd...
local title="$1"; shift
local logf; logf="$(mktemp)"
ui_info "$title" "Bitte warten ...\n\n$*"
if "$@" >"$logf" 2>&1; then
ui_text "$title - erfolgreich" "$logf"
rm -f "$logf"; return 0
else
ui_text "$title - FEHLGESCHLAGEN" "$logf"
rm -f "$logf"; return 1
fi
}
# ---------------------------------------------------------------------
# Dateiauswahl im Midnight-Commander-Stil (funktioniert auch mit whiptail)
# ---------------------------------------------------------------------
ui_pick_file() { # startdir [glob] -> Dateipfad auf stdout
local dir="${1:-$PWD}" glob="${2:-*}" sel
dir="$(cd "$dir" 2>/dev/null && pwd || echo "$PWD")"
while :; do
local -a items=()
items+=(".." "<Verzeichnis hoeher>")
local d f
while IFS= read -r d; do
[ -n "$d" ] && items+=("$d/" "<DIR>")
done < <(find "$dir" -mindepth 1 -maxdepth 1 -type d -printf '%f\n' 2>/dev/null | sort)
while IFS= read -r f; do
[ -n "$f" ] || continue
items+=("$f" "$(human_size "$(file_size "$dir/$f")")")
done < <(find "$dir" -mindepth 1 -maxdepth 1 -type f -name "$glob" -printf '%f\n' 2>/dev/null | sort)
sel="$(ui_menu "Datei waehlen" "Verzeichnis: $dir\nFilter: $glob" "${items[@]}")" || return 1
case "$sel" in
"..") dir="$(dirname "$dir")" ;;
*/) dir="$dir/${sel%/}" ;;
*) printf '%s\n' "$dir/$sel"; return 0 ;;
esac
done
}
+378
View File
@@ -0,0 +1,378 @@
#!/usr/bin/env bash
# =====================================================================
# migrate_creator.sh
#
# Wird auf den QUELLSERVER kopiert (klassische Checkmk-/OMD-Direkt-
# installation auf Bare-Metal oder VM) und dort als root ausgefuehrt.
#
# Aufgabe: eine oder mehrere Sites auswaehlen und daraus ein Migrations-
# Bundle (.tar.gz) erzeugen, das auf dem Zielsystem mit
# ./scripts/cmk-manage.sh import --file <bundle>
# in den Checkmk-Container uebernommen wird.
#
# Die Agents muessen dabei NICHT neu eingerichtet werden, solange
# * der Site-Name unveraendert bleibt und
# * das Zielsystem dieselbe IP-Adresse / denselben DNS-Namen bekommt.
# Die Site-Zertifikate (Agent-Registrierung) sind im omd-Backup enthalten.
#
# Dieses Skript ist bewusst eigenstaendig - es hat ausser bash, tar und
# omd keine Abhaengigkeiten. Fuer die Oberflaeche wird dialog oder
# whiptail genutzt, falls vorhanden.
# =====================================================================
set -euo pipefail
VERSION="1.0"
BUNDLE_DIRNAME="cmkbundle"
OUT_DIR="${PWD}"
declare -a SITES=()
ALL=""; STOP_SITE=""; ASSUME_YES=""; USE_GUI=""; SCP_TARGET=""; LABEL=""
declare -a OMD_OPTS=()
if [ -t 1 ] && [ -z "${NO_COLOR:-}" ]; then
C_RED=$'\033[31m'; C_GRN=$'\033[32m'; C_YEL=$'\033[33m'
C_BLU=$'\033[34m'; C_BLD=$'\033[1m'; C_RST=$'\033[0m'
else
C_RED=; C_GRN=; C_YEL=; C_BLU=; C_BLD=; C_RST=
fi
log() { printf '%s[ INFO ]%s %s\n' "$C_BLU" "$C_RST" "$*"; }
ok() { printf '%s[ OK ]%s %s\n' "$C_GRN" "$C_RST" "$*"; }
warn() { printf '%s[ WARN ]%s %s\n' "$C_YEL" "$C_RST" "$*" >&2; }
err() { printf '%s[FEHLER]%s %s\n' "$C_RED" "$C_RST" "$*" >&2; }
die() { err "$*"; exit 1; }
usage() {
cat <<'EOF'
migrate_creator.sh - Migrations-Bundle einer Checkmk-Direktinstallation erzeugen
AUFRUF
migrate_creator.sh [Optionen]
OPTIONEN
-l, --list vorhandene Sites anzeigen und beenden
-s, --site SITE Site auswaehlen (mehrfach angebbar)
-a, --all alle Sites sichern
-o, --out VERZEICHNIS Zielverzeichnis fuer die .tar.gz-Bundles (Vorgabe: aktuelles)
--label TEXT Freitext-Kennzeichnung im Dateinamen
--no-rrds Performance-Daten (RRDs) auslassen -> kleineres Archiv
--no-logs Logdateien auslassen
--stop Site waehrend des Backups stoppen (konsistenteste Variante)
--scp ZIEL Bundle anschliessend per scp uebertragen,
z.B. --scp root@10.0.0.5:/opt/checkmk-stack/backups/
-g, --gui ncurses-Oberflaeche (dialog/whiptail)
-y, --yes keine Rueckfragen
-h, --help diese Hilfe
BEISPIELE
./migrate_creator.sh --list
./migrate_creator.sh --gui
./migrate_creator.sh --site prod --stop --out /var/tmp
./migrate_creator.sh --all --no-logs --scp root@10.0.0.5:/opt/checkmk-stack/backups/
EOF
}
# ---------------------------------------------------------------------
# Hilfsfunktionen
# ---------------------------------------------------------------------
human_size() {
local b="${1:-0}"
if [ "$b" -ge 1073741824 ] 2>/dev/null; then awk -v b="$b" 'BEGIN{printf "%.1f GiB", b/1073741824}'
elif [ "$b" -ge 1048576 ] 2>/dev/null; then awk -v b="$b" 'BEGIN{printf "%.1f MiB", b/1048576}'
elif [ "$b" -ge 1024 ] 2>/dev/null; then awk -v b="$b" 'BEGIN{printf "%.1f KiB", b/1024}'
else echo "${b} B"; fi
}
file_size() { stat -c %s "$1" 2>/dev/null || echo 0; }
confirm() {
[ -n "$ASSUME_YES" ] && return 0
local a; read -r -p "${1:-Fortfahren?} [j/N] " a </dev/tty || return 1
case "$a" in [jJyY]*) return 0 ;; *) return 1 ;; esac
}
primary_ip() {
ip -4 route get 1.1.1.1 2>/dev/null | awk '{for(i=1;i<=NF;i++) if($i=="src"){print $(i+1);exit}}' \
|| hostname -I 2>/dev/null | awk '{print $1}'
}
all_ips() { hostname -I 2>/dev/null | tr ' ' '\n' | sed '/^$/d' | paste -sd, - ; }
list_sites() { ls -1 /omd/sites 2>/dev/null | sed '/^$/d'; }
site_version(){ omd version -b "$1" 2>/dev/null | tr -d '\r'; }
site_state() { local rc=0; omd status --bare "$1" >/dev/null 2>&1 || rc=$?
case "$rc" in 0) echo laeuft ;; 1) echo teilweise ;; *) echo gestoppt ;; esac; }
site_size() { du -sb "/omd/sites/$1" 2>/dev/null | awk '{print $1}'; }
# ---------------------------------------------------------------------
# TUI-Helfer
# ---------------------------------------------------------------------
ui_init() {
if command -v dialog >/dev/null 2>&1; then UI_BIN=dialog
elif command -v whiptail >/dev/null 2>&1; then UI_BIN=whiptail
else UI_BIN=""; fi
UI_BACKTITLE="migrate_creator $VERSION - $(hostname)"
}
ui_menu() { local t="$1" x="$2"; shift 2; "$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$t" --menu "$x" 20 76 11 "$@" 3>&1 1>&2 2>&3; }
ui_checklist() { local t="$1" x="$2"; shift 2; "$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$t" --checklist "$x" 20 76 11 "$@" 3>&1 1>&2 2>&3; }
ui_input() { "$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$1" --inputbox "$2" 10 76 "${3:-}" 3>&1 1>&2 2>&3; }
ui_msg() { "$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$1" --msgbox "$2" 18 76; }
ui_yesno() { "$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$1" --yesno "$2" 14 76; }
ui_text() { "$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$1" --textbox "$2" 22 78; }
ui_info() { "$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$1" --infobox "$2" 8 76; }
# ---------------------------------------------------------------------
# Bundle erzeugen
# ---------------------------------------------------------------------
create_bundle() {
local site="$1"
local version state ts outfile tmp restarted="" sum size
version="$(site_version "$site")"
[ -n "$version" ] || die "Konnte die Version der Site '$site' nicht ermitteln."
state="$(site_state "$site")"
ts="$(date +%Y%m%d-%H%M%S)"
outfile="$OUT_DIR/cmkmigrate_${site}_${version}${LABEL:+_$LABEL}_${ts}.tar.gz"
log "Site '$site' Version $version Status $state Groesse $(human_size "$(site_size "$site")")"
if [ -n "$STOP_SITE" ] && [ "$state" != "gestoppt" ]; then
log "Stoppe Site '$site' ..."
omd stop "$site" >/dev/null
restarted=1
fi
tmp="$(mktemp -d)"
mkdir -p "$tmp/$BUNDLE_DIRNAME/siteinfo"
log "Erzeuge omd-Backup ${OMD_OPTS[*]+(${OMD_OPTS[*]})} - das kann dauern ..."
if ! omd backup ${OMD_OPTS[@]+"${OMD_OPTS[@]}"} "$site" "$tmp/$BUNDLE_DIRNAME/site.tar.gz"; then
[ -n "$restarted" ] && omd start "$site" >/dev/null || true
rm -rf "$tmp"
die "omd backup der Site '$site' ist fehlgeschlagen."
fi
if [ -n "$restarted" ]; then
log "Starte Site '$site' wieder ..."
omd start "$site" >/dev/null || warn "Site '$site' konnte nicht wieder gestartet werden."
fi
# --- Zusatzinformationen (rein informativ, fuer die Nachkontrolle) ---
omd config "$site" show >"$tmp/$BUNDLE_DIRNAME/siteinfo/omd-config.txt" 2>/dev/null || true
omd status --bare "$site" >"$tmp/$BUNDLE_DIRNAME/siteinfo/omd-status.txt" 2>/dev/null || true
ip -4 addr show >"$tmp/$BUNDLE_DIRNAME/siteinfo/ip-addr.txt" 2>/dev/null || true
{
echo "hostname : $(hostname -f 2>/dev/null || hostname)"
echo "ips : $(all_ips)"
echo "kernel : $(uname -r)"
[ -r /etc/os-release ] && (. /etc/os-release && echo "os : $PRETTY_NAME")
echo "omd sites:"; omd sites 2>/dev/null || true
} >"$tmp/$BUNDLE_DIRNAME/siteinfo/system.txt"
# Anzahl registrierter Agent-Verbindungen (nur zur Kontrolle nach dem Import)
if [ -d "/omd/sites/$site/var/agent-receiver" ]; then
find "/omd/sites/$site/var/agent-receiver" -maxdepth 2 -type d 2>/dev/null \
| wc -l >"$tmp/$BUNDLE_DIRNAME/siteinfo/agent-receiver-entries.txt" || true
fi
sum="$(sha256sum "$tmp/$BUNDLE_DIRNAME/site.tar.gz" | awk '{print $1}')"
size="$(file_size "$tmp/$BUNDLE_DIRNAME/site.tar.gz")"
printf '%s site.tar.gz\n' "$sum" >"$tmp/$BUNDLE_DIRNAME/site.tar.gz.sha256"
cat >"$tmp/$BUNDLE_DIRNAME/MANIFEST" <<EOF
BUNDLE_VERSION=1
BUNDLE_TYPE=migrate
SITE=$site
CMK_VERSION=$version
SOURCE_KIND=baremetal
SOURCE_HOST=$(hostname -f 2>/dev/null || hostname)
SOURCE_IP=$(primary_ip)
SOURCE_IPS=$(all_ips)
SOURCE_OS=$( [ -r /etc/os-release ] && (. /etc/os-release && echo "$PRETTY_NAME") || echo unbekannt )
CREATED=$(date -Is)
CREATED_BY=migrate_creator.sh/$VERSION
ARCHIVE=site.tar.gz
ARCHIVE_SHA256=$sum
ARCHIVE_SIZE=$size
OMD_BACKUP_OPTS=${OMD_OPTS[*]-}
LABEL=$LABEL
SITE_STOPPED_DURING_BACKUP=$([ -n "$STOP_SITE" ] && echo yes || echo no)
AGENT_RECEIVER_PORT=8000
EOF
cat >"$tmp/$BUNDLE_DIRNAME/INFO.txt" <<EOF
Checkmk Migrations-Bundle
=========================
Site : $site
Checkmk-Version : $version
Quellserver : $(hostname -f 2>/dev/null || hostname)
Quell-IP(s) : $(all_ips)
Erstellt : $(date -Is)
Nutzdaten : site.tar.gz ($(human_size "$size"))
SHA256 : $sum
Import auf dem Zielsystem (Container-Installation):
1) Bundle in das Verzeichnis ./backups des Projekts kopieren
2) In der .env die passende Checkmk-Version eintragen:
CMK_IMAGE_TAG=${version%.*}
und den Stack neu erstellen: ./scripts/cmk-manage.sh up
3) Import ausfuehren:
./scripts/cmk-manage.sh import --file backups/$(basename "$outfile") --set-default-site
Damit die vorhandenen Agents ohne Neuinstallation weiterarbeiten:
* Der Site-Name muss "$site" bleiben (kein --as-site verwenden).
* Das Zielsystem muss die IP/den DNS-Namen des Quellservers uebernehmen.
* Port 8000 (Agent-Receiver) und 443/80 (Web) muessen erreichbar sein.
* Der alte Server sollte danach abgeschaltet bzw. dessen Site gestoppt werden.
EOF
log "Verpacke Bundle ..."
mkdir -p "$OUT_DIR"
tar -czf "$outfile" -C "$tmp" "$BUNDLE_DIRNAME"
rm -rf "$tmp"
ok "Bundle erstellt: $outfile ($(human_size "$(file_size "$outfile")"))"
LAST_BUNDLE="$outfile"
if [ -n "$SCP_TARGET" ]; then
log "Uebertrage per scp nach $SCP_TARGET ..."
scp "$outfile" "$SCP_TARGET" && ok "Uebertragung abgeschlossen." \
|| warn "scp fehlgeschlagen - bitte manuell kopieren."
fi
}
print_summary() {
cat <<EOF
${C_BLD}Weiteres Vorgehen${C_RST}
1) Bundle auf das Zielsystem kopieren, z.B.:
scp ${LAST_BUNDLE:-<bundle>} root@<ziel>:/opt/checkmk-stack/backups/
2) Dort importieren:
cd /opt/checkmk-stack
./scripts/cmk-manage.sh import --file backups/$(basename "${LAST_BUNDLE:-<bundle>}") --set-default-site
3) Quellserver: Site stoppen bzw. Server abschalten, damit es keine
doppelte Ueberwachung und keinen IP-Konflikt gibt:
omd stop <site> && omd disable ... / systemctl poweroff
4) Zielsystem auf die IP/den DNS-Namen des Quellservers umstellen -
dann melden sich die vorhandenen Agents unveraendert weiter.
EOF
}
# ---------------------------------------------------------------------
# GUI-Ablauf
# ---------------------------------------------------------------------
gui_main() {
ui_init
[ -n "$UI_BIN" ] || die "Fuer --gui wird 'dialog' oder 'whiptail' benoetigt.
Debian/Ubuntu : apt-get install -y dialog
RHEL/Rocky : dnf install -y dialog"
local -a items=() s
while IFS= read -r s; do
[ -n "$s" ] || continue
items+=("$s" "$(site_version "$s") | $(site_state "$s") | $(human_size "$(site_size "$s")")" off)
done < <(list_sites)
[ ${#items[@]} -gt 0 ] || die "Auf diesem System wurde keine OMD-Site gefunden."
ui_msg "Checkmk Migration" \
"Dieses Skript erzeugt aus einer Direktinstallation ein Migrations-Bundle
(.tar.gz) fuer den Import in eine Checkmk-Container-Installation.
Server : $(hostname -f 2>/dev/null || hostname)
IP(s) : $(all_ips)
Sites : $(list_sites | tr '\n' ' ')
Die Agents muessen spaeter NICHT neu eingerichtet werden, solange der
Site-Name gleich bleibt und das Zielsystem dieselbe IP uebernimmt."
local chosen
chosen="$(ui_checklist "Sites auswaehlen" "Mit LEERTASTE markieren, mit ENTER bestaetigen:" "${items[@]}")" || exit 0
[ -n "$chosen" ] || { ui_msg "Abbruch" "Es wurde keine Site ausgewaehlt."; exit 0; }
SITES=()
local x
for x in $(printf '%s' "$chosen" | tr -d '"'); do SITES+=("$x"); done
local opt
opt="$(ui_checklist "Optionen" "Optionen fuer das Backup:" \
stop "Site waehrend des Backups stoppen (empfohlen)" on \
no-rrds "Performance-Daten (RRD) auslassen" off \
no-logs "Logdateien auslassen" off)" || exit 0
case "$opt" in *stop*) STOP_SITE=1 ;; esac
case "$opt" in *no-rrds*) OMD_OPTS+=(--no-rrds) ;; esac
case "$opt" in *no-logs*) OMD_OPTS+=(--no-logs) ;; esac
OUT_DIR="$(ui_input "Zielverzeichnis" "Wohin sollen die Bundles geschrieben werden?" "${OUT_DIR}")" || exit 0
LABEL="$(ui_input "Kennzeichnung" "Optionaler Text fuer den Dateinamen (leer = keiner):" "")" || LABEL=""
LABEL="$(printf '%s' "$LABEL" | tr -cd 'A-Za-z0-9._-')"
if ui_yesno "Direkt uebertragen?" "Sollen die Bundles anschliessend per scp auf das Zielsystem kopiert werden?"; then
SCP_TARGET="$(ui_input "scp-Ziel" "Ziel angeben (Benutzer@Host:/Pfad/):" "root@zielserver:/opt/checkmk-stack/backups/")" || SCP_TARGET=""
fi
ui_yesno "Starten" "Jetzt Bundle(s) erzeugen fuer:\n\n ${SITES[*]}\n\nZiel: $OUT_DIR\n$([ -n "$STOP_SITE" ] && echo 'Die Site(s) werden dabei kurz gestoppt!')" || exit 0
local logf; logf="$(mktemp)"
clear
ASSUME_YES=1
local site
for site in "${SITES[@]}"; do
create_bundle "$site" 2>&1 | tee -a "$logf"
done
print_summary | tee -a "$logf" >/dev/null
ui_text "Fertig" "$logf"
rm -f "$logf"
print_summary
}
# ---------------------------------------------------------------------
# Argumente
# ---------------------------------------------------------------------
while [ $# -gt 0 ]; do
case "$1" in
-l|--list) LIST_ONLY=1; shift ;;
-s|--site) SITES+=("$2"); shift 2 ;;
-a|--all) ALL=1; shift ;;
-o|--out) OUT_DIR="$2"; shift 2 ;;
--label) LABEL="$2"; shift 2 ;;
--no-rrds) OMD_OPTS+=(--no-rrds); shift ;;
--no-logs) OMD_OPTS+=(--no-logs); shift ;;
--stop) STOP_SITE=1; shift ;;
--scp) SCP_TARGET="$2"; shift 2 ;;
-g|--gui) USE_GUI=1; shift ;;
-y|--yes) ASSUME_YES=1; shift ;;
-h|--help) usage; exit 0 ;;
*) die "Unbekannte Option: $1 (--help fuer Hilfe)" ;;
esac
done
# ---------------------------------------------------------------------
# Vorbedingungen
# ---------------------------------------------------------------------
command -v omd >/dev/null 2>&1 \
|| die "Auf diesem System ist keine OMD-/Checkmk-Direktinstallation gefunden worden ('omd' fehlt)."
[ "$(id -u)" -eq 0 ] || die "Bitte als root ausfuehren (sudo $0 ...)."
if [ -n "${LIST_ONLY:-}" ]; then
printf '%-20s %-18s %-12s %s\n' "SITE" "VERSION" "STATUS" "GROESSE"
while IFS= read -r s; do
[ -n "$s" ] || continue
printf '%-20s %-18s %-12s %s\n' "$s" "$(site_version "$s")" "$(site_state "$s")" "$(human_size "$(site_size "$s")")"
done < <(list_sites)
exit 0
fi
if [ -n "$USE_GUI" ] || { [ ${#SITES[@]} -eq 0 ] && [ -z "$ALL" ] && [ -t 0 ]; }; then
gui_main
exit 0
fi
[ -n "$ALL" ] && mapfile -t SITES < <(list_sites)
[ ${#SITES[@]} -gt 0 ] || die "Keine Site angegeben. --site SITE, --all oder --gui verwenden."
for s in "${SITES[@]}"; do
list_sites | grep -qxF "$s" || die "Site '$s' existiert auf diesem Server nicht."
done
printf '\n%sZu sichernde Sites:%s %s\n' "$C_BLD" "$C_RST" "${SITES[*]}"
printf '%sZielverzeichnis :%s %s\n\n' "$C_BLD" "$C_RST" "$OUT_DIR"
[ -n "$STOP_SITE" ] && warn "Die Site(s) werden waehrend des Backups gestoppt!"
confirm "Jetzt Migrations-Bundle(s) erzeugen?" || die "Abgebrochen."
for s in "${SITES[@]}"; do
create_bundle "$s"
done
print_summary
+33
View File
@@ -0,0 +1,33 @@
# =====================================================================
# Beispiel-Unit (wird von install.sh --with-systemd automatisch
# mit den korrekten Pfaden erzeugt und nach
# /etc/systemd/system/checkmk-stack.service geschrieben).
#
# Manuell:
# cp systemd/checkmk-stack.service.example \
# /etc/systemd/system/checkmk-stack.service
# # Pfade und Compose-Kommando anpassen
# systemctl daemon-reload && systemctl enable --now checkmk-stack
# =====================================================================
[Unit]
Description=Checkmk + NGINX Proxy Manager (Container-Stack)
Wants=network-online.target
After=network-online.target
# Nur bei Docker zusaetzlich:
#Requires=docker.service
#After=docker.service
[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/opt/checkmk-stack
EnvironmentFile=/opt/checkmk-stack/.env
TimeoutStartSec=0
# Docker: /usr/bin/docker compose up -d
# Podman: /usr/bin/podman-compose up -d
ExecStart=/bin/sh -c '/usr/bin/podman-compose up -d'
ExecStartPost=/opt/checkmk-stack/scripts/cmk-manage.sh start-all-sites
ExecStop=/bin/sh -c '/usr/bin/podman-compose down'
[Install]
WantedBy=multi-user.target
+30
View File
@@ -0,0 +1,30 @@
# =====================================================================
# Podman Quadlet Unit - Checkmk
# Ziel: /etc/containers/systemd/checkmk.container
# Wird von install.sh --quadlet mit den Werten aus der .env befuellt.
# Danach: systemctl daemon-reload && systemctl start checkmk
# Der Autostart nach einem Reboot ergibt sich aus [Install].
# =====================================================================
[Unit]
Description=Checkmk (OMD) im Container
Wants=network-online.target
After=network-online.target
[Container]
ContainerName=__CMK_CONTAINER_NAME__
Image=docker.io/__CMK_IMAGE__
Network=host
EnvironmentFile=__PROJECT_DIR__/.env
Environment=CMK_SITE_ID=__CMK_SITE_ID__
Volume=__PROJECT_DIR__/data/checkmk/sites:/omd/sites:Z
Volume=/etc/localtime:/etc/localtime:ro
PodmanArgs=--ulimit nofile=65536:65536
[Service]
Restart=always
TimeoutStartSec=300
# Alle vorhandenen Sites starten (der Entrypoint startet nur CMK_SITE_ID)
ExecStartPost=/bin/sh -c 'sleep 20; __PROJECT_DIR__/scripts/cmk-manage.sh start-all-sites || true'
[Install]
WantedBy=multi-user.target default.target
@@ -0,0 +1,24 @@
# =====================================================================
# Podman Quadlet Unit - NGINX Proxy Manager
# Ziel: /etc/containers/systemd/nginx-proxy-manager.container
# =====================================================================
[Unit]
Description=NGINX Proxy Manager (HTTPS-Terminierung fuer Checkmk)
Wants=network-online.target
After=network-online.target checkmk.service
[Container]
ContainerName=__NPM_CONTAINER_NAME__
Image=docker.io/__NPM_IMAGE__
Network=host
EnvironmentFile=__PROJECT_DIR__/.env
Environment=DISABLE_IPV6=true
Volume=__PROJECT_DIR__/data/npm/data:/data:Z
Volume=__PROJECT_DIR__/data/npm/letsencrypt:/etc/letsencrypt:Z
[Service]
Restart=always
TimeoutStartSec=180
[Install]
WantedBy=multi-user.target default.target