From f5d0f4643693ebbd376e4e31fc174a805261d72e Mon Sep 17 00:00:00 2001 From: CI Bot Date: Sun, 19 Jul 2026 22:42:30 +0800 Subject: [PATCH] =?UTF-8?q?fix(ci):=20=E5=8F=8C=E6=A8=A1=E5=BC=8F=E8=BF=9E?= =?UTF-8?q?=E6=8E=A5PG/Redis=EF=BC=88=E7=AB=AF=E5=8F=A3=E6=98=A0=E5=B0=84+?= =?UTF-8?q?=E5=AE=B9=E5=99=A8IP=20fallback=EF=BC=89=EF=BC=8C=E5=85=BC?= =?UTF-8?q?=E5=AE=B9=E6=89=80=E6=9C=89runner=E7=BD=91=E7=BB=9C=E7=8E=AF?= =?UTF-8?q?=E5=A2=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 不同CI runner实例的Docker网络配置不一致: - new-4/new-10: 端口映射正常,容器IP不通 - new-9: 端口映射失效,容器IP可能可用 改为双模式自动探测:先尝试宿主端口映射(127.0.0.1:随机端口), 失败则自动fallback到容器IP直连,两种方式都试15次, 确保在任何runner环境下都能连通PG和Redis容器。 修改文件: - scripts/ci/run_validate.sh: PG双模式连接 - scripts/ci/run_integration_tests.sh: PG+Redis双模式连接 --- scripts/ci/run_integration_tests.sh | 91 +++++++++++++++++++---------- scripts/ci/run_validate.sh | 51 +++++++++++----- 2 files changed, 97 insertions(+), 45 deletions(-) diff --git a/scripts/ci/run_integration_tests.sh b/scripts/ci/run_integration_tests.sh index d3abd991b..ed552e8b8 100755 --- a/scripts/ci/run_integration_tests.sh +++ b/scripts/ci/run_integration_tests.sh @@ -25,19 +25,16 @@ echo "=== 启动 Redis ===" REDIS_CONTAINER="ci-redis-${GITHUB_RUN_ID:-$$}" docker rm -f "$REDIS_CONTAINER" 2>/dev/null || true docker run -d --name "$REDIS_CONTAINER" \ + -P \ --health-cmd "redis-cli ping" \ --health-interval 2s \ --health-timeout 2s \ --health-retries 10 \ redis:7-alpine -# 使用容器IP直连,不依赖宿主端口映射(部分runner的DinD端口映射不稳定) -REDIS_HOST=$(docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "$REDIS_CONTAINER") -REDIS_PORT=6379 -echo "Redis host: $REDIS_HOST:$REDIS_PORT" -export REDIS_URL="redis://$REDIS_HOST:$REDIS_PORT/0" +# 等待Redis容器内部ready for i in $(seq 1 15); do if docker inspect --format='{{.State.Health.Status}}' "$REDIS_CONTAINER" 2>/dev/null | grep -q healthy; then - echo "Redis is ready on $REDIS_HOST:$REDIS_PORT" + echo "Redis container is healthy" break fi echo "Waiting for Redis... ($i/15)" @@ -45,16 +42,34 @@ for i in $(seq 1 15); do done docker inspect --format='{{.State.Health.Status}}' "$REDIS_CONTAINER" | grep -q healthy -# 验证:通过容器IP直接连通 Redis -for i in $(seq 1 20); do - if python3 -c "import socket; s=socket.socket(); s.settimeout(2); s.connect(('$REDIS_HOST', $REDIS_PORT)); s.close()" 2>/dev/null; then - echo "TCP connectivity to Redis confirmed on $REDIS_HOST:$REDIS_PORT" - break - fi - echo "Waiting for TCP connectivity to Redis on $REDIS_HOST:$REDIS_PORT... ($i/20)" - sleep 2 +# 双模式探测:先试端口映射,再试容器IP,哪个通就用哪个 +REDIS_MAPPED_PORT=$(docker port "$REDIS_CONTAINER" 6379/tcp | cut -d: -f2) +REDIS_CONTAINER_IP=$(docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "$REDIS_CONTAINER") +echo "Redis mapped port: $REDIS_MAPPED_PORT, container IP: $REDIS_CONTAINER_IP" +REDIS_HOST="" +REDIS_PORT="" +for mode in "mapped:127.0.0.1:$REDIS_MAPPED_PORT" "container:$REDIS_CONTAINER_IP:6379"; do + MODE_NAME=$(echo "$mode" | cut -d: -f1) + TEST_HOST=$(echo "$mode" | cut -d: -f2) + TEST_PORT=$(echo "$mode" | cut -d: -f3) + echo "Testing Redis $MODE_NAME mode ($TEST_HOST:$TEST_PORT)..." + for i in $(seq 1 15); do + if python3 -c "import socket; s=socket.socket(); s.settimeout(2); s.connect(('$TEST_HOST', $TEST_PORT)); s.close()" 2>/dev/null; then + REDIS_HOST="$TEST_HOST" + REDIS_PORT="$TEST_PORT" + echo "✅ Redis $MODE_NAME mode works: $TEST_HOST:$TEST_PORT" + break 2 + fi + sleep 2 + done + echo "❌ Redis $MODE_NAME mode failed, trying next..." done -python3 -c "import socket; s=socket.socket(); s.settimeout(2); s.connect(('$REDIS_HOST', $REDIS_PORT)); s.close()" +if [ -z "$REDIS_HOST" ]; then + echo "ERROR: 无法通过任何方式连接到Redis容器" + exit 1 +fi +echo "Using Redis: $REDIS_HOST:$REDIS_PORT" +export REDIS_URL="redis://$REDIS_HOST:$REDIS_PORT/0" # --- 启动 PostgreSQL --- echo "" @@ -66,19 +81,16 @@ docker run -d --name "$PG_CONTAINER" \ -e POSTGRES_USER=postgres \ -e POSTGRES_PASSWORD=postgres \ -e POSTGRES_DB=xiaoxia_saas \ + -P \ --health-cmd "pg_isready -U postgres" \ --health-interval 5s \ --health-timeout 5s \ --health-retries 12 \ postgres:16 -# 使用容器IP直连,不依赖宿主端口映射(部分runner的DinD端口映射不稳定) -PG_HOST=$(docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "$PG_CONTAINER") -PG_PORT=5432 -echo "PostgreSQL host: $PG_HOST:$PG_PORT" -export DATABASE_URL="postgresql+psycopg://postgres:postgres@$PG_HOST:$PG_PORT/xiaoxia_saas" +# 等待PG容器内部ready for i in $(seq 1 30); do if docker inspect --format='{{.State.Health.Status}}' "$PG_CONTAINER" 2>/dev/null | grep -q healthy; then - echo "PostgreSQL is ready on $PG_HOST:$PG_PORT" + echo "PostgreSQL container is healthy" break fi echo "Waiting for PostgreSQL... ($i/30)" @@ -86,16 +98,35 @@ for i in $(seq 1 30); do done docker inspect --format='{{.State.Health.Status}}' "$PG_CONTAINER" | grep -q healthy -# 验证:通过容器IP直接连通 PG -for i in $(seq 1 30); do - if python3 -c "import socket; s=socket.socket(); s.settimeout(2); s.connect(('$PG_HOST', $PG_PORT)); s.close()" 2>/dev/null; then - echo "TCP connectivity to PostgreSQL confirmed on $PG_HOST:$PG_PORT" - break - fi - echo "Waiting for TCP connectivity to PostgreSQL on $PG_HOST:$PG_PORT... ($i/30)" - sleep 2 +# 双模式探测:先试端口映射,再试容器IP,哪个通就用哪个 +PG_MAPPED_PORT=$(docker port "$PG_CONTAINER" 5432/tcp | cut -d: -f2) +PG_CONTAINER_IP=$(docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "$PG_CONTAINER") +echo "PostgreSQL mapped port: $PG_MAPPED_PORT, container IP: $PG_CONTAINER_IP" +PG_HOST="" +PG_PORT="" +for mode in "mapped:127.0.0.1:$PG_MAPPED_PORT" "container:$PG_CONTAINER_IP:5432"; do + MODE_NAME=$(echo "$mode" | cut -d: -f1) + TEST_HOST=$(echo "$mode" | cut -d: -f2) + TEST_PORT=$(echo "$mode" | cut -d: -f3) + echo "Testing PG $MODE_NAME mode ($TEST_HOST:$TEST_PORT)..." + for i in $(seq 1 15); do + if python3 -c "import socket; s=socket.socket(); s.settimeout(2); s.connect(('$TEST_HOST', $TEST_PORT)); s.close()" 2>/dev/null; then + PG_HOST="$TEST_HOST" + PG_PORT="$TEST_PORT" + echo "✅ PG $MODE_NAME mode works: $TEST_HOST:$TEST_PORT" + break 2 + fi + sleep 2 + done + echo "❌ PG $MODE_NAME mode failed, trying next..." done -python3 -c "import socket; s=socket.socket(); s.settimeout(2); s.connect(('$PG_HOST', $PG_PORT)); s.close()" +if [ -z "$PG_HOST" ]; then + echo "ERROR: 无法通过任何方式连接到PostgreSQL容器" + echo "端口映射和容器IP两种方式均失败,可能是当前runner的Docker网络配置异常" + exit 1 +fi +echo "Using PostgreSQL: $PG_HOST:$PG_PORT" +export DATABASE_URL="postgresql+psycopg://postgres:postgres@$PG_HOST:$PG_PORT/xiaoxia_saas" # --- 执行迁移 --- echo "" diff --git a/scripts/ci/run_validate.sh b/scripts/ci/run_validate.sh index 9807cbb3b..6734eb1af 100755 --- a/scripts/ci/run_validate.sh +++ b/scripts/ci/run_validate.sh @@ -178,19 +178,17 @@ docker run -d --name "$PG_CONTAINER" \ -e POSTGRES_USER=postgres \ -e POSTGRES_PASSWORD=postgres \ -e POSTGRES_DB=xiaoxia_saas \ + -P \ --health-cmd "pg_isready -U postgres" \ --health-interval 3s \ --health-timeout 3s \ --health-retries 20 \ postgres:16-alpine -# 使用容器IP直连,不依赖宿主端口映射(部分runner的DinD端口映射不稳定) -PG_HOST=$(docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "$PG_CONTAINER") -PG_PORT=5432 -echo "PostgreSQL host: $PG_HOST:$PG_PORT" -export DATABASE_URL=postgresql+psycopg://postgres:postgres@$PG_HOST:$PG_PORT/xiaoxia_saas + +# 等待PG容器内部ready for i in $(seq 1 30); do if docker inspect --format='{{.State.Health.Status}}' "$PG_CONTAINER" 2>/dev/null | grep -q healthy; then - echo "PostgreSQL is ready on $PG_HOST:$PG_PORT" + echo "PostgreSQL container is healthy" break fi echo "Waiting for PostgreSQL... ($i/30)" @@ -198,16 +196,39 @@ for i in $(seq 1 30); do done docker inspect --format='{{.State.Health.Status}}' "$PG_CONTAINER" | grep -q healthy -# 验证:通过容器IP直接连通 -for i in $(seq 1 30); do - if python3 -c "import socket; s=socket.socket(); s.settimeout(2); s.connect(('$PG_HOST', $PG_PORT)); s.close()" 2>/dev/null; then - echo "TCP connectivity to PostgreSQL confirmed on $PG_HOST:$PG_PORT" - break - fi - echo "Waiting for TCP connectivity to PostgreSQL on $PG_HOST:$PG_PORT... ($i/30)" - sleep 2 +# 获取两种连接方式 +PG_MAPPED_PORT=$(docker port "$PG_CONTAINER" 5432/tcp | cut -d: -f2) +PG_CONTAINER_IP=$(docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "$PG_CONTAINER") +echo "Mapped port: $PG_MAPPED_PORT, Container IP: $PG_CONTAINER_IP" + +# 双模式探测:先试端口映射,再试容器IP,哪个通就用哪个 +PG_HOST="" +PG_PORT="" +for mode in "mapped:127.0.0.1:$PG_MAPPED_PORT" "container:$PG_CONTAINER_IP:5432"; do + MODE_NAME=$(echo "$mode" | cut -d: -f1) + TEST_HOST=$(echo "$mode" | cut -d: -f2) + TEST_PORT=$(echo "$mode" | cut -d: -f3) + echo "Testing $MODE_NAME mode ($TEST_HOST:$TEST_PORT)..." + for i in $(seq 1 15); do + if python3 -c "import socket; s=socket.socket(); s.settimeout(2); s.connect(('$TEST_HOST', $TEST_PORT)); s.close()" 2>/dev/null; then + PG_HOST="$TEST_HOST" + PG_PORT="$TEST_PORT" + echo "✅ $MODE_NAME mode works: $TEST_HOST:$TEST_PORT" + break 2 + fi + sleep 2 + done + echo "❌ $MODE_NAME mode failed after 15 attempts, trying next..." done -python3 -c "import socket; s=socket.socket(); s.settimeout(2); s.connect(('$PG_HOST', $PG_PORT)); s.close()" + +if [ -z "$PG_HOST" ]; then + echo "ERROR: 无法通过任何方式连接到PostgreSQL容器" + echo "端口映射和容器IP两种方式均失败,可能是当前runner的Docker网络配置异常" + exit 1 +fi + +echo "Using PostgreSQL: $PG_HOST:$PG_PORT" +export DATABASE_URL="postgresql+psycopg://postgres:postgres@$PG_HOST:$PG_PORT/xiaoxia_saas" PYTHONPATH="$PWD/apps/api:$PWD" python3 -m alembic upgrade head echo "✅ Alembic migrations applied successfully"