diff --git a/README.md b/README.md index b0f5e7e..9448af7 100644 --- a/README.md +++ b/README.md @@ -2549,6 +2549,8 @@ Eine Dateiauswahl mit Verzeichnisnavigation ist eingebaut. | `sites` | Sites im Container auflisten | | `site-start SITE` / `site-stop SITE` | einzelne Site steuern | | `start-all-sites` | alle Sites starten (wird von systemd aufgerufen) | +| `site-create NAME` | weitere Site im Container anlegen – eigene Ports (5001, 8001, …) | +| `site-delete NAME` | Site samt Daten entfernen | | `backup …` | Backup als `.tar.gz`-Bundle | | `list` / `verify` / `prune --keep N` | Backups verwalten | | `job list\|add\|set\|del\|run\|auto` | geplante Backups (eigene Zielpfade je Job) | @@ -3054,25 +3056,81 @@ Danach in der Oberfläche prüfen: ## 18. 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. +Der Entrypoint des Abbilds legt nur die Site aus `CMK_SITE_ID` an und startet +auch nur diese. Weitere Sites lassen sich aber anlegen – und mit dem Proxy +davor liegen sie nebeneinander unter einem einzigen Port: -Deshalb: +```bash +./scripts/cmk-manage.sh site-create zweite +./scripts/cmk-manage.sh site-create kunde2 --password geheim123 +./scripts/cmk-manage.sh sites +./scripts/cmk-manage.sh site-delete zweite +``` + +### 18.1 Eigene Ports je Site + +`omd` vergibt die Ports selbst weiter – daran hängt alles Weitere: + +| | erste Site | zweite | dritte | +|---|---|---|---| +| Oberfläche (Apache) | 5000 | 5001 | 5002 | +| **Agent-Receiver** | 8000 | **8001** | **8002** | +| Livestatus | 6557 | 6558 | 6559 | + +Zwei Dinge folgen daraus: + +1. **Im Proxy** bekommt jede Site ihren Pfad: + + ```bash + ./scripts/cmk-manage.sh proxy edit --domain --location /zweite=127.0.0.1:5001 + ./scripts/cmk-manage.sh proxy reload + ``` + + Danach liegt sie neben der ersten: `https:///cmk/` und + `https:///zweite/`. Checkmk kommt mit dem Präfix zurecht, weil der + Site-Name ohnehin Teil jeder Adresse ist – siehe + [Kapitel 10.12](#1012-weitere-dienste-dazunehmen--beispiel-grafana). + +2. **In der Firewall** sprechen die Agents dieser Site einen anderen Port an: + + ```bash + ./scripts/cmk-manage.sh config set FW_EXTRA_PORTS "8001/tcp=Agent-Receiver zweite" + sudo ./scripts/cmk-manage.sh firewall open --apply + ``` + +`site-create` gibt genau diese beiden Befehle mit den richtigen Portnummern +aus, dazu die Erinnerung an den Standard-Server für den IP-Zugang, falls er +eingerichtet ist. + +### 18.2 Was beim Anlegen passiert + +Im Container läuft `omd create --no-tmpfs` – ohne `--no-tmpfs` scheitert es, +weil sich im Container kein tmpfs einhängen lässt. Läuft der Container **nicht** +im Host-Netz, wird der Apache der Site zusätzlich auf `0.0.0.0` gebunden, sonst +wäre sie von außen nicht erreichbar; im Host-Netz genügt `127.0.0.1`, und der +Proxy kommt trotzdem hin. Ohne `--password` erzeugt `omd` eines und zeigt es an. + +### 18.3 Nach einem Neustart ```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. +Der Entrypoint startet nur die Standard-Site. Dieses Kommando wird von der +systemd-Unit (`ExecStartPost`) bzw. der Quadlet-Unit automatisch nach dem +Containerstart ausgeführt und fährt alle vorhandenen Sites hoch. -> **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. +### 18.4 Wann lieber getrennte Projekte? + +Mehrere Sites in einem Container teilen sich Abbild und Version – ein +`omd update` betrifft alle. Für sauber getrennte Umgebungen (unterschiedliche +Checkmk-Versionen, getrennte Wartungsfenster, verschiedene Kunden) ist je Site +ein eigenes Projektverzeichnis mit eigener `.env` besser. 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. + +In der Oberfläche liegt alles unter **Sites im Container verwalten**. --- diff --git a/README_en.md b/README_en.md index 036bc49..32834a6 100644 --- a/README_en.md +++ b/README_en.md @@ -2531,6 +2531,8 @@ A file chooser with directory navigation is built in. | `sites` | list the sites in the container | | `site-start SITE` / `site-stop SITE` | control a single site | | `start-all-sites` | start all sites (invoked by systemd) | +| `site-create NAME` | create another site in the container – own ports (5001, 8001, …) | +| `site-delete NAME` | remove a site and all its data | | `backup …` | backup as a `.tar.gz` bundle | | `list` / `verify` / `prune --keep N` | manage backups | | `job list\|add\|set\|del\|run\|auto` | scheduled backups (own target path per job) | @@ -3038,24 +3040,81 @@ Then check in the interface: ## 18. Several sites in one container -At startup the Checkmk entrypoint only creates – and only starts – the site -from `CMK_SITE_ID`. Further sites (e.g. several imported ones) exist in the -bind mount but are not brought up automatically after a restart. +The image's entrypoint creates only the site from `CMK_SITE_ID` and starts only +that one. Further sites can be created, though – and with the proxy in front +they sit side by side behind a single port: -Hence: +```bash +./scripts/cmk-manage.sh site-create zweite +./scripts/cmk-manage.sh site-create kunde2 --password secret123 +./scripts/cmk-manage.sh sites +./scripts/cmk-manage.sh site-delete zweite +``` + +### 18.1 Each site gets its own ports + +`omd` assigns the ports itself – everything else follows from that: + +| | first site | second | third | +|---|---|---|---| +| web UI (Apache) | 5000 | 5001 | 5002 | +| **agent receiver** | 8000 | **8001** | **8002** | +| Livestatus | 6557 | 6558 | 6559 | + +Two consequences: + +1. **In the proxy** every site gets its path: + + ```bash + ./scripts/cmk-manage.sh proxy edit --domain --location /zweite=127.0.0.1:5001 + ./scripts/cmk-manage.sh proxy reload + ``` + + It then sits next to the first one: `https:///cmk/` and + `https:///zweite/`. Checkmk copes with the prefix because the site name + is part of every address anyway – see + [chapter 10.12](#1012-adding-more-services--grafana-as-an-example). + +2. **In the firewall** the agents of that site talk to a different port: + + ```bash + ./scripts/cmk-manage.sh config set FW_EXTRA_PORTS "8001/tcp=Agent-Receiver zweite" + sudo ./scripts/cmk-manage.sh firewall open --apply + ``` + +`site-create` prints exactly those two commands with the right port numbers, +plus a reminder about the default server for IP access if it is set up. + +### 18.2 What happens during creation + +Inside the container `omd create --no-tmpfs` runs – without `--no-tmpfs` it +fails, because no tmpfs can be mounted there. If the container does **not** run +in the host network, the site's Apache is additionally bound to `0.0.0.0`, +otherwise it would not be reachable from outside; in the host network +`127.0.0.1` is enough and the proxy still reaches it. Without `--password`, +`omd` generates one and prints it. + +### 18.3 After a restart ```bash sudo ./scripts/cmk-manage.sh start-all-sites ``` -This command is executed automatically by the systemd unit (`ExecStartPost`) -or the Quadlet unit after the container starts, and starts every site present. +The entrypoint starts only the default site. This command is run automatically +by the systemd unit (`ExecStartPost`) or the Quadlet unit after the container +starts, and brings up all existing sites. -> **Recommendation:** for cleanly separated environments, run one project -> directory with its own `.env` per site. Note that because both containers run -> on the host network, only **one** Checkmk container per host can occupy ports -> 5000/8000 – for genuinely multiple instances use separate hosts or a -> bridge-network variant. +### 18.4 When separate projects are better + +Several sites in one container share the image and version – one `omd update` +affects all of them. For cleanly separated environments (different Checkmk +versions, separate maintenance windows, different customers) a separate project +directory with its own `.env` per site is better. Since both containers run in +the host network, only **one** Checkmk container per host can occupy ports +5000/8000 – for real multiple instances use separate hosts or a bridge-network +variant. + +The GUI has all of it under **Sites im Container verwalten**. --- diff --git a/scripts/cmk-manage.sh b/scripts/cmk-manage.sh index aa361bf..c85d0af 100755 --- a/scripts/cmk-manage.sh +++ b/scripts/cmk-manage.sh @@ -75,6 +75,8 @@ KOMMANDOS - Sites site-start SITE Site starten site-stop SITE Site stoppen start-all-sites alle vorhandenen Sites starten (fuer Autostart nach Reboot) + site-create NAME weitere Site im Container anlegen (eigene Ports) + site-delete NAME Site samt Daten entfernen KOMMANDOS - Backup / Restore / Migration backup [Optionen] Backup einer/aller Sites als .tar.gz-Bundle erzeugen @@ -1883,6 +1885,169 @@ cmd_site_stop() { # Wird vom systemd-Unit nach dem Start aufgerufen: alle Sites hochfahren. # (Der Container-Entrypoint startet nur CMK_SITE_ID.) +# ===================================================================== +# Weitere Sites anlegen und entfernen +# +# Der Entrypoint des Abbilds legt nur die Site aus CMK_SITE_ID an. Eine +# zweite entsteht mit "omd create" im Container - mit --no-tmpfs, denn +# ein tmpfs laesst sich dort nicht einhaengen. +# +# Die Ports vergibt omd selbst weiter: die zweite Site bekommt 5001 +# statt 5000, den Agent-Receiver auf 8001 statt 8000 und Livestatus auf +# 6558. Das ist beim Proxy und bei der Firewall zu beachten - die +# Agents dieser Site sprechen 8001 an, nicht 8000. +# ===================================================================== + +# Einen Wert aus der site.conf lesen +site_conf_get() { # site schluessel + ct sh -c "sed -n \"s/^CONFIG_$2='\\(.*\\)'\$/\\1/p\" /omd/sites/$1/etc/omd/site.conf" 2>/dev/null +} + +# Laeuft der Checkmk-Container im Host-Netz? Dann genuegt 127.0.0.1, +# denn der Proxy Manager liegt im selben Netz. +cmk_host_network() { + [ "$($RUNTIME container inspect -f '{{.HostConfig.NetworkMode}}' "$CMK_CONTAINER_NAME" 2>/dev/null)" = host ] +} + +cmd_site_create() { + local name="" pass="" expose="" nostart="" + while [ $# -gt 0 ]; do + case "$1" in + --password) pass="$2"; shift 2 ;; + --expose) expose=1; shift ;; + --no-start) nostart=1; shift ;; + --help|-h) +cat <" + case "$name" in + *[!a-zA-Z0-9_]*) die "Ein Site-Name darf nur Buchstaben, Ziffern und _ enthalten." ;; + esac + [ "${#name}" -le 16 ] || die "Ein Site-Name darf hoechstens 16 Zeichen lang sein." + + container_running "$CMK_CONTAINER_NAME" \ + || die "Der Container '$CMK_CONTAINER_NAME' laeuft nicht. + Starten: $SELF up" + + if ct test -d "/omd/sites/$name" 2>/dev/null; then + die "Die Site '$name' gibt es schon. ('$SELF sites' zeigt alle)" + fi + + local -a a=(omd create --no-tmpfs) + if [ -n "$pass" ]; then a+=(--admin-password "$pass"); fi + a+=("$name") + + log "Lege Site '$name' an ..." + # Die Ausgabe von omd enthaelt das erzeugte Kennwort - sie gehoert + # dem Benutzer vor die Augen. + ct "${a[@]}" || die "'omd create' ist fehlgeschlagen." + + # Ohne Host-Netz waere die Site von aussen nicht erreichbar + if [ -n "$expose" ] || ! cmk_host_network; then + log "Binde den Apache der Site auf 0.0.0.0 ..." + ct omd config "$name" set APACHE_TCP_ADDR 0.0.0.0 >/dev/null 2>&1 || true + fi + + if [ -z "$nostart" ]; then + log "Starte Site '$name' ..." + ct omd start "$name" >/dev/null 2>&1 || warn "Die Site liess sich nicht starten." + fi + + local port recv ls ip + port="$(site_conf_get "$name" APACHE_TCP_PORT)" + recv="$(site_conf_get "$name" AGENT_RECEIVER_PORT)" + ls="$(site_conf_get "$name" LIVESTATUS_TCP_PORT)" + ip="$(primary_ip)" + + echo + ok "Site '$name' angelegt." + printf ' %-22s %s\n' "Oberflaeche (direkt)" "http://127.0.0.1:${port:-?}/$name/" + printf ' %-22s %s\n' "Agent-Receiver" "${recv:-?}" + printf ' %-22s %s\n' "Livestatus (falls an)" "${ls:-?}" + cat < --location /$name=127.0.0.1:${port:-5001} + $SELF proxy reload + Danach: https://${ip:-}/$name/ + + 2) Die Agents dieser Site sprechen Port ${recv:-8001} an, nicht 8000. + In der Firewall ergaenzen: + $SELF config set FW_EXTRA_PORTS "${recv:-8001}/tcp=Agent-Receiver $name" + sudo $SELF firewall open --apply + + 3) Nach einem Neustart des Containers startet nur die Standard-Site. + Alle starten: $SELF start-all-sites +EOF + if grep -qsF "$NPM_DEF_START" "$(npm_custom_conf_path)" 2>/dev/null; then + log "Der Standard-Server fuer den IP-Zugang muss danach nachgezogen werden: + $SELF proxy default --domain --restart" + fi + echo + return 0 +} + +cmd_site_delete() { + local name="" force="" + while [ $# -gt 0 ]; do + case "$1" in + --force) force=1; shift ;; + -*) die "Unbekannte Option fuer 'site-delete': $1" ;; + *) name="$1"; shift ;; + esac + done + [ -n "$name" ] || die "Name fehlt: $SELF site-delete " + container_running "$CMK_CONTAINER_NAME" \ + || die "Der Container '$CMK_CONTAINER_NAME' laeuft nicht." + ct test -d "/omd/sites/$name" 2>/dev/null || die "Die Site '$name' gibt es nicht." + + if [ "$name" = "${CMK_SITE_ID:-cmk}" ] && [ -z "$force" ]; then + die "'$name' ist die Standard-Site aus CMK_SITE_ID. + Der Entrypoint legt sie beim naechsten Start ohnehin neu an. + Wenn es wirklich sein soll: $SELF site-delete $name --force" + fi + + warn "Die Site '$name' und ALLE ihre Daten werden entfernt." + printf ' Vorher ein Backup: %s backup --site %s\n' "$SELF" "$name" + confirm "Site '$name' wirklich loeschen?" || die "Abgebrochen." + + log "Stoppe und entferne Site '$name' ..." + ct omd stop "$name" >/dev/null 2>&1 || true + # "omd rm" fragt selbst nach; das globale -f ueberspringt die Rueckfrage. + # Ohne das bleibt die Site stehen, waehrend hier Erfolg gemeldet wuerde. + ct omd -f rm --kill "$name" >/dev/null 2>&1 || true + if ct test -d "/omd/sites/$name" 2>/dev/null; then + err "Die Site '$name' ist noch da - 'omd rm' hat nicht durchgegriffen." + log "Von Hand nachsehen: $SELF omd -f rm --kill $name" + return 1 + fi + ok "Site '$name' entfernt." + log "Einen Proxy-Pfad dafuer gibt es womoeglich noch: + $SELF proxy edit --domain --del-location /$name" + return 0 +} + cmd_start_all_sites() { local tries=0 while ! container_running "$CMK_CONTAINER_NAME"; do @@ -5604,16 +5769,51 @@ gui_backups_menu() { done } +gui_site_create() { + local name pass + name="$(ui_input "Weitere Site anlegen" \ +"Name der neuen Site (Buchstaben, Ziffern, _ - hoechstens 16 Zeichen). + +Sie bekommt eigene Ports: die naechste freie ab 5001 fuer die +Oberflaeche, ab 8001 fuer den Agent-Receiver. Beides muss danach im +Proxy bzw. in der Firewall nachgezogen werden - der Befehl sagt genau, +wie." "")" || return 0 + [ -n "$name" ] || return 0 + case "$name" in + *[!a-zA-Z0-9_]*) ui_msg "Ungueltig" "Erlaubt sind nur Buchstaben, Ziffern und _"; return 0 ;; + esac + if [ "${#name}" -gt 16 ]; then + ui_msg "Ungueltig" "Hoechstens 16 Zeichen."; return 0 + fi + + pass="$(ui_password "Weitere Site anlegen" "Kennwort fuer cmkadmin (leer = omd erzeugt eines):")" || return 0 + + local -a a=(site-create "$name") + if [ -n "$pass" ]; then a+=(--password "$pass"); fi + local f rc=0 + f="$(mktemp)" + ( NO_COLOR=1 cmd_site_create "${a[@]}" ) >"$f" 2>&1 || rc=$? + if [ "$rc" -eq 0 ]; then + ui_text "Site '$name' angelegt" "$f" + else + ui_text "Anlegen fehlgeschlagen" "$f" + fi + rm -f "$f" + return 0 +} + gui_sites_menu() { gui_need_container || return 0 while :; do local sel sel="$(ui_menu "Sites" "Sites im Container '$CMK_CONTAINER_NAME'" \ - list "Sites anzeigen" \ - start "Site starten" \ - stop "Site stoppen" \ - all "alle Sites starten" \ - back "zurueck")" || return 0 + list "Sites anzeigen" \ + neu "Weitere Site anlegen (eigene Ports)" \ + start "Site starten" \ + stop "Site stoppen" \ + all "alle Sites starten" \ + loesch "Site samt Daten entfernen" \ + back "zurueck")" || return 0 case "$sel" in list) local f; f="$(mktemp)"; ( cmd_sites ) >"$f" 2>&1 || true; ui_text "Sites" "$f"; rm -f "$f" ;; start) local s; s="$(gui_pick_site)" || continue; [ -n "$s" ] || continue @@ -5621,6 +5821,19 @@ gui_sites_menu() { stop) local s2; s2="$(gui_pick_site)" || continue; [ -n "$s2" ] || continue ui_run "Site stoppen" bash -c "cd '$PROJECT_DIR' && '$SELF' $SELF_ENV site-stop '$s2'" ;; all) ui_run "Alle Sites starten" bash -c "cd '$PROJECT_DIR' && '$SELF' $SELF_ENV start-all-sites" ;; + neu) gui_site_create || true ;; + loesch) local s3; s3="$(gui_pick_site)" || continue; [ -n "$s3" ] || continue + if [ "$s3" = "${CMK_SITE_ID:-cmk}" ]; then + ui_msg "Standard-Site" "'$s3' ist die Site aus CMK_SITE_ID. + +Der Entrypoint legt sie beim naechsten Start ohnehin neu an - +loeschen bringt hier nichts." + continue + fi + ui_yesno "Site entfernen" "Die Site '$s3' und ALLE ihre Daten entfernen? + +Vorher ein Backup: backup --site $s3" || continue + ui_run "Site entfernen" bash -c "cd '$PROJECT_DIR' && '$SELF' $SELF_ENV -y site-delete '$s3'" ;; back) return 0 ;; esac done @@ -7964,6 +8177,8 @@ main() { site-start) cmd_site_start "$@" ;; site-stop) cmd_site_stop "$@" ;; start-all-sites) cmd_start_all_sites ;; + site-create) cmd_site_create "$@" ;; + site-delete|site-rm) cmd_site_delete "$@" ;; backup) cmd_backup "$@" || true ;; list) cmd_list || true ;; prune) cmd_prune "$@" || true ;;