diff --git a/.gitea/workflows/deploy-env-dns.yml b/.gitea/workflows/deploy-env-dns.yml index 536d912..324f8a4 100644 --- a/.gitea/workflows/deploy-env-dns.yml +++ b/.gitea/workflows/deploy-env-dns.yml @@ -206,6 +206,73 @@ jobs: EOF scp /tmp/sync-fe-hosts.sh ${DEPLOY_USER}@${HOST}:${REMOTE_PATH}/.sync-fe-hosts.sh + # Script di attesa "running/healthy", usato sia da "Verifica container" + # dopo un deploy riuscito sia dal rollback prima di risincronizzare + # /etc/hosts: dopo un "up -d" (nuovo deploy o rollback) i container + # impiegano tempo a diventare healthy, quindi non basta lanciare + # "up -d" e proseguire subito - va aspettato che si stabilizzino, + # altrimenti (come nel caso del rollback) si rischia di leggere + # l'IP del FE prima che sia pronto o mentre sta ancora ripartendo. + cat > /tmp/wait-healthy.sh << 'EOF' + #!/bin/sh + set -e + COMPOSE="$1" + ENV_FILE="$2" + TIMEOUT="$3" + INTERVAL=5 + + ENVFLAG="" + [ -n "$ENV_FILE" ] && [ -f "$ENV_FILE" ] && ENVFLAG="--env-file $ENV_FILE" + + elapsed=0 + while true; do + ids=$(DOCKER_CONFIG=/tmp/.docker-ci docker compose -f "$COMPOSE" $ENVFLAG ps -q) + if [ -z "$ids" ]; then + echo "ERRORE: nessun container in esecuzione." + exit 1 + fi + fail=0 + pending=0 + for cid in $ids; do + svc=$(docker inspect -f '{{ index .Config.Labels "com.docker.compose.service" }}' "$cid" 2>/dev/null) + [ -z "$svc" ] && svc="$cid" + status=$(docker inspect -f '{{.State.Status}}' "$cid") + health=$(docker inspect -f '{{if .State.Health}}{{.State.Health.Status}}{{else}}n/a{{end}}' "$cid") + echo "Servizio $svc: status=$status health=$health" + if [ "$status" != "running" ]; then + echo "ERRORE: servizio $svc non e' in stato running (status=$status)." + fail=1 + elif [ "$health" = "unhealthy" ]; then + echo "ERRORE: servizio $svc risulta unhealthy." + fail=1 + elif [ "$health" = "starting" ]; then + pending=1 + fi + done + + if [ "$fail" -eq 1 ]; then + DOCKER_CONFIG=/tmp/.docker-ci docker compose -f "$COMPOSE" $ENVFLAG ps + exit 1 + fi + + if [ "$pending" -eq 0 ]; then + echo "Tutti i servizi sono running e (se previsto) healthy." + exit 0 + fi + + if [ "$elapsed" -ge "$TIMEOUT" ]; then + echo "ERRORE: timeout (${TIMEOUT}s) in attesa che tutti i servizi diventino healthy." + DOCKER_CONFIG=/tmp/.docker-ci docker compose -f "$COMPOSE" $ENVFLAG ps + exit 1 + fi + + echo "In attesa che i servizi diventino healthy... (${elapsed}s/${TIMEOUT}s)" + sleep "$INTERVAL" + elapsed=$((elapsed + INTERVAL)) + done + EOF + scp /tmp/wait-healthy.sh ${DEPLOY_USER}@${HOST}:${REMOTE_PATH}/.wait-healthy.sh + # ───────────────────────────────────────────────────────────── # APACHE: sostituiamo il file di config SOLO se è cambiato. # Il backup timestamped viene creato solo in caso di differenza. @@ -302,62 +369,9 @@ jobs: ssh ${DEPLOY_USER}@${HOST} "${CBASE} ps" # Attende che tutti i container siano "running" e, se hanno un - # healthcheck definito, "healthy" - non basta una fotografia subito - # dopo "up -d", che coglie quasi sempre i servizi ancora in - # "starting" (specie servizi Java/Spring con avvio lento). Fallisce - # subito se un servizio è "unhealthy" o non "running" (nessuna - # attesa inutile), altrimenti continua a interrogare fino a TIMEOUT. - ssh ${DEPLOY_USER}@${HOST} " - set -e - TIMEOUT=${TIMEOUT} - INTERVAL=5 - elapsed=0 - while true; do - ids=\$(${CBASE} ps -q) - if [ -z \"\$ids\" ]; then - echo 'ERRORE: nessun container in esecuzione dopo il deploy.' - exit 1 - fi - fail=0 - pending=0 - for cid in \$ids; do - svc=\$(docker inspect -f '{{ index .Config.Labels \"com.docker.compose.service\" }}' \$cid 2>/dev/null) - [ -z \"\$svc\" ] && svc=\$cid - status=\$(docker inspect -f '{{.State.Status}}' \$cid) - health=\$(docker inspect -f '{{if .State.Health}}{{.State.Health.Status}}{{else}}n/a{{end}}' \$cid) - if [ \"\$status\" != 'running' ]; then - echo \"ERRORE: servizio \$svc non e' in stato running (status=\$status).\" - fail=1 - elif [ \"\$health\" = 'unhealthy' ]; then - echo \"ERRORE: servizio \$svc risulta unhealthy.\" - fail=1 - elif [ \"\$health\" = 'starting' ]; then - pending=1 - fi - done - - if [ \"\$fail\" -eq 1 ]; then - echo '── Stato al momento del fallimento ──' - ${CBASE} ps - exit 1 - fi - - if [ \"\$pending\" -eq 0 ]; then - echo 'Tutti i servizi sono running e (se previsto) healthy.' - break - fi - - if [ \"\$elapsed\" -ge \"\$TIMEOUT\" ]; then - echo \"ERRORE: timeout (\${TIMEOUT}s) in attesa che tutti i servizi diventino healthy.\" - ${CBASE} ps - exit 1 - fi - - echo \"In attesa che i servizi diventino healthy... (\${elapsed}s/\${TIMEOUT}s)\" - sleep \$INTERVAL - elapsed=\$((elapsed + INTERVAL)) - done - " + # healthcheck definito, "healthy" (vedi .wait-healthy.sh, creato + # nello step "Prepara server" e condiviso con il rollback). + ssh ${DEPLOY_USER}@${HOST} "cd ${REMOTE_PATH} && sh .wait-healthy.sh ${COMPOSE} ${ENV_FILE} ${TIMEOUT}" echo "── Container status (finale) ──" ssh ${DEPLOY_USER}@${HOST} "${CBASE} ps" @@ -423,6 +437,7 @@ jobs: COMPOSE="docker-compose-${ENV_NAME}.yml" ENV_FILE=".env_${ENV_NAME}" SITE="${{ github.event.repository.name }}.site-ssl" + TIMEOUT="${{ inputs.health-timeout-seconds }}" ENVFLAG="" [ -f "$ENV_FILE" ] && ENVFLAG="--env-file ${ENV_FILE}" @@ -455,7 +470,12 @@ jobs: service apache2 reload " || apache_ok=1 - # 2) Rollback dei container alla versione precedente + # 2) Rollback dei container alla versione precedente, poi attesa che + # siano running/healthy (stesso script .wait-healthy.sh usato in + # "Verifica container"): "up -d" ritorna appena i container sono + # avviati, non quando sono pronti. Senza questa attesa, il punto 3) + # rischia di leggere l'IP del FE prima che sia stabile o mentre sta + # ancora ripartendo, risincronizzando /etc/hosts con un valore errato. container_ok=0 ssh ${DEPLOY_USER}@${HOST} " set -e @@ -467,6 +487,8 @@ jobs: [ -f .rollback/pin.yml ] && PIN='-f .rollback/pin.yml' echo 'Ripristino container alla versione precedente...' DOCKER_CONFIG=/tmp/.docker-ci docker compose -f ${COMPOSE} \$PIN ${ENVFLAG} up -d + echo 'Attendo che i container ripristinati siano running/healthy...' + sh .wait-healthy.sh ${COMPOSE} ${ENV_FILE} ${TIMEOUT} else echo 'Nessuno snapshot disponibile: nessun rollback container (probabile primo deploy).' fi