release: 合并最新 TEST 并发布阿里云灰度 2026-11-06 #41

Merged
admin merged 7 commits from hotfix-gray-bugfix into main 2026-11-06 14:07:02 +08:00
3 changed files with 87 additions and 2 deletions
Showing only changes of commit 9c8273d9a0 - Show all commits
+65
View File
@@ -0,0 +1,65 @@
# 仅由 TEST 部署入口叠加,固定 JVM 堆并为线程、元空间和直接内存保留余量。
x-test-limits: &test-limits
mem_limit: 1536m
memswap_limit: 1536m
environment: &test-java-environment
JAVA_OPTS: "-XX:+ExitOnOutOfMemoryError -Dfile.encoding=UTF-8"
JAVA_TOOL_OPTIONS: "-Xms128m -Xmx768m"
services:
gateway:
<<: *test-limits
auth:
<<: *test-limits
iam:
<<: *test-limits
order:
<<: *test-limits
firstleg:
<<: *test-limits
mem_limit: 2g
memswap_limit: 2g
environment:
<<: *test-java-environment
JAVA_TOOL_OPTIONS: "-Xms256m -Xmx1g"
finance:
<<: *test-limits
warehouse:
<<: *test-limits
warehouse-overseas-service:
<<: *test-limits
integration-service:
<<: *test-limits
audit-service:
<<: *test-limits
file:
<<: *test-limits
mem_limit: 3g
memswap_limit: 3g
environment:
<<: *test-java-environment
JAVA_TOOL_OPTIONS: "-Xms1g -Xmx2g"
print:
<<: *test-limits
environment:
<<: *test-java-environment
JAVA_TOOL_OPTIONS: "-Xms512m -Xmx1g"
lastleg-service:
<<: *test-limits
mem_limit: 3g
memswap_limit: 3g
environment:
<<: *test-java-environment
JAVA_TOOL_OPTIONS: "-Xms512m -Xmx1536m"
+8
View File
@@ -12,6 +12,7 @@ IMAGE_TAG="$3"
SERVICE="$4"
ENV_FILE="$5"
COMPOSE_FILE="deploy/docker-compose.yml"
TEST_LIMITS_FILE="deploy/docker-compose.test-limits.yml"
IMAGE="$IMAGE_REPOSITORY/$SERVICE:$IMAGE_TAG"
REGISTRY_HOST="${IMAGE_REPOSITORY%%/*}"
PROJECT_NAME="firstleg-$DEPLOY_ENV"
@@ -40,6 +41,9 @@ case "${ALLOW_SAME_CONTRACT_ROLLBACK:-false}" in true|false) ;; *) exit 2 ;; esa
if [ "$DEPLOY_ENV" = "test" ] && [ "${TARGET_IS_LOCAL:-false}" != "true" ]; then
echo "TEST deploy must run on the target host" >&2; exit 2
fi
if [ "$DEPLOY_ENV" = "test" ] && [ ! -f "$TEST_LIMITS_FILE" ]; then
echo "TEST memory limits file missing: $TEST_LIMITS_FILE" >&2; exit 2
fi
[ -f "$ENV_FILE" ] || { echo "Environment file missing" >&2; exit 2; }
env_file_value() { sed -n "s/^$1=//p" "$ENV_FILE" | tail -n 1 | tr -d '\r'; }
for key in NACOS_SERVER_ADDR NACOS_NAMESPACE NACOS_USERNAME NACOS_PASSWORD REDIS_HOST REDIS_PORT REDIS_DATABASE REDIS_TOKEN_KEY_PREFIX INTERNAL_SERVICE_TOKEN JWT_RSA_JWK_SET JWT_CURRENT_KID ROCKETMQ_NAME_SERVER; do
@@ -74,7 +78,11 @@ local_registry_logout() {
if [ -n "${deploy_docker_config:-}" ]; then rm -rf -- "$deploy_docker_config"; fi
}
# TEST 的查询、部署和回滚共用同一份内存限额,缺失时由入口提前拒绝。
local_compose() {
if [ "$DEPLOY_ENV" = "test" ]; then
set -- -f "$TEST_LIMITS_FILE" "$@"
fi
docker compose \
--project-name "$PROJECT_NAME" \
--env-file "$ENV_FILE" \
+14 -2
View File
@@ -69,7 +69,8 @@ case "$command" in
esac
`
async function runDeployment({ imageTag, failingTag, failingStartTag, restartingTag, allowRollback = true, environment = 'test', service = 'audit-service', missingHealth = false, topicPrefix = `${environment}-`, remote = false }) {
// 通过临时 Docker/SSH 替身验证环境选择、健康门禁及失败后的镜像恢复。
async function runDeployment({ imageTag, failingTag, failingStartTag, restartingTag, allowRollback = true, environment = 'test', service = 'audit-service', missingHealth = false, missingTestLimits = false, topicPrefix = `${environment}-`, remote = false }) {
const temporaryDirectory = await mkdtemp(join(tmpdir(), 'firstleg-rollback-'))
const mockBin = join(temporaryDirectory, 'bin')
const dockerPath = join(mockBin, 'docker')
@@ -115,7 +116,7 @@ cp "$1" "\${2#*:}"
service,
envFile.replaceAll('\\', '/')
], {
cwd: repositoryRoot,
cwd: missingTestLimits ? temporaryDirectory : repositoryRoot,
encoding: 'utf8',
env: {
...process.env,
@@ -158,6 +159,16 @@ test('successful deployment keeps the new healthy image', async (context) => {
assert.match(result.stdout, /部署前运行镜像: registry\.local\/group\/firstleg\/audit-service:oldgood/)
assert.match(result.stdout, /新镜像健康检查通过: registry\.local\/group\/firstleg\/audit-service:newgood/)
assert.equal(result.currentImage, 'registry.local/group/firstleg/audit-service:newgood')
for (const command of result.dockerLog.split(/\r?\n/).filter(line => line.startsWith('compose '))) {
assert.match(command, /-f deploy\/docker-compose\.yml -f deploy\/docker-compose\.test-limits\.yml /)
}
})
test('TEST rejects a missing memory limits file before Docker', async () => {
const result = await runDeployment({ imageTag: 'newgood', missingTestLimits: true })
assert.equal(result.status, 2)
assert.equal(result.dockerLog, '')
assert.match(result.stderr, /TEST memory limits file missing/)
})
test('unhealthy deployment restores the previous healthy image and still fails the job', async (context) => {
@@ -232,6 +243,7 @@ test('gray deploy preserves health checks and uses an isolated Compose project',
assert.equal(result.status, 0, result.stderr)
assert.match(result.dockerLog, /--project-name firstleg-production/)
assert.doesNotMatch(result.dockerLog, /--project-name firstleg-test/)
assert.doesNotMatch(result.dockerLog, /docker-compose\.test-limits\.yml/)
})
test('gray deployment rejects TEST message scope before any Docker operation', async () => {