Files
docker-podman-installation-…/install.sh
T
duffyduckandClaude Opus 5 15369f829d RHEL ohne Subskription: Paketzugang dokumentiert und abgefangen
Auf einem frisch installierten RHEL liefert dnf ohne Registrierung gar
keine Pakete - weder podman noch git oder dialog. Das ist jetzt in
beiden READMEs als Kapitel 5.1 beschrieben, mit drei Wegen:

  1) kostenlose Developer Subscription for Individuals (bis 16 Systeme,
     kleine Produktivnutzung eingeschlossen). RHEL 9 nutzt Simple
     Content Access, ein "subscription-manager attach" entfaellt.
  2) Installations-DVD als lokales Repository - ohne Konto und offline,
     aber ohne Sicherheitsupdates, daher nur als Ueberbrueckung.
  3) Wechsel auf Rocky Linux oder AlmaLinux.

Dazu eine Tabelle, welche Pakete das Projekt wirklich braucht und aus
welchem Repository sie kommen, sowie der Hinweis, dass EPEL hier nicht
weiterhilft (es setzt die Basis-Repositories voraus) - in dem Fall ist
der Quadlet-Weg ohne podman-compose die Loesung.

install.sh:
  - rhel_repo_check prueft vor der Installation, ob ueberhaupt ein
    nutzbares Repository vorhanden ist, und nennt sonst die drei Wege,
    statt den nackten dnf-Fehler stehen zu lassen.
  - "dialog" ist nicht mehr zwingend: es liegt in AppStream, bei
    Fehlschlag wird "newt" (whiptail, BaseOS) versucht. Vorher brach die
    Installation ab, wenn dialog fehlte - obwohl die Skripte whiptail
    ohnehin unterstuetzen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-20 13:22:32 +02:00

356 lines
14 KiB
Bash
Executable File

#!/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)"
}
# Prueft, ob ueberhaupt ein nutzbares Repository vorhanden ist. Auf einem
# nicht registrierten RHEL ist das nicht der Fall - dnf meldet dann nur
# "This system is not registered with an entitlement server".
rhel_repo_check() {
local PM="$1"
$PM -q list --available podman >/dev/null 2>&1 && return 0
err "Es ist kein nutzbares Paket-Repository vorhanden."
cat >&2 <<'EOF'
Auf Red Hat Enterprise Linux liefert dnf ohne Subskription keine Pakete.
Drei Wege fuehren weiter (ausfuehrlich in der README, Kapitel 5.1):
1) Kostenlose Developer-Subskription (empfohlen, bis 16 Systeme):
Konto auf https://developers.redhat.com anlegen, dann
subscription-manager register --username <benutzer>
subscription-manager status
2) Installations-DVD als lokales Repository (ohne Konto, offline):
mount -o loop,ro /pfad/rhel-9.x-x86_64-dvd.iso /mnt/rhel
(Repo-Datei anlegen - siehe README, Kapitel 5.1)
3) Auf Rocky Linux oder AlmaLinux wechseln (binaerkompatibel, frei):
dieses Projekt laeuft dort unveraendert.
Pruefen, was aktiv ist: subscription-manager status ; dnf repolist
EOF
return 1
}
install_rhel() {
log "Installiere Podman ..."
local PM=dnf
command -v dnf >/dev/null 2>&1 || PM=yum
rhel_repo_check "$PM" || die "Abbruch - bitte zuerst den Paketzugang einrichten."
$PM install -y podman tar openssl || die "Podman-Installation fehlgeschlagen."
# dialog ist optional: liegt in AppStream. Fehlt es, genuegt whiptail
# aus dem Paket "newt" (BaseOS) - die Skripte koennen beides.
if ! $PM install -y dialog 2>/dev/null; then
warn "'dialog' nicht verfuegbar - versuche 'newt' (whiptail)."
$PM install -y newt 2>/dev/null \
|| warn "Weder dialog noch newt installierbar - die ncurses-Oberflaeche
steht dann nicht zur Verfuegung, die Kommandozeile funktioniert weiterhin."
fi
# 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/data/nginx/custom" \
"$PROJECT_DIR/data/npm/custom-certs" \
"$PROJECT_DIR/backups" \
"$PROJECT_DIR/images"
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:-docker.io/checkmk/check-mk-raw:2.3.0-latest}|g" \
-e "s|__NPM_IMAGE__|${NPM_IMAGE:-docker.io/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) Ist Port 81 im Netz gesperrt, laesst sich die Adminoberflaeche auf einen
anderen Port legen:
$PROJECT_DIR/scripts/cmk-manage.sh npm-port 10443
Oder ganz ohne Netzzugriff per Terminal-Browser auf dieser Maschine:
$PROJECT_DIR/scripts/cmk-manage.sh browser
5) Verwaltung / Backup / Migration - ncurses-Oberflaeche:
$PROJECT_DIR/scripts/cmk-manage.sh gui
Alles Weitere steht in der README.md.
EOF