fix(ci): 修复registry不稳定导致镜像构建失败 - 解耦缓存推送与镜像构建
问题: - Worker镜像构建因registry缓存推送时连接断开(use of closed network connection)而失败 - docker buildx exporting cache to registry阶段大层传输超时 - 缓存推送失败导致整个构建失败,镜像其实已经构建完成 修复: 1. 创建scripts/ci/docker_build_push.sh通用构建脚本 2. 镜像构建与缓存推送解耦:先构建+推送镜像(只读缓存)保证成功 3. 再单独尝试推送缓存,自动重试3次,失败不影响主构建结果 4. 缓存使用zstd压缩+oci-mediatypes减小传输体积 5. 缓存引用改为按分支名区分,支持多分支独立缓存 涉及:全部6个镜像构建job(staging/production x api/worker/web)
This commit is contained in:
@@ -448,7 +448,7 @@ 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\
|
||||
run: "set -eu\nREGISTRY=\"git.xiaoxiajianji.com/xiaoxia/xiaoxia-saas\"\nIMAGE_TAG=\"${REGISTRY}/xiaoxia-saas-api:${GITHUB_SHA}\"\nCACHE_REF=\"${REGISTRY}/api-cache:${GITHUB_REF_NAME}\"\n\nbash scripts/ci/docker_build_push.sh \\\n infra/docker/api.Dockerfile \\\n \"${IMAGE_TAG}\" \\\n \"${CACHE_REF}\" \\\n APP_VERSION=\"${GITHUB_SHA}\"\n\necho\necho \"API image pushed: ${IMAGE_TAG}\""
|
||||
\ \"API image pushed: ${REGISTRY}/${IMAGE_NAME}:${GITHUB_SHA}\"\n"
|
||||
- name: Job duration summary
|
||||
if: always()
|
||||
@@ -510,7 +510,7 @@ 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 \
|
||||
run: "set -eu\nREGISTRY=\"git.xiaoxiajianji.com/xiaoxia/xiaoxia-saas\"\nIMAGE_TAG=\"${REGISTRY}/xiaoxia-saas-worker:${GITHUB_SHA}\"\nCACHE_REF=\"${REGISTRY}/worker-cache:${GITHUB_REF_NAME}\"\n\nbash scripts/ci/docker_build_push.sh \\\n infra/docker/worker.Dockerfile \\\n \"${IMAGE_TAG}\" \\\n \"${CACHE_REF}\" \\\n APP_VERSION=\"${GITHUB_SHA}\"\n\necho\necho \"Worker image pushed: ${IMAGE_TAG}\""
|
||||
\ .\nfi\necho \"Worker image pushed: ${REGISTRY}/${IMAGE_NAME}:${GITHUB_SHA}\"\n"
|
||||
- name: Job duration summary
|
||||
if: always()
|
||||
@@ -575,7 +575,7 @@ 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}\"\
|
||||
run: "set -eu\nREGISTRY=\"git.xiaoxiajianji.com/xiaoxia/xiaoxia-saas\"\nIMAGE_TAG=\"${REGISTRY}/xiaoxia-saas-web:${GITHUB_SHA}\"\nCACHE_REF=\"${REGISTRY}/web-cache:${GITHUB_REF_NAME}\"\n\nbash scripts/ci/docker_build_push.sh \\\n infra/docker/web.Dockerfile \\\n \"${IMAGE_TAG}\" \\\n \"${CACHE_REF}\" \\\n APP_VERSION=\"${GITHUB_SHA}\"\n\necho\necho \"Web image pushed: ${IMAGE_TAG}\""
|
||||
\ --push .\nfi\necho \"Web image pushed: ${REGISTRY}/${IMAGE_NAME}:${GITHUB_SHA}\"\n"
|
||||
- name: Job duration summary
|
||||
if: always()
|
||||
@@ -825,7 +825,7 @@ 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\nREGISTRY=\"git.xiaoxiajianji.com/xiaoxia/xiaoxia-saas\"\nIMAGE_TAG=\"${REGISTRY}/xiaoxia-saas-api:${GITHUB_SHA}\"\nCACHE_REF=\"${REGISTRY}/api-cache:${GITHUB_REF_NAME}\"\n\nbash scripts/ci/docker_build_push.sh \\\n infra/docker/api.Dockerfile \\\n \"${IMAGE_TAG}\" \\\n \"${CACHE_REF}\" \\\n APP_VERSION=\"${GITHUB_SHA}\"\n\necho\necho \"API image pushed: ${IMAGE_TAG}\""
|
||||
|
||||
REGISTRY="git.xiaoxiajianji.com/xiaoxia/xiaoxia-saas"
|
||||
|
||||
@@ -900,7 +900,7 @@ 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\nREGISTRY=\"git.xiaoxiajianji.com/xiaoxia/xiaoxia-saas\"\nIMAGE_TAG=\"${REGISTRY}/xiaoxia-saas-worker:${GITHUB_SHA}\"\nCACHE_REF=\"${REGISTRY}/worker-cache:${GITHUB_REF_NAME}\"\n\nbash scripts/ci/docker_build_push.sh \\\n infra/docker/worker.Dockerfile \\\n \"${IMAGE_TAG}\" \\\n \"${CACHE_REF}\" \\\n APP_VERSION=\"${GITHUB_SHA}\"\n\necho\necho \"Worker image pushed: ${IMAGE_TAG}\""
|
||||
|
||||
REGISTRY="git.xiaoxiajianji.com/xiaoxia/xiaoxia-saas"
|
||||
|
||||
@@ -978,7 +978,7 @@ 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\nREGISTRY=\"git.xiaoxiajianji.com/xiaoxia/xiaoxia-saas\"\nIMAGE_TAG=\"${REGISTRY}/xiaoxia-saas-web:${GITHUB_SHA}\"\nCACHE_REF=\"${REGISTRY}/web-cache:${GITHUB_REF_NAME}\"\n\nbash scripts/ci/docker_build_push.sh \\\n infra/docker/web.Dockerfile \\\n \"${IMAGE_TAG}\" \\\n \"${CACHE_REF}\" \\\n APP_VERSION=\"${GITHUB_SHA}\"\n\necho\necho \"Web image pushed: ${IMAGE_TAG}\""
|
||||
|
||||
REGISTRY="git.xiaoxiajianji.com/xiaoxia/xiaoxia-saas"
|
||||
|
||||
|
||||
Executable
+85
@@ -0,0 +1,85 @@
|
||||
#!/bin/bash
|
||||
# Docker镜像构建与推送脚本 - 处理registry不稳定问题
|
||||
# 特性:
|
||||
# 1. 构建+推送镜像(缓存只读模式)保证成功
|
||||
# 2. 单独尝试推送缓存,失败不影响主构建
|
||||
# 3. 缓存推送自动重试3次
|
||||
# 4. zstd压缩减小缓存体积
|
||||
set -eu
|
||||
|
||||
usage() {
|
||||
echo "Usage: $0 <dockerfile> <image_tag> <cache_ref> [build_args...]"
|
||||
echo " dockerfile: Dockerfile路径"
|
||||
echo " image_tag: 完整镜像标签 (registry/repo:tag)"
|
||||
echo " cache_ref: 缓存引用 (registry/repo-cache:branch)"
|
||||
echo " build_args: 额外的--build-arg参数 (可选)"
|
||||
exit 1
|
||||
}
|
||||
|
||||
if [ $# -lt 3 ]; then
|
||||
usage
|
||||
fi
|
||||
|
||||
DOCKERFILE="$1"
|
||||
IMAGE_TAG="$2"
|
||||
CACHE_REF="$3"
|
||||
shift 3
|
||||
BUILD_ARGS=""
|
||||
for arg in "$@"; do
|
||||
BUILD_ARGS="$BUILD_ARGS --build-arg $arg"
|
||||
done
|
||||
|
||||
CACHE_FROM="type=registry,ref=${CACHE_REF},ignore-error=true"
|
||||
CACHE_TO="type=registry,ref=${CACHE_REF},mode=min,compression=zstd,oci-mediatypes=true"
|
||||
|
||||
echo "========================================"
|
||||
echo "构建镜像: $IMAGE_TAG"
|
||||
echo "Dockerfile: $DOCKERFILE"
|
||||
echo "缓存引用: $CACHE_REF"
|
||||
echo "========================================"
|
||||
|
||||
# Step 1: 构建并推送镜像(只读缓存,保证主构建成功)
|
||||
echo ""
|
||||
echo "[1/2] 构建并推送镜像 (只读缓存模式)..."
|
||||
docker buildx build \
|
||||
$BUILD_ARGS \
|
||||
--cache-from "$CACHE_FROM" \
|
||||
-f "$DOCKERFILE" \
|
||||
-t "$IMAGE_TAG" \
|
||||
--push \
|
||||
.
|
||||
echo "✅ 镜像构建并推送成功: $IMAGE_TAG"
|
||||
|
||||
# Step 2: 尝试推送缓存(失败不影响,重试3次)
|
||||
echo ""
|
||||
echo "[2/2] 尝试推送缓存到 registry (最多重试3次)..."
|
||||
CACHE_PUSH_SUCCESS=false
|
||||
for attempt in 1 2 3; do
|
||||
echo " 缓存推送尝试 $attempt/3..."
|
||||
if docker buildx build \
|
||||
$BUILD_ARGS \
|
||||
--cache-from "$CACHE_FROM" \
|
||||
--cache-to "$CACHE_TO" \
|
||||
-f "$DOCKERFILE" \
|
||||
-t "$IMAGE_TAG" \
|
||||
--push \
|
||||
. 2>&1 | tail -5; then
|
||||
CACHE_PUSH_SUCCESS=true
|
||||
echo " ✅ 缓存推送成功 (第$attempt次尝试)"
|
||||
break
|
||||
else
|
||||
echo " ⚠️ 缓存推送失败 (第$attempt次尝试),等待后重试..."
|
||||
sleep $((attempt * 5))
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$CACHE_PUSH_SUCCESS" = false ]; then
|
||||
echo ""
|
||||
echo "⚠️ 缓存推送最终失败,跳过(不影响构建结果)"
|
||||
echo " 下一次构建将使用旧缓存或无缓存构建"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "========================================"
|
||||
echo "✅ 构建完成: $IMAGE_TAG"
|
||||
echo "========================================"
|
||||
Reference in New Issue
Block a user