# Compose-Stack infra: Router, TTS, Postgres, Prometheus, Alertmanager, Grafana, pve-exporter, node-exporter. # Idempotent: Geheimnisse nur anlegen, wenn sie fehlen; compose up ändert nur, was sich geändert hat. - name: Werkzeuge ansible.builtin.apt: name: [rsync, openssl, curl] state: present update_cache: true cache_valid_time: 3600 - name: Verzeichnisse ansible.builtin.file: path: "{{ item.path }}" state: directory mode: "{{ item.mode }}" loop: - {path: /opt/knaster, mode: "0755"} - {path: "{{ infra_repo_dir }}", mode: "0755"} - {path: "{{ infra_state_dir }}", mode: "0700"} - {path: "{{ infra_state_dir }}/secrets", mode: "0700"} - {path: "{{ infra_state_dir }}/secrets/k3s", mode: "0755"} # Nur die Teile, die Build und Konfiguration brauchen. Nie .git, .venv, tofu-State oder .env-Dateien. - name: Repo-Auszug von ops abgleichen ansible.posix.synchronize: src: "{{ infra_repo_src }}/" dest: "{{ infra_repo_dir }}/" delete: true rsync_opts: # Der Clone auf ops ist teils 0600/0700; Container-Benutzer (nobody, grafana, knaster) müssen lesen können - "--chmod=go+rX,go-w" - "--exclude=__pycache__" - "--exclude=.pytest_cache" - "--exclude=*.egg-info" - "--exclude=.env" - "--exclude=secrets/" - "--include=/core/***" - "--include=/config/***" - "--include=/catalog/***" - "--include=/skills/***" - "--include=/ontology/***" # Router-Image kopiert ontology/ (KNASTER_ONTOLOGY) - "--include=/sites/***" - "--include=/deploy/***" - "--exclude=*" - name: Einstellungen in .env ansible.builtin.lineinfile: path: "{{ infra_state_dir }}/.env" regexp: "^{{ item.key }}=" line: "{{ item.key }}={{ item.value }}" create: true mode: "0600" loop: "{{ infra_settings | dict2items }}" # Gibt nur Namen aus, nie Werte - name: Fehlende Geheimnisse im Container erzeugen ansible.builtin.shell: | set -eu umask 077 f={{ infra_state_dir }}/.env for k in {{ infra_secrets | join(' ') }}; do grep -q "^$k=" "$f" || { echo "$k=$(openssl rand -hex 24)" >> "$f"; echo "neu: $k"; } done chmod 0600 "$f" args: executable: /bin/bash register: secrets_gen changed_when: "'neu:' in secrets_gen.stdout" - name: Router-Token für Alertmanager (= KNASTER_API_TOKEN, lesbar für nobody im Container) ansible.builtin.shell: | set -eu umask 077 cd {{ infra_state_dir }} v=$(sed -n 's/^KNASTER_API_TOKEN=//p' .env) t=secrets/router_token if [ ! -f "$t" ] || [ "$(cat "$t")" != "$v" ]; then printf '%s' "$v" > "$t"; echo geändert; fi chown 65534:65534 "$t" chmod 0400 "$t" args: executable: /bin/bash register: router_token changed_when: "'geändert' in router_token.stdout" no_log: true - name: Proxmox-Lese-Token für den pve-exporter ansible.builtin.copy: src: "{{ infra_pve_exporter_env }}" dest: "{{ infra_state_dir }}/secrets/pve-exporter.env" mode: "0600" no_log: true # Task 6.5: Token des ServiceAccounts prometheus-infra und CA des Clusters (von playbooks/monitoring-k3s.yml # auf ops abgelegt). Lesbar für nobody (Prometheus im Container). Fehlen sie, bleiben die k3s-Jobs down. - name: k3s-Zugang für Prometheus vorhanden? (auf ops) ansible.builtin.stat: path: "{{ item }}" loop: ["{{ infra_k3s_token }}", "{{ infra_k3s_ca }}"] delegate_to: localhost register: k3s_access - name: k3s-Zugang für Prometheus kopieren ansible.builtin.copy: src: "{{ item.src }}" dest: "{{ infra_state_dir }}/secrets/k3s/{{ item.dest }}" owner: "65534" group: "65534" mode: "0400" loop: - {src: "{{ infra_k3s_token }}", dest: token} - {src: "{{ infra_k3s_ca }}", dest: ca.crt} loop_control: label: "{{ item.dest }}" when: k3s_access.results | map(attribute='stat.exists') | min no_log: true - name: file_sd-Ziele ansible.builtin.include_tasks: targets.yml # HA-Umzug: Nach zfs receive liefert der Zielknoten für früher gemappte Dateien veralteten Seiten-Cache # (12.10.2026 belegt). Vor jedem Docker-Start den Cache dieses Containers verwerfen. BETRIEB.md Abschnitt 7. - name: Schutz gegen veralteten Seiten-Cache – Skript ansible.builtin.copy: src: knaster-drop-stale-cache.py dest: /usr/local/sbin/knaster-drop-stale-cache mode: "0755" - name: Schutz gegen veralteten Seiten-Cache – systemd-Dienst vor containerd/docker ansible.builtin.copy: src: knaster-drop-stale-cache.service dest: /etc/systemd/system/knaster-drop-stale-cache.service mode: "0644" register: dropcache_unit - name: Schutz gegen veralteten Seiten-Cache – aktivieren ansible.builtin.systemd: name: knaster-drop-stale-cache.service enabled: true daemon_reload: "{{ dropcache_unit.changed }}" - name: Stack bauen und starten ansible.builtin.command: > docker compose -p {{ infra_compose_project }} --env-file {{ infra_state_dir }}/.env -f {{ infra_compose_file }} up -d --build --remove-orphans register: compose_up changed_when: compose_up.stderr is search('Container \S+\s+(Created|Recreated|Started)') - name: Prometheus und Alertmanager laden geänderte Konfiguration (bind-mount, kein Neustart nötig) ansible.builtin.command: "curl -fsS -m 10 -X POST http://127.0.0.1:{{ item }}/-/reload" loop: [9090, 9093] register: reload retries: 15 delay: 4 until: reload.rc == 0 changed_when: false - name: Grafana-Konto kiosk (Viewer) anlegen, Passwort aus .env ansible.builtin.shell: | set -eu cd {{ infra_state_dir }} A=$(sed -n 's/^GRAFANA_ADMIN_PASSWORD=//p' .env) K=$(sed -n 's/^GRAFANA_KIOSK_PASSWORD=//p' .env) umask 077 cfg=$(mktemp) trap 'rm -f "$cfg"' EXIT printf 'user = "admin:%s"\n' "$A" > "$cfg" # Passwort nicht in der Prozessliste g() { curl -sS -K "$cfg" -H 'Content-Type: application/json' "$@"; } for i in $(seq 60); do curl -fsS -m 3 http://127.0.0.1:3000/api/health >/dev/null 2>&1 && break; sleep 2; done code=$(g -o /dev/null -w '%{http_code}' 'http://127.0.0.1:3000/api/users/lookup?loginOrEmail=kiosk') if [ "$code" = 404 ]; then printf '{"name":"Kiosk","login":"kiosk","password":"%s"}' "$K" \ | g -f -X POST --data @- http://127.0.0.1:3000/api/admin/users >/dev/null echo angelegt elif [ "$code" != 200 ]; then echo "Grafana-API antwortet $code" >&2 exit 1 fi id=$(g -f 'http://127.0.0.1:3000/api/users/lookup?loginOrEmail=kiosk' | python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])') g -f -X PATCH --data '{"role":"Viewer"}' "http://127.0.0.1:3000/api/org/users/$id" >/dev/null args: executable: /bin/bash register: kiosk changed_when: "'angelegt' in kiosk.stdout" no_log: true