first commit
This commit is contained in:
@@ -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.
|
||||
@@ -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
@@ -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
|
||||
Executable
+1019
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
}
|
||||
Executable
+378
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user