diff --git a/.gitea/workflows/ci-cd.yml b/.gitea/workflows/ci-cd.yml index b975e84cc..25a26511d 100755 --- a/.gitea/workflows/ci-cd.yml +++ b/.gitea/workflows/ci-cd.yml @@ -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" diff --git a/scripts/ci/docker_build_push.sh b/scripts/ci/docker_build_push.sh new file mode 100755 index 000000000..33c644fba --- /dev/null +++ b/scripts/ci/docker_build_push.sh @@ -0,0 +1,85 @@ +#!/bin/bash +# Docker镜像构建与推送脚本 - 处理registry不稳定问题 +# 特性: +# 1. 构建+推送镜像(缓存只读模式)保证成功 +# 2. 单独尝试推送缓存,失败不影响主构建 +# 3. 缓存推送自动重试3次 +# 4. zstd压缩减小缓存体积 +set -eu + +usage() { + echo "Usage: $0 [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 "========================================"