diff --git a/ops/README.md b/ops/README.md index c39ceef..eda26aa 100644 --- a/ops/README.md +++ b/ops/README.md @@ -1,6 +1,6 @@ # Supervision des applications -Le script `healthcheck-apps.sh` est exécuté par le timer systemd toutes les cinq minutes et trois minutes après chaque démarrage serveur. Il vérifie les conteneurs Docker Compose, leur healthcheck, leur politique de redémarrage et les routes HTTPS Traefik déclarées. En cas d’anomalie, une remise en route Compose ciblée est tentée une fois. +Le script `healthcheck-apps.sh` est exécuté par le timer systemd toutes les cinq minutes et trois minutes après chaque démarrage serveur. Il vérifie les conteneurs Docker Compose, leur healthcheck, leur politique de redémarrage et les routes HTTPS Traefik déclarées. En cas d’anomalie, il produit un rapport et une entrée de journal, **sans redémarrer de conteneur**. Les redémarrages automatiques restent assurés par les politiques Docker `restart` et par `manus-apps.service` au démarrage du serveur. Le dernier rapport est écrit dans `/var/lib/manus-apps-health/latest.txt`. Les anomalies sont aussi consignées dans le journal système avec le tag `manus-apps-health`. diff --git a/ops/healthcheck-apps.sh b/ops/healthcheck-apps.sh index fb5ecf1..df23c87 100644 --- a/ops/healthcheck-apps.sh +++ b/ops/healthcheck-apps.sh @@ -1,21 +1,18 @@ #!/usr/bin/env bash # Vérifie les conteneurs Compose, leur healthcheck, leur redémarrage et leurs routes Traefik. -# En cas d'écart, un redémarrage Compose ciblé est tenté une fois avant le prochain cycle. +# Ce contrôle est volontairement non intrusif : Docker et manus-apps.service assurent les redémarrages. set -Eeuo pipefail readonly STATE_DIR="/var/lib/manus-apps-health" readonly REPORT_FILE="${STATE_DIR}/latest.txt" readonly LOCK_FILE="/run/manus-apps-health.lock" -readonly MODE="${1:---repair}" +readonly MODE="${1:---check-only}" mkdir -p "$STATE_DIR" exec 9>"$LOCK_FILE" flock -n 9 || exit 0 -declare -A PROJECT_CONFIGS=() -declare -A PROJECT_NAMES=() declare -A SEEN_HTTP_HOSTS=() -declare -A FAILED_PROJECTS=() declare -a REPORT_LINES=() HAS_FAILURE=false @@ -32,11 +29,6 @@ mark_failure() { log_line "KO" "$1" } -mark_project_failure() { - FAILED_PROJECTS["$1"]=1 - mark_failure "$2" -} - is_healthy_http_status() { [[ "$1" =~ ^2[0-9][0-9]$ || "$1" =~ ^3[0-9][0-9]$ ]] } @@ -44,7 +36,6 @@ is_healthy_http_status() { check_http_route() { local container_id="$1" local project="$2" - local workdir="$3" local labels host_rule host status labels="$(docker inspect -f '{{range $key, $value := .Config.Labels}}{{$key}}={{$value}}{{"\n"}}{{end}}' "$container_id")" @@ -57,12 +48,14 @@ check_http_route() { [[ -z "${SEEN_HTTP_HOSTS[$host]:-}" ]] || return 0 SEEN_HTTP_HOSTS["$host"]=1 - status="$(curl --silent --show-error --location --max-redirs 3 --connect-timeout 10 --max-time 25 \ + status="$(curl --silent --show-error --location --max-redirs 3 --connect-timeout 5 --max-time 8 \ --output /dev/null --write-out '%{http_code}' "https://${host}" 2>/dev/null || true)" if is_healthy_http_status "$status"; then log_line "OK" "${project}: HTTPS ${host} → ${status}" else - mark_project_failure "$workdir" "${project}: HTTPS ${host} → ${status:-erreur réseau}" + # Un échec HTTPS peut dépendre de Traefik, DNS ou d’une redirection applicative. + # Il déclenche une alerte, jamais un redémarrage Compose automatique. + mark_failure "${project}: HTTPS ${host} → ${status:-erreur réseau}" fi } @@ -84,64 +77,26 @@ check_containers() { health="$(docker inspect -f '{{if .State.Health}}{{.State.Health.Status}}{{else}}none{{end}}' "$container_id")" restart_policy="$(docker inspect -f '{{.HostConfig.RestartPolicy.Name}}' "$container_id")" - PROJECT_NAMES["$workdir"]="$project" - PROJECT_CONFIGS["$workdir"]="$config_files" - if [[ "$status" != "running" ]]; then - mark_project_failure "$workdir" "${project}/${name}: état Docker ${status}" + mark_failure "${project}/${name}: état Docker ${status}" continue fi if [[ "$health" == "unhealthy" ]]; then - mark_project_failure "$workdir" "${project}/${name}: healthcheck unhealthy" + mark_failure "${project}/${name}: healthcheck unhealthy" continue fi if [[ "$restart_policy" != "unless-stopped" && "$restart_policy" != "always" ]]; then - mark_project_failure "$workdir" "${project}/${name}: politique de redémarrage ${restart_policy:-none}" + mark_failure "${project}/${name}: politique de redémarrage ${restart_policy:-none}" continue fi log_line "OK" "${project}/${name}: running, health=${health}, restart=${restart_policy}" - check_http_route "$container_id" "$project" "$workdir" - done -} - -repair_projects() { - local workdir config_files project config - for workdir in "${!FAILED_PROJECTS[@]}"; do - project="${PROJECT_NAMES[$workdir]}" - [[ -d "$workdir" ]] || continue - config_files="${PROJECT_CONFIGS[$workdir]}" - local -a compose_args=() - if [[ -n "$config_files" ]]; then - IFS=',' read -r -a configs <<< "$config_files" - for config in "${configs[@]}"; do - compose_args+=("-f" "$config") - done - elif [[ -f "$workdir/docker-compose.yml" ]]; then - compose_args+=("-f" "$workdir/docker-compose.yml") - else - mark_failure "${project}: fichier Compose introuvable pour la réparation" - continue - fi - - log_line "INFO" "${project}: tentative de réparation Compose" - if (cd "$workdir" && docker compose "${compose_args[@]}" up -d --remove-orphans); then - log_line "INFO" "${project}: réparation Compose demandée" - else - mark_failure "${project}: échec de réparation Compose" - fi + check_http_route "$container_id" "$project" done } check_containers -if [[ "$HAS_FAILURE" == true && "$MODE" == "--repair" ]]; then - repair_projects - log_line "INFO" "Nouvelle vérification après réparation" - sleep 15 - exec "$0" --check-only -fi - { echo "Supervision Santinova / Itinova" echo "Généré : $(date -Is)" diff --git a/ops/manus-apps-health.service b/ops/manus-apps-health.service index 77185ad..5fcbf92 100644 --- a/ops/manus-apps-health.service +++ b/ops/manus-apps-health.service @@ -1,5 +1,5 @@ [Unit] -Description=Contrôle et réparation des applications Santinova / Itinova +Description=Contrôle de santé des applications Santinova / Itinova Documentation=https://git.santinova-soft.org/manus-admin/manus-dashboard After=docker.service network-online.target manus-apps.service Requires=docker.service @@ -7,7 +7,7 @@ Wants=network-online.target [Service] Type=oneshot -ExecStart=/opt/manus-deploy/scripts/healthcheck-apps.sh --repair -TimeoutStartSec=10min +ExecStart=/opt/manus-deploy/scripts/healthcheck-apps.sh --check-only +TimeoutStartSec=5min StandardOutput=journal StandardError=journal