From 99fa146a5c23ce205edf91387dd2a86e70320a5e Mon Sep 17 00:00:00 2001 From: XiaoXia Bot Date: Wed, 15 Jul 2026 12:33:02 +0800 Subject: [PATCH] =?UTF-8?q?fix(ci):=20=E4=BF=AE=E5=A4=8Dregistry=E4=B8=8D?= =?UTF-8?q?=E7=A8=B3=E5=AE=9A=E5=AF=BC=E8=87=B4=E6=9E=84=E5=BB=BA=E5=A4=B1?= =?UTF-8?q?=E8=B4=A5=20-=20=E6=9E=84=E5=BB=BA=E4=B8=8E=E7=BC=93=E5=AD=98?= =?UTF-8?q?=E6=8E=A8=E9=80=81=E8=A7=A3=E8=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增docker_build_push.sh通用脚本 - 构建镜像(只读缓存)与推送缓存分离 - 缓存推送失败重试3次,不影响主构建结果 - 使用zstd压缩缓存层 --- .gitea/workflows/ci-cd.yml | 134 +++++++++++++++++++------------- scripts/ci/docker_build_push.sh | 71 +++++++++++++++++ 2 files changed, 149 insertions(+), 56 deletions(-) create mode 100755 scripts/ci/docker_build_push.sh diff --git a/.gitea/workflows/ci-cd.yml b/.gitea/workflows/ci-cd.yml index b975e84cc..e286f2cae 100755 --- a/.gitea/workflows/ci-cd.yml +++ b/.gitea/workflows/ci-cd.yml @@ -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 diff --git a/scripts/ci/docker_build_push.sh b/scripts/ci/docker_build_push.sh new file mode 100755 index 000000000..84fb2c891 --- /dev/null +++ b/scripts/ci/docker_build_push.sh @@ -0,0 +1,71 @@ +#!/bin/bash +# 通用Docker镜像构建+推送脚本(构建与缓存解耦) +# 用法: docker_build_push.sh [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}" -- 2.54.0