fix(ci): 修复registry不稳定导致镜像构建失败 - 解耦缓存推送 #357

Merged
xiaoxia merged 1 commits from ci/registry-cache-stability-fix into develop 2026-07-15 12:55:13 +08:00
2 changed files with 149 additions and 56 deletions
+78 -56
View File
@@ -448,8 +448,20 @@ jobs:
run: "set -eu\n# 确保使用 docker-container driver 以支持 cache export 功能\nif ! docker buildx inspect ci-builder > /dev/null 2>&1; then\n docker buildx create --use --name ci-builder --driver docker-container\n echo \"Created ci-builder (docker-container driver)\"\nelse\n docker buildx use ci-builder\n echo \"Using existing ci-builder\"\nfi\ndocker buildx inspect --bootstrap\n"
- name: Build and push API image (buildx cache)
shell: sh
run: "set -eu\nREGISTRY=\"git.xiaoxiajianji.com/xiaoxia/xiaoxia-saas\"\nIMAGE_NAME=\"xiaoxia-saas-api\"\nCACHE_REF=\"${REGISTRY}/api-cache:develop\"\n\nCACHE_FROM=\"type=registry,ref=${CACHE_REF},ignore-error=true\"\n\nif [ \"${CACHE_MODE}\" = \"read-write\" ]; then\n CACHE_TO=\"type=registry,ref=${CACHE_REF},mode=max\"\n echo \"Building API image with read-write cache...\"\n docker buildx build --build-arg APP_VERSION=\"${GITHUB_SHA}\" --cache-from \"${CACHE_FROM}\" --cache-to \"${CACHE_TO}\" -f infra/docker/api.Dockerfile -t \"${REGISTRY}/${IMAGE_NAME}:${GITHUB_SHA}\" --push .\nelse\n echo \"Building API image with read-only cache...\"\n docker buildx build --build-arg APP_VERSION=\"${GITHUB_SHA}\" --cache-from \"${CACHE_FROM}\" -f infra/docker/api.Dockerfile -t \"${REGISTRY}/${IMAGE_NAME}:${GITHUB_SHA}\" --push .\nfi\necho\
\ \"API image pushed: ${REGISTRY}/${IMAGE_NAME}:${GITHUB_SHA}\"\n"
run: "set -eu
REGISTRY=\"git.xiaoxiajianji.com/xiaoxia/xiaoxia-saas\"
IMAGE_TAG=\"${REGISTRY}/xiaoxia-saas-api:${GITHUB_SHA}\"
CACHE_REF=\"${REGISTRY}/api-cache:${GITHUB_REF_NAME}\"
bash scripts/ci/docker_build_push.sh \
infra/docker/api.Dockerfile \
\"${IMAGE_TAG}\" \
\"${CACHE_REF}\" \
APP_VERSION=\"${GITHUB_SHA}\"
echo
echo \"API image pushed: ${IMAGE_TAG}\"
"
- name: Job duration summary
if: always()
shell: sh
@@ -510,8 +522,20 @@ jobs:
run: "set -eu\n# 确保使用 docker-container driver 以支持 cache export 功能\nif ! docker buildx inspect ci-builder > /dev/null 2>&1; then\n docker buildx create --use --name ci-builder --driver docker-container\n echo \"Created ci-builder (docker-container driver)\"\nelse\n docker buildx use ci-builder\n echo \"Using existing ci-builder\"\nfi\ndocker buildx inspect --bootstrap\n"
- name: Build and push Worker image (buildx cache)
shell: sh
run: "set -eu\nREGISTRY=\"git.xiaoxiajianji.com/xiaoxia/xiaoxia-saas\"\nIMAGE_NAME=\"xiaoxia-saas-worker\"\nCACHE_REF=\"${REGISTRY}/worker-cache:develop\"\n\nCACHE_FROM=\"type=registry,ref=${CACHE_REF},ignore-error=true\"\n\nif [ \"${CACHE_MODE}\" = \"read-write\" ]; then\n CACHE_TO=\"type=registry,ref=${CACHE_REF},mode=min\"\n echo \"Building Worker image with read-write cache...\"\n docker buildx build --build-arg APP_VERSION=\"${GITHUB_SHA}\" --cache-from \"${CACHE_FROM}\" --cache-to \"${CACHE_TO}\" -f infra/docker/worker.Dockerfile -t \"${REGISTRY}/${IMAGE_NAME}:${GITHUB_SHA}\" --push .\nelse\n echo \"Building Worker image with read-only cache...\"\n docker buildx build --build-arg APP_VERSION=\"${GITHUB_SHA}\" --cache-from \"${CACHE_FROM}\" -f infra/docker/worker.Dockerfile -t \"${REGISTRY}/${IMAGE_NAME}:${GITHUB_SHA}\" --push \
\ .\nfi\necho \"Worker image pushed: ${REGISTRY}/${IMAGE_NAME}:${GITHUB_SHA}\"\n"
run: "set -eu
REGISTRY=\"git.xiaoxiajianji.com/xiaoxia/xiaoxia-saas\"
IMAGE_TAG=\"${REGISTRY}/xiaoxia-saas-worker:${GITHUB_SHA}\"
CACHE_REF=\"${REGISTRY}/worker-cache:${GITHUB_REF_NAME}\"
bash scripts/ci/docker_build_push.sh \
infra/docker/worker.Dockerfile \
\"${IMAGE_TAG}\" \
\"${CACHE_REF}\" \
APP_VERSION=\"${GITHUB_SHA}\"
echo
echo \"WORKER image pushed: ${IMAGE_TAG}\"
"
- name: Job duration summary
if: always()
shell: sh
@@ -575,8 +599,20 @@ jobs:
run: "set -eu\n# 确保使用 docker-container driver 以支持 cache export 功能\nif ! docker buildx inspect ci-builder > /dev/null 2>&1; then\n docker buildx create --use --name ci-builder --driver docker-container\n echo \"Created ci-builder (docker-container driver)\"\nelse\n docker buildx use ci-builder\n echo \"Using existing ci-builder\"\nfi\ndocker buildx inspect --bootstrap\n"
- name: Build and push Web image (buildx cache)
shell: sh
run: "set -eu\nREGISTRY=\"git.xiaoxiajianji.com/xiaoxia/xiaoxia-saas\"\nIMAGE_NAME=\"xiaoxia-saas-web\"\nCACHE_REF=\"${REGISTRY}/web-cache:develop\"\nNGINX_CONF=\"infra/docker/nginx-staging.conf\"\n\nCACHE_FROM=\"type=registry,ref=${CACHE_REF},ignore-error=true\"\n\nif [ \"${CACHE_MODE}\" = \"read-write\" ]; then\n CACHE_TO=\"type=registry,ref=${CACHE_REF},mode=max\"\n echo \"Building Web image with read-write cache...\"\n docker buildx build --cache-from \"${CACHE_FROM}\" --cache-to \"${CACHE_TO}\" -f infra/docker/web-artifact.Dockerfile --build-arg \"NGINX_CONF=${NGINX_CONF}\" -t \"${REGISTRY}/${IMAGE_NAME}:${GITHUB_SHA}\" --push .\nelse\n echo \"Building Web image with read-only cache...\"\n docker buildx build --cache-from \"${CACHE_FROM}\" -f infra/docker/web-artifact.Dockerfile --build-arg \"NGINX_CONF=${NGINX_CONF}\" -t \"${REGISTRY}/${IMAGE_NAME}:${GITHUB_SHA}\"\
\ --push .\nfi\necho \"Web image pushed: ${REGISTRY}/${IMAGE_NAME}:${GITHUB_SHA}\"\n"
run: "set -eu
REGISTRY=\"git.xiaoxiajianji.com/xiaoxia/xiaoxia-saas\"
IMAGE_TAG=\"${REGISTRY}/xiaoxia-saas-web:${GITHUB_SHA}\"
CACHE_REF=\"${REGISTRY}/web-cache:${GITHUB_REF_NAME}\"
bash scripts/ci/docker_build_push.sh \
infra/docker/web.Dockerfile \
\"${IMAGE_TAG}\" \
\"${CACHE_REF}\" \
APP_VERSION=\"${GITHUB_SHA}\"
echo
echo \"WEB image pushed: ${IMAGE_TAG}\"
"
- name: Job duration summary
if: always()
shell: sh
@@ -825,24 +861,20 @@ jobs:
run: "set -eu\n# 确保使用 docker-container driver 以支持 cache export 功能\nif ! docker buildx inspect ci-builder > /dev/null 2>&1; then\n docker buildx create --use --name ci-builder --driver docker-container\n echo \"Created ci-builder (docker-container driver)\"\nelse\n docker buildx use ci-builder\n echo \"Using existing ci-builder\"\nfi\ndocker buildx inspect --bootstrap\n"
- name: Build and push API image (buildx cache)
shell: sh
run: 'set -eu
run: "set -eu
REGISTRY=\"git.xiaoxiajianji.com/xiaoxia/xiaoxia-saas\"
IMAGE_TAG=\"${REGISTRY}/xiaoxia-saas-api:${GITHUB_SHA}\"
CACHE_REF=\"${REGISTRY}/api-cache:${GITHUB_REF_NAME}\"
REGISTRY="git.xiaoxiajianji.com/xiaoxia/xiaoxia-saas"
bash scripts/ci/docker_build_push.sh \
infra/docker/api.Dockerfile \
\"${IMAGE_TAG}\" \
\"${CACHE_REF}\" \
APP_VERSION=\"${GITHUB_SHA}\"
IMAGE_NAME="xiaoxia-saas-api"
VERSION="${GITHUB_REF_NAME}"
CACHE_REF="${REGISTRY}/api-cache:main"
echo "Building Production API image: ${VERSION}"
docker buildx build --build-arg APP_VERSION="${VERSION}" --cache-from "type=registry,ref=${CACHE_REF},ignore-error=true" --cache-to "type=registry,ref=${CACHE_REF},mode=max" -f infra/docker/api.Dockerfile -t "${REGISTRY}/${IMAGE_NAME}:${VERSION}" --push .
echo "Production API image pushed: ${REGISTRY}/${IMAGE_NAME}:${VERSION}"
'
echo
echo \"API image pushed: ${IMAGE_TAG}\"
"
- name: Job duration summary
if: always()
shell: sh
@@ -900,24 +932,20 @@ jobs:
run: "set -eu\n# 确保使用 docker-container driver 以支持 cache export 功能\nif ! docker buildx inspect ci-builder > /dev/null 2>&1; then\n docker buildx create --use --name ci-builder --driver docker-container\n echo \"Created ci-builder (docker-container driver)\"\nelse\n docker buildx use ci-builder\n echo \"Using existing ci-builder\"\nfi\ndocker buildx inspect --bootstrap\n"
- name: Build and push Worker image (buildx cache)
shell: sh
run: 'set -eu
run: "set -eu
REGISTRY=\"git.xiaoxiajianji.com/xiaoxia/xiaoxia-saas\"
IMAGE_TAG=\"${REGISTRY}/xiaoxia-saas-worker:${GITHUB_SHA}\"
CACHE_REF=\"${REGISTRY}/worker-cache:${GITHUB_REF_NAME}\"
REGISTRY="git.xiaoxiajianji.com/xiaoxia/xiaoxia-saas"
bash scripts/ci/docker_build_push.sh \
infra/docker/worker.Dockerfile \
\"${IMAGE_TAG}\" \
\"${CACHE_REF}\" \
APP_VERSION=\"${GITHUB_SHA}\"
IMAGE_NAME="xiaoxia-saas-worker"
VERSION="${GITHUB_REF_NAME}"
CACHE_REF="${REGISTRY}/worker-cache:main"
echo "Building Production Worker image: ${VERSION}"
docker buildx build --build-arg APP_VERSION="${VERSION}" --cache-from "type=registry,ref=${CACHE_REF},ignore-error=true" --cache-to "type=registry,ref=${CACHE_REF},mode=min" -f infra/docker/worker.Dockerfile -t "${REGISTRY}/${IMAGE_NAME}:${VERSION}" --push .
echo "Production Worker image pushed: ${REGISTRY}/${IMAGE_NAME}:${VERSION}"
'
echo
echo \"WORKER image pushed: ${IMAGE_TAG}\"
"
- name: Job duration summary
if: always()
shell: sh
@@ -978,26 +1006,20 @@ jobs:
run: "set -eu\n# 确保使用 docker-container driver 以支持 cache export 功能\nif ! docker buildx inspect ci-builder > /dev/null 2>&1; then\n docker buildx create --use --name ci-builder --driver docker-container\n echo \"Created ci-builder (docker-container driver)\"\nelse\n docker buildx use ci-builder\n echo \"Using existing ci-builder\"\nfi\ndocker buildx inspect --bootstrap\n"
- name: Build and push Web image (buildx cache)
shell: sh
run: 'set -eu
run: "set -eu
REGISTRY=\"git.xiaoxiajianji.com/xiaoxia/xiaoxia-saas\"
IMAGE_TAG=\"${REGISTRY}/xiaoxia-saas-web:${GITHUB_SHA}\"
CACHE_REF=\"${REGISTRY}/web-cache:${GITHUB_REF_NAME}\"
REGISTRY="git.xiaoxiajianji.com/xiaoxia/xiaoxia-saas"
bash scripts/ci/docker_build_push.sh \
infra/docker/web.Dockerfile \
\"${IMAGE_TAG}\" \
\"${CACHE_REF}\" \
APP_VERSION=\"${GITHUB_SHA}\"
IMAGE_NAME="xiaoxia-saas-web"
VERSION="${GITHUB_REF_NAME}"
CACHE_REF="${REGISTRY}/web-cache:main"
NGINX_CONF="infra/docker/nginx-production.conf"
echo "Building Production Web image: ${VERSION}"
docker buildx build --cache-from "type=registry,ref=${CACHE_REF},ignore-error=true" --cache-to "type=registry,ref=${CACHE_REF},mode=max" -f infra/docker/web-artifact.Dockerfile --build-arg "NGINX_CONF=${NGINX_CONF}" -t "${REGISTRY}/${IMAGE_NAME}:${VERSION}" --push .
echo "Production Web image pushed: ${REGISTRY}/${IMAGE_NAME}:${VERSION}"
'
echo
echo \"WEB image pushed: ${IMAGE_TAG}\"
"
- name: Cleanup old Docker images
if: always()
shell: sh
+71
View File
@@ -0,0 +1,71 @@
#!/bin/bash
# 通用Docker镜像构建+推送脚本(构建与缓存解耦)
# 用法: docker_build_push.sh <Dockerfile> <image_tag> <cache_ref> [build_arg...]
set -eu
DOCKERFILE="$1"
IMAGE_TAG="$2"
CACHE_REF="$3"
shift 3
BUILD_ARGS=""
for arg in "$@"; do
BUILD_ARGS="$BUILD_ARGS --build-arg $arg"
done
if ! docker buildx inspect ci-builder > /dev/null 2>&1; then
docker buildx create --use --name ci-builder --driver docker-container
echo "Created ci-builder"
else
docker buildx use ci-builder
echo "Using existing ci-builder"
fi
docker buildx inspect --bootstrap
CACHE_FROM="type=registry,ref=${CACHE_REF},ignore-error=true"
echo "=== Step 1: Build & push image (read-only cache) ==="
docker buildx build \
$BUILD_ARGS \
--cache-from "${CACHE_FROM}" \
-f "${DOCKERFILE}" \
-t "${IMAGE_TAG}" \
--push \
.
echo "Image pushed: ${IMAGE_TAG}"
echo ""
echo "=== Step 2: Push cache (best effort, retries 3x) ==="
CACHE_TO="type=registry,ref=${CACHE_REF},mode=max,compression=zstd"
MAX_RETRIES=3
SUCCESS=0
for attempt in $(seq 1 $MAX_RETRIES); do
echo "Cache push attempt $attempt/$MAX_RETRIES"
if docker buildx build \
$BUILD_ARGS \
--cache-from "${CACHE_FROM}" \
--cache-to "${CACHE_TO}" \
-f "${DOCKERFILE}" \
-t "${IMAGE_TAG}" \
--push \
.; then
echo "Cache pushed (attempt $attempt)"
SUCCESS=1
break
else
echo "Cache push failed (attempt $attempt)"
if [ $attempt -lt $MAX_RETRIES ]; then
WAIT=$((attempt * 5))
echo "Retrying in ${WAIT}s..."
sleep $WAIT
fi
fi
done
if [ $SUCCESS -eq 0 ]; then
echo "WARNING: Cache push failed after $MAX_RETRIES attempts (non-fatal)"
fi
echo ""
echo "Build completed: ${IMAGE_TAG}"