diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index 588a03f98..24ebf8a13 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -169,6 +169,21 @@ jobs: scp -i "$key_path" "dist/release-artifacts/xiaoxia-web-${GITHUB_REF_NAME}.tar" \ "$production_user@$production_host:/var/lib/xiaoxia-saas-production/web-${GITHUB_REF_NAME}.tar" + - name: Cleanup old Docker images + if: always() + shell: sh + run: | + set -eu + if [ -f scripts/cleanup_old_images.sh ]; then + chmod +x scripts/cleanup_old_images.sh + scripts/cleanup_old_images.sh + else + echo "Cleanup script not found, doing basic prune..." + docker image prune -f 2>/dev/null || true + fi + echo "Disk usage after cleanup:" + df -h / | tail -1 + deploy-production: name: Deploy Production runs-on: runtime-builder:host diff --git a/scripts/cleanup_old_images.sh b/scripts/cleanup_old_images.sh new file mode 100755 index 000000000..e3499f685 --- /dev/null +++ b/scripts/cleanup_old_images.sh @@ -0,0 +1,63 @@ +#!/bin/sh +# cleanup_old_images.sh +# 清理构建服务器上的旧 Docker 镜像和 Registry 旧版本 +# 保留最近 KEEP_VERSIONS 个版本(默认 2) +# 在 CI 构建完成后调用,防止磁盘空间耗尽 +set -eu + +KEEP_VERSIONS="${KEEP_VERSIONS:-2}" +REGISTRY_HOST="${REGISTRY_HOST:-172.30.18.198:5000}" +SERVICES="xiaoxia-saas-api xiaoxia-saas-worker xiaoxia-saas-web" + +echo "=== Docker Image Cleanup ===" +echo "Keeping last ${KEEP_VERSIONS} versions per service" +echo "" + +for svc in $SERVICES; do + # 收集所有 v0.N.N 格式的版本号(去重,按版本号排序) + versions=$(docker images --format "{{.Repository}}:{{.Tag}}" | \ + grep "${svc}" | \ + grep -E "v[0-9]+\.[0-9]+\.[0-9]+" | \ + sed -E "s/.*:${svc}:(v[0-9]+\.[0-9]+\.[0-9]+).*/\1/" | \ + sed -E "s/.*:v([0-9]+\.[0-9]+\.[0-9]+).*/v\1/" | \ + sort -t. -k1,1V -k2,2n -k3,3n | \ + uniq) + + total=$(echo "$versions" | grep -c "^v" || true) + + if [ "$total" -gt "$KEEP_VERSIONS" ]; then + remove_count=$((total - KEEP_VERSIONS)) + to_remove=$(echo "$versions" | head -n "$remove_count") + + echo "[Registry] Cleaning old blobs for ${svc}..." + for ver in $to_remove; do + repo_name=$(echo "$svc" | sed "s/xiaoxia-saas-//") + manifest_url="http://admin:Xiaoxia2026@localhost:5000/v2/${repo_name}/manifests/${ver}" + digest=$(curl -sI -H "Accept: application/vnd.docker.distribution.manifest.v2+json" "$manifest_url" | grep -i "^docker-content-digest:" | tr -d "\r" | awk "{print \$2}") + if [ -n "$digest" ]; then + curl -s -X DELETE -H "Accept: application/vnd.docker.distribution.manifest.v2+json" "http://admin:Xiaoxia2026@localhost:5000/v2/${repo_name}/manifests/${digest}" > /dev/null 2>&1 || true + echo " Deleted registry tag: ${svc}:${ver}" + fi + done + + echo "[Local] Removing old local images for ${svc}..." + for ver in $to_remove; do + docker rmi "${svc}:${ver}" 2>/dev/null && echo " Removed local: ${svc}:${ver}" || true + docker rmi "${REGISTRY_HOST}/${svc}:${ver}" 2>/dev/null && echo " Removed registry-ref: ${REGISTRY_HOST}/${svc}:${ver}" || true + done + else + echo "[${svc}] ${total} version(s) found, within keep limit (${KEEP_VERSIONS})" + fi + echo "" +done + +# 清理悬空镜像(构建中间层) +echo "=== Pruning dangling images ===" +pruned=$(docker image prune -f 2>&1) +echo "$pruned" | tail -1 + +# 清理 dev 标签的旧层(dev 标签会被下次构建覆盖) +echo "" +echo "=== Cleanup complete ===" +echo "Current images:" +docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}" | grep -E "(xiaoxia|REPOSITORY)" || true