chore: test deploy snapshot

This commit is contained in:
2026-05-28 12:45:36 +09:00
parent 983887dc05
commit 82c46f4be4
21 changed files with 4163 additions and 449 deletions

View File

@@ -180,8 +180,14 @@ const payload = {
recoveredRequeuedCount:
parseCount(env.DEPLOY_RECOVERED_REQUEUED_COUNT_VALUE)
?? (!shouldResetForNewRun ? parseCount(current.recoveredRequeuedCount) : null),
lastError: env.DEPLOY_LAST_ERROR || (!shouldResetForNewRun ? current.lastError : null) || null,
logExcerpt: env.DEPLOY_LOG_EXCERPT || (!shouldResetForNewRun ? current.logExcerpt : null) || null,
lastError:
env.DEPLOY_STATUS === 'completed'
? null
: env.DEPLOY_LAST_ERROR || (!shouldResetForNewRun ? current.lastError : null) || null,
logExcerpt:
env.DEPLOY_STATUS === 'completed'
? null
: env.DEPLOY_LOG_EXCERPT || (!shouldResetForNewRun ? current.logExcerpt : null) || null,
steps: stepKeys.map((stepKey) => stepsByKey.get(stepKey)),
};
fs.writeFileSync(filePath, JSON.stringify(payload) + '\n', 'utf8');
@@ -278,20 +284,50 @@ server {
EOF2
}
wait_for_container_health() {
wait_for_container_runtime_ready() {
TARGET_CONTAINER="$1"
TARGET_SLOT="$2"
ATTEMPT=0
STABLE_SUCCESS_COUNT=0
while [ "$ATTEMPT" -lt 60 ]; do
if docker exec "$TARGET_CONTAINER" node -e "fetch(process.argv[1]).then(async (response) => { if (!response.ok) process.exit(1); process.stdout.write(await response.text()); }).catch(() => process.exit(1));" "$HEALTH_ENDPOINT" >/dev/null 2>&1; then
return 0
while [ "$ATTEMPT" -lt 90 ]; do
if docker exec "$TARGET_CONTAINER" node -e "const validatePayload = (payload, expectedSlot, requireSlot) => { if (payload?.ok !== true) process.exit(1); if (requireSlot && payload?.slot !== expectedSlot) process.exit(1); if (payload?.draining === true || payload?.canAcceptNewChatRequests === false) process.exit(1); }; Promise.all([fetch(process.argv[1]), fetch(process.argv[2])]).then(async ([healthResponse, runtimeResponse]) => { if (!healthResponse.ok || !runtimeResponse.ok) process.exit(1); const healthPayload = await healthResponse.json(); const runtimePayload = await runtimeResponse.json(); validatePayload(healthPayload, process.argv[3], true); validatePayload(runtimePayload, process.argv[3], false); }).catch(() => process.exit(1));" "$HEALTH_ENDPOINT" "$RUNTIME_ENDPOINT" "$TARGET_SLOT" >/dev/null 2>&1; then
STABLE_SUCCESS_COUNT=$((STABLE_SUCCESS_COUNT + 1))
if [ "$STABLE_SUCCESS_COUNT" -ge 3 ]; then
return 0
fi
else
STABLE_SUCCESS_COUNT=0
fi
ATTEMPT=$((ATTEMPT + 1))
sleep 2
sleep 1
done
echo "health check failed for $TARGET_CONTAINER" >&2
echo "runtime readiness check failed for $TARGET_CONTAINER slot $TARGET_SLOT" >&2
return 1
}
wait_for_proxy_slot_health() {
TARGET_SLOT="$1"
ATTEMPT=0
STABLE_SUCCESS_COUNT=0
while [ "$ATTEMPT" -lt 90 ]; do
if node -e "const validatePayload = (payload, expectedSlot, requireSlot) => { if (payload?.ok !== true) process.exit(1); if (requireSlot && payload?.slot !== expectedSlot) process.exit(1); if (payload?.draining === true || payload?.canAcceptNewChatRequests === false) process.exit(1); }; Promise.all([fetch(process.argv[1]), fetch(process.argv[2])]).then(async ([healthResponse, runtimeResponse]) => { if (!healthResponse.ok || !runtimeResponse.ok) process.exit(1); const healthPayload = await healthResponse.json(); const runtimePayload = await runtimeResponse.json(); validatePayload(healthPayload, process.argv[3], true); validatePayload(runtimePayload, process.argv[3], false); }).catch(() => process.exit(1));" "$HEALTH_ENDPOINT" "$RUNTIME_ENDPOINT" "$TARGET_SLOT" >/dev/null 2>&1; then
STABLE_SUCCESS_COUNT=$((STABLE_SUCCESS_COUNT + 1))
if [ "$STABLE_SUCCESS_COUNT" -ge 3 ]; then
return 0
fi
else
STABLE_SUCCESS_COUNT=0
fi
ATTEMPT=$((ATTEMPT + 1))
sleep 1
done
echo "proxy runtime readiness check failed for slot $TARGET_SLOT via $HEALTH_ENDPOINT" >&2
return 1
}
@@ -374,28 +410,28 @@ else
exit "$BUILD_STATUS"
fi
write_deploy_state running verify-target-health "새 슬롯 health 확인을 진행합니다." "verify-target-health" running "대상 컨테이너 ${TARGET_CONTAINER}"
if wait_for_container_health "$TARGET_CONTAINER"; then
write_deploy_state running verify-target-health "새 슬롯 health 확인이 완료되었습니다." "verify-target-health" completed "대상 슬롯 ${TARGET_SLOT} 정상 응답"
write_deploy_state running verify-target-health "새 슬롯 API 준비 상태를 확인합니다." "verify-target-health" running "대상 컨테이너 ${TARGET_CONTAINER}"
if wait_for_container_runtime_ready "$TARGET_CONTAINER" "$TARGET_SLOT"; then
write_deploy_state running verify-target-health "새 슬롯 API 준비 상태 확인이 완료되었습니다." "verify-target-health" completed "대상 슬롯 ${TARGET_SLOT} health/runtime 정상 응답"
else
LAST_DEPLOY_ERROR="새 슬롯 health 확인에 실패했습니다."
LAST_DEPLOY_LOG="health check failed for ${TARGET_CONTAINER}"
write_deploy_state failed failed "새 슬롯 health 확인에 실패했습니다." "verify-target-health" failed "대상 슬롯 ${TARGET_SLOT} health 실패" "$LAST_DEPLOY_ERROR" "$LAST_DEPLOY_LOG"
LAST_DEPLOY_ERROR="새 슬롯 API 준비 상태 확인에 실패했습니다."
LAST_DEPLOY_LOG="runtime readiness check failed for ${TARGET_CONTAINER}"
write_deploy_state failed failed "새 슬롯 API 준비 상태 확인에 실패했습니다." "verify-target-health" failed "대상 슬롯 ${TARGET_SLOT} health/runtime 실패" "$LAST_DEPLOY_ERROR" "$LAST_DEPLOY_LOG"
exit 1
fi
write_deploy_state running switch-proxy "프록시를 새 슬롯으로 전환합니다." "switch-proxy" running "활성 ${ACTIVE_SLOT} -> 대상 ${TARGET_SLOT}"
write_proxy_config "$TARGET_SLOT"
if ensure_proxy_running; then
write_deploy_state running switch-proxy "프록시 전환을 완료했습니다." "switch-proxy" completed "활성 ${ACTIVE_SLOT} -> 대상 ${TARGET_SLOT}"
if ensure_proxy_running && wait_for_proxy_slot_health "$TARGET_SLOT"; then
printf '%s\n' "$TARGET_SLOT" >"$ACTIVE_SLOT_FILE"
ACTIVE_SLOT="$TARGET_SLOT"
write_deploy_state running switch-proxy "프록시 전환을 완료했습니다." "switch-proxy" completed "프록시 3100 -> 대상 슬롯 ${TARGET_SLOT} 안정 응답 확인"
else
LAST_DEPLOY_ERROR="프록시 전환에 실패했습니다."
LAST_DEPLOY_LOG="nginx reload failed"
write_deploy_state failed failed "프록시 전환에 실패했습니다." "switch-proxy" failed "활성 ${ACTIVE_SLOT} -> 대상 ${TARGET_SLOT}" "$LAST_DEPLOY_ERROR" "$LAST_DEPLOY_LOG"
LAST_DEPLOY_LOG="nginx reload or proxy health verification failed"
write_deploy_state failed failed "프록시 전환에 실패했습니다." "switch-proxy" failed "대상 슬롯 ${TARGET_SLOT} 프록시 응답 확인 실패" "$LAST_DEPLOY_ERROR" "$LAST_DEPLOY_LOG"
exit 1
fi
printf '%s\n' "$TARGET_SLOT" >"$ACTIVE_SLOT_FILE"
ACTIVE_SLOT="$TARGET_SLOT"
if [ "$PREVIOUS_SERVICE" != "$TARGET_SERVICE" ]; then
set_container_draining "$PREVIOUS_CONTAINER" true
@@ -420,12 +456,12 @@ if [ "$PREVIOUS_SERVICE" != "$TARGET_SERVICE" ]; then
exit "$PREVIOUS_BUILD_STATUS"
fi
if wait_for_container_health "$PREVIOUS_CONTAINER"; then
write_deploy_state running rebuild-previous-slot "이전 슬롯을 대기 슬롯으로 복구했습니다." "rebuild-previous-slot" completed "대기 슬롯 ${PREVIOUS_SLOT} 정상 응답"
if wait_for_container_runtime_ready "$PREVIOUS_CONTAINER" "$PREVIOUS_SLOT"; then
write_deploy_state running rebuild-previous-slot "이전 슬롯을 대기 슬롯으로 복구했습니다." "rebuild-previous-slot" completed "대기 슬롯 ${PREVIOUS_SLOT} health/runtime 정상 응답"
else
LAST_DEPLOY_ERROR="이전 슬롯 복구 health 확인에 실패했습니다."
LAST_DEPLOY_LOG="health check failed for ${PREVIOUS_CONTAINER}"
write_deploy_state failed failed "이전 슬롯 복구 health 확인에 실패했습니다." "rebuild-previous-slot" failed "대기 슬롯 ${PREVIOUS_SLOT} health 실패" "$LAST_DEPLOY_ERROR" "$LAST_DEPLOY_LOG"
LAST_DEPLOY_ERROR="이전 슬롯 복구 API 준비 상태 확인에 실패했습니다."
LAST_DEPLOY_LOG="runtime readiness check failed for ${PREVIOUS_CONTAINER}"
write_deploy_state failed failed "이전 슬롯 복구 API 준비 상태 확인에 실패했습니다." "rebuild-previous-slot" failed "대기 슬롯 ${PREVIOUS_SLOT} health/runtime 실패" "$LAST_DEPLOY_ERROR" "$LAST_DEPLOY_LOG"
exit 1
fi
fi