自动拉起 Pingora shadow 发布服务

让 API deploy 在发布包包含 Pingora 时先校验 shadow systemd 和 env 安全边界

在 shadow 配置安全时自动启动或重启 genarrative-pingora-gateway.service

拒绝 direct-entry capability、公网监听 env,并在 current 切换前失败

补充 production-api-deploy 动态烟测、生产运维护栏和 Pingora 运维文档

在 dev 服务器真实验证 shadow inactive 场景可由发布脚本自动拉起
This commit is contained in:
2026-06-18 14:53:03 +08:00
parent 10e559701f
commit 0cc2a79c7b
7 changed files with 278 additions and 44 deletions
+99 -10
View File
@@ -11,7 +11,7 @@ usage() {
进入维护模式,校验并发布 api-server 单文件,更新 current 链接,重启 systemd 服务并执行 readiness 检查。
默认同时重启外部生成 worker controller 和已加载的 worker 实例;未启用 worker 单元时会自动跳过。
若传入 --database,会在重启前把 GENARRATIVE_SPACETIME_DATABASE 写入 api-server 环境文件,避免服务继续读取旧库。
若发布包包含 pingora-gateway,部署脚本会在 current 链接切换后仅对已 active 的 Pingora 影子服务执行 try-restart 并复核 active;未运行时不会主动拉起。
若发布包包含 pingora-gateway,部署脚本会在 current 链接切换后先复核 systemd/env 仍是本机高端口 shadow 配置,再启动或重启 Pingora 影子服务并复核 active。
失败时保留维护模式。
EOF
}
@@ -235,18 +235,101 @@ ensure_runtime_env_and_dirs() {
fi
}
restart_pingora_if_active() {
extract_pingora_env_files_from_unit() {
local service_name="$1"
local unit_content
if ! systemctl is-active --quiet "${service_name}"; then
echo "[production-api-deploy] Pingora 影子服务未处于 active,跳过自动重启: ${service_name}"
return
if ! unit_content="$(systemctl cat "${service_name}")"; then
echo "[production-api-deploy] 无法读取 Pingora systemd 最终配置: ${service_name}" >&2
exit 1
fi
echo "[production-api-deploy] 发布包包含 Pingora,重启已运行的影子服务: ${service_name}"
systemctl try-restart "${service_name}"
if printf "%s\n" "${unit_content}" | grep -Eq '^[[:space:]]*(AmbientCapabilities|CapabilityBoundingSet)=.*CAP_NET_BIND_SERVICE'; then
echo "[production-api-deploy] Pingora systemd 已包含 CAP_NET_BIND_SERVICE,疑似直连入口配置;API deploy 不会自动启动或重启直连服务: ${service_name}" >&2
exit 1
fi
printf "%s\n" "${unit_content}" | while IFS= read -r raw_line; do
local line value token env_file
line="${raw_line#"${raw_line%%[![:space:]]*}"}"
[[ "${line}" == EnvironmentFile=* ]] || continue
value="${line#EnvironmentFile=}"
for token in ${value}; do
env_file="${token#-}"
env_file="${env_file%\"}"
env_file="${env_file#\"}"
env_file="${env_file%\'}"
env_file="${env_file#\'}"
[[ -n "${env_file}" ]] && printf "%s\n" "${env_file}"
done
done
}
find_pingora_gateway_env_file() {
local service_name="$1"
local env_file listen
while IFS= read -r env_file; do
if [[ "${env_file}" != /* ]]; then
echo "[production-api-deploy] Pingora EnvironmentFile 必须使用绝对路径: ${env_file}" >&2
exit 1
fi
listen="$(read_env_value "${env_file}" "GENARRATIVE_PINGORA_GATEWAY_LISTEN")"
if [[ -n "${listen}" ]]; then
printf "%s\n" "${env_file}"
return
fi
done < <(extract_pingora_env_files_from_unit "${service_name}")
echo "[production-api-deploy] Pingora systemd 配置缺少包含 GENARRATIVE_PINGORA_GATEWAY_LISTEN 的 EnvironmentFile: ${service_name}" >&2
exit 1
}
require_pingora_shadow_env() {
local env_file="$1"
local listen tls_listen redirect_listen
listen="$(read_env_value "${env_file}" "GENARRATIVE_PINGORA_GATEWAY_LISTEN")"
tls_listen="$(read_env_value "${env_file}" "GENARRATIVE_PINGORA_GATEWAY_TLS_LISTEN")"
redirect_listen="$(read_env_value "${env_file}" "GENARRATIVE_PINGORA_GATEWAY_HTTP_REDIRECT_LISTEN")"
if [[ "${listen}" != "127.0.0.1:18081" ]]; then
echo "[production-api-deploy] Pingora 自动启动只允许 shadow 监听 127.0.0.1:18081,当前 GENARRATIVE_PINGORA_GATEWAY_LISTEN=${listen:-<empty>}" >&2
exit 1
fi
if [[ -n "${tls_listen}" ]]; then
echo "[production-api-deploy] Pingora 自动启动不允许启用 TLS_LISTEN,当前 GENARRATIVE_PINGORA_GATEWAY_TLS_LISTEN=${tls_listen}" >&2
exit 1
fi
if [[ -n "${redirect_listen}" ]]; then
echo "[production-api-deploy] Pingora 自动启动不允许启用 HTTP_REDIRECT_LISTEN,当前 GENARRATIVE_PINGORA_GATEWAY_HTTP_REDIRECT_LISTEN=${redirect_listen}" >&2
exit 1
fi
}
check_pingora_shadow_service_config() {
local service_name="$1"
local env_file
env_file="$(find_pingora_gateway_env_file "${service_name}")"
require_pingora_shadow_env "${env_file}"
printf "%s\n" "${env_file}"
}
ensure_pingora_shadow_service() {
local service_name="$1"
local env_file="${2:-}"
if [[ -z "${env_file}" ]]; then
env_file="$(check_pingora_shadow_service_config "${service_name}")"
else
require_pingora_shadow_env "${env_file}"
fi
echo "[production-api-deploy] 发布包包含 Pingora,启动或重启 shadow 影子服务: ${service_name} (${env_file})"
systemctl restart "${service_name}"
if ! systemctl is-active --quiet "${service_name}"; then
echo "[production-api-deploy] Pingora 影子服务重启后不是 active: ${service_name}" >&2
echo "[production-api-deploy] Pingora shadow 影子服务启动或重启后不是 active: ${service_name}" >&2
exit 1
fi
}
@@ -604,7 +687,7 @@ if [[ -f "${SOURCE_DIR}/pingora-gateway" ]]; then
cp "${SOURCE_DIR}/pingora-gateway" "${RELEASE_CONTENT_DIR}/pingora-gateway"
cp "${SOURCE_DIR}/pingora-gateway.sha256" "${RELEASE_CONTENT_DIR}/pingora-gateway.sha256"
chmod +x "${RELEASE_CONTENT_DIR}/pingora-gateway"
echo "[production-api-deploy] 已复制 Pingora 影子网关;current 链接切换后将复核并重启已运行的 ${PINGORA_SERVICE_NAME}"
echo "[production-api-deploy] 已复制 Pingora 影子网关;current 链接切换后将复核 shadow 配置并启动或重启 ${PINGORA_SERVICE_NAME}"
fi
BACKUP_SCRIPT_SOURCE="${SOURCE_DIR}/scripts/database-backup-to-oss.mjs"
@@ -786,6 +869,8 @@ cp -R "${ENV_DEPLOY_DIR_SOURCE}" "${RELEASE_CONTENT_DIR}/deploy/env"
cp "${SOURCE_DIR}/release-manifest.json" "${RELEASE_CONTENT_DIR}/release-manifest.api-server.json"
PINGORA_SHADOW_ENV_FILE=""
if [[ -n "${DATABASE}" ]]; then
echo "[production-api-deploy] 写入 api-server SpacetimeDB database: ${DATABASE} -> ${API_ENV_FILE}"
write_env_value "${API_ENV_FILE}" "GENARRATIVE_SPACETIME_DATABASE" "${DATABASE}"
@@ -798,6 +883,10 @@ fi
ensure_runtime_env_and_dirs "${API_ENV_FILE}"
if [[ "${PINGORA_INCLUDED}" -eq 1 ]]; then
PINGORA_SHADOW_ENV_FILE="$(check_pingora_shadow_service_config "${PINGORA_SERVICE_NAME}")"
fi
mkdir -p "$(dirname "${CURRENT_LINK}")"
if [[ -e "${RELEASE_DIR}" ]]; then
echo "[production-api-deploy] 目标 release 在发布过程中出现,拒绝合并 staging: ${RELEASE_DIR}" >&2
@@ -808,7 +897,7 @@ STAGING_RELEASE_DIR=""
ln -sfnT "${RELEASE_DIR}" "${CURRENT_LINK}"
if [[ "${PINGORA_INCLUDED}" -eq 1 ]]; then
restart_pingora_if_active "${PINGORA_SERVICE_NAME}"
ensure_pingora_shadow_service "${PINGORA_SERVICE_NAME}" "${PINGORA_SHADOW_ENV_FILE}"
fi
echo "[production-api-deploy] 重启服务: ${SERVICE_NAME}"