Retry image pushes on transient registry failures

The engine images carry a ~5.6GB torch layer. Uploading it intermittently
fails with a 502 from the reverse proxy in front of the registry, and the
manifest PUT that follows can fail with a 500 because the blob commit has
not registered yet. Both clear on a retry, but the script pushed each tag
exactly once, so a whole release — including a 25GB rebuild — could be lost
to one hiccup.

push_image() replaces the bare `run docker push` calls and retries up to
PUSH_RETRIES times (default 5) with PUSH_RETRY_DELAY seconds between
attempts (default 10). Exhausting the retries still returns non-zero so
set -e aborts the release rather than reporting a partial push as success.
Dry runs make no docker calls.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-21 17:30:33 +01:00
parent 739c3ff30c
commit 3ab8a81c14
2 changed files with 49 additions and 6 deletions
+9
View File
@@ -101,6 +101,15 @@ Override the registry and org via environment variables:
REGISTRY=ghcr.io IMAGE_ORG=myorg ./release-engine.sh --github REGISTRY=ghcr.io IMAGE_ORG=myorg ./release-engine.sh --github
``` ```
Pushes are retried on transient registry failures. The engine images carry a
~5.6GB torch layer, and uploading it can fail with a 502 from the proxy in
front of the registry (or a 500 on the manifest PUT that follows), which
clears on a retry. Tune with:
```bash
PUSH_RETRIES=8 PUSH_RETRY_DELAY=20 ./release-engine.sh --gitea
```
## API reference ## API reference
All endpoints are under `/api/v1/`. Requires `Authorization: Bearer <key>` header when `KB_API_KEY` is set. All endpoints are under `/api/v1/`. Requires `Authorization: Bearer <key>` header when `KB_API_KEY` is set.
+40 -6
View File
@@ -23,6 +23,10 @@ REGISTRY="${REGISTRY:-docker.dcglab.co.uk}"
IMAGE_ORG="${IMAGE_ORG:-}" IMAGE_ORG="${IMAGE_ORG:-}"
IMAGE_BASE="${REGISTRY}${IMAGE_ORG:+/${IMAGE_ORG}}/kb" IMAGE_BASE="${REGISTRY}${IMAGE_ORG:+/${IMAGE_ORG}}/kb"
# Push retries — see push_image() below
PUSH_RETRIES="${PUSH_RETRIES:-5}"
PUSH_RETRY_DELAY="${PUSH_RETRY_DELAY:-10}"
#────────────────────────────────────────────────────────────────────── #──────────────────────────────────────────────────────────────────────
# Parse args # Parse args
#────────────────────────────────────────────────────────────────────── #──────────────────────────────────────────────────────────────────────
@@ -102,6 +106,36 @@ run() {
fi fi
} }
# Push one image tag, retrying on transient registry failures.
#
# The engine images carry a ~5.6GB torch layer. Uploading it intermittently
# fails with a 502 from the reverse proxy in front of the registry, and a
# manifest PUT can then fail with a 500 because the blob commit has not yet
# registered. Both clear on a retry, so a whole release should not be lost to
# one hiccup. Tune with PUSH_RETRIES / PUSH_RETRY_DELAY.
push_image() {
local image="$1"
local attempt=1
echo " $ docker push $image"
[[ "$DRY_RUN" == true ]] && return 0
while true; do
if docker push "$image"; then
return 0
fi
if (( attempt >= PUSH_RETRIES )); then
echo "Error: failed to push $image after $PUSH_RETRIES attempts" >&2
return 1
fi
echo " push failed (attempt $attempt/$PUSH_RETRIES) — retrying in ${PUSH_RETRY_DELAY}s"
sleep "$PUSH_RETRY_DELAY"
attempt=$(( attempt + 1 ))
done
}
#────────────────────────────────────────────────────────────────────── #──────────────────────────────────────────────────────────────────────
# Determine release version # Determine release version
#────────────────────────────────────────────────────────────────────── #──────────────────────────────────────────────────────────────────────
@@ -237,14 +271,14 @@ echo ""
#────────────────────────────────────────────────────────────────────── #──────────────────────────────────────────────────────────────────────
echo "==> Pushing Docker images to $REGISTRY" echo "==> Pushing Docker images to $REGISTRY"
run docker push "$NVIDIA_IMAGE" push_image "$NVIDIA_IMAGE"
run docker push "$NVIDIA_LATEST" push_image "$NVIDIA_LATEST"
run docker push "$CPU_IMAGE" push_image "$CPU_IMAGE"
run docker push "$CPU_LATEST" push_image "$CPU_LATEST"
if [[ -n "${MCP_IMAGE:-}" ]]; then if [[ -n "${MCP_IMAGE:-}" ]]; then
run docker push "$MCP_IMAGE" push_image "$MCP_IMAGE"
run docker push "$MCP_LATEST" push_image "$MCP_LATEST"
fi fi
echo "" echo ""