deploy.sh: serialize concurrent deploys with flock; GC old app-image tags (#6)
This commit is contained in:
parent
e929f4606f
commit
e44d1603b6
2 changed files with 37 additions and 0 deletions
8
.gitignore
vendored
8
.gitignore
vendored
|
|
@ -14,3 +14,11 @@ age.key
|
||||||
*.age.key
|
*.age.key
|
||||||
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
|
# Host-side deploy state (deploy.sh): the live per-service image tags and the
|
||||||
|
# cross-repo deploy lock. Untracked on purpose -- they must survive the
|
||||||
|
# `git reset --hard` at the top of every deploy (deploy.sh's comments already
|
||||||
|
# assumed .image-tags.env was ignored; make it actually true so a stray
|
||||||
|
# `git clean` can't destroy the record of what's running).
|
||||||
|
deploy/.image-tags.env
|
||||||
|
deploy/.deploy.lock
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,21 @@ case "$SERVICE" in
|
||||||
*) echo "!! SERVICE must be backend|frontend|all, got '$SERVICE'" >&2; exit 2 ;;
|
*) echo "!! SERVICE must be backend|frontend|all, got '$SERVICE'" >&2; exit 2 ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
# Serialize deploys on this host. Backend and frontend deploy from SEPARATE
|
||||||
|
# repos whose workflows can fire for the same push within seconds of each
|
||||||
|
# other, and both SSH into this one checkout: concurrent runs race on the
|
||||||
|
# `git reset` below, the shared compose project, and the .image-tags.env
|
||||||
|
# read/modify/write (a lost update there re-rolls the sibling onto a stale
|
||||||
|
# tag). flock makes the second deploy wait its turn instead. The lock fd is
|
||||||
|
# inherited across the self re-exec below, so the lock spans the whole run;
|
||||||
|
# -w 600 bounds the wait (a deploy holding the lock >10 min is already
|
||||||
|
# broken), after which this exits non-zero and the CI job fails loudly.
|
||||||
|
DEPLOY_LOCK="$APP_DIR/deploy/.deploy.lock"
|
||||||
|
if [ -z "${DEPLOY_SH_FLOCKED:-}" ]; then
|
||||||
|
export DEPLOY_SH_FLOCKED=1
|
||||||
|
exec flock -w 600 "$DEPLOY_LOCK" "$0" "$@"
|
||||||
|
fi
|
||||||
|
|
||||||
# Secrets (POSTGRES_PASSWORD, VAPID keys, AUTH_SECRET, ...) drive compose
|
# Secrets (POSTGRES_PASSWORD, VAPID keys, AUTH_SECRET, ...) drive compose
|
||||||
# interpolation and are also loaded into the backend container via env_file.
|
# interpolation and are also loaded into the backend container via env_file.
|
||||||
#
|
#
|
||||||
|
|
@ -246,6 +261,20 @@ if [ "$health_ok" != 1 ]; then
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Every deploy pulls a new sha-tagged image and nothing ever removed the old
|
||||||
|
# ones -- hosts accumulate gigabytes of dead tags at one per app commit. Keep
|
||||||
|
# only the tags recorded as now-live (both services) and delete other tags of
|
||||||
|
# the two app-image repos. Best-effort and after the health gate, so a failed
|
||||||
|
# roll never garbage-collects the image a rollback would need; docker also
|
||||||
|
# refuses to remove an image any container still uses.
|
||||||
|
echo "==> Pruning old app-image tags"
|
||||||
|
_be_repo="${REGISTRY_HOST}/${BACKEND_IMAGE_PATH:-emi/thermograph-backend/app}"
|
||||||
|
_fe_repo="${REGISTRY_HOST}/${FRONTEND_IMAGE_PATH:-emi/thermograph-frontend/app}"
|
||||||
|
docker images --format '{{.Repository}}:{{.Tag}}' \
|
||||||
|
| grep -E "^(${_be_repo}|${_fe_repo}):" \
|
||||||
|
| grep -v -e "^${_be_repo}:${BACKEND_IMAGE_TAG}$" -e "^${_fe_repo}:${FRONTEND_IMAGE_TAG}$" \
|
||||||
|
| xargs -r docker rmi 2>/dev/null || true
|
||||||
|
|
||||||
# Post-deploy warm/IndexNow only make sense once the backend is (re)deployed --
|
# Post-deploy warm/IndexNow only make sense once the backend is (re)deployed --
|
||||||
# they exec inside the backend container. Skip them on a frontend-only roll.
|
# they exec inside the backend container. Skip them on a frontend-only roll.
|
||||||
if [ "$SERVICE" = backend ] || [ "$SERVICE" = all ]; then
|
if [ "$SERVICE" = backend ] || [ "$SERVICE" = all ]; then
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue