API 部署按参数渲染 worker systemd unit 后再安装

deploy 此前把发布产物里的三个 worker unit(bgfilter / worker@ / controller)
原文安装,unit 模板硬编码 /opt/genarrative/current 与默认 env 路径:自定义
--current-link/env 部署会覆盖 provision 渲染过的定制 unit,服务重启后指向
旧二进制并加载默认 env,可能静默发布错误版本。现安装前按部署参数执行与
provision render_*_service 同语义的替换(current 链接 + 共享 API env +
角色 env),并新增 --controller-env-file 补齐 controller 此前无法表达的
自定义 env 路径。默认参数下渲染输出与模板逐字节一致,生产现状零行为变化。
守卫测试改为断言安装后的 unit 含 fixture 自定义路径、不残留模板默认字面量。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-23 06:15:42 +00:00
parent b93ace708c
commit 2995447192
3 changed files with 120 additions and 7 deletions
+51 -5
View File
@@ -5,12 +5,13 @@ set -euo pipefail
usage() {
cat <<'EOF'
用法:
./scripts/deploy/production-api-deploy.sh --source-dir build/<version> [--version <version>] [--release-root /opt/genarrative/releases] [--current-link /opt/genarrative/current] [--service genarrative-api.service] [--pingora-service genarrative-pingora-gateway.service] [--require-pingora-gateway] [--bgfilter-worker-service genarrative-bgfilter-worker.service] [--bgfilter-worker-health-url <url>] [--bgfilter-worker-env-file /etc/genarrative/bgfilter-worker.env] [--no-bgfilter-worker] [--worker-service-pattern 'genarrative-external-generation-worker@*.service'] [--no-worker-services] [--worker-controller-service genarrative-external-generation-controller.service] [--no-worker-controller] [--health-url http://127.0.0.1:8082/readyz] [--api-env-file /etc/genarrative/api-server.env] [--worker-env-file /etc/genarrative/external-generation-worker.env] [--database genarrative-prod] [--spacetime-server-url http://127.0.0.1:3101] [--keep-maintenance-mode]
./scripts/deploy/production-api-deploy.sh --source-dir build/<version> [--version <version>] [--release-root /opt/genarrative/releases] [--current-link /opt/genarrative/current] [--service genarrative-api.service] [--pingora-service genarrative-pingora-gateway.service] [--require-pingora-gateway] [--bgfilter-worker-service genarrative-bgfilter-worker.service] [--bgfilter-worker-health-url <url>] [--bgfilter-worker-env-file /etc/genarrative/bgfilter-worker.env] [--no-bgfilter-worker] [--worker-service-pattern 'genarrative-external-generation-worker@*.service'] [--no-worker-services] [--worker-controller-service genarrative-external-generation-controller.service] [--no-worker-controller] [--health-url http://127.0.0.1:8082/readyz] [--api-env-file /etc/genarrative/api-server.env] [--worker-env-file /etc/genarrative/external-generation-worker.env] [--controller-env-file /etc/genarrative/external-generation-controller.env] [--database genarrative-prod] [--spacetime-server-url http://127.0.0.1:3101] [--keep-maintenance-mode]
说明:
进入维护模式,校验并发布 api-server 单文件,更新 current 链接,重启 systemd 服务并执行 readiness 检查。
默认先停止、启动并验活唯一 BgFilter worker,再重启 API、外部生成 worker controller 和已加载的 worker 实例。
--bgfilter-worker-health-url 缺省时从已校验的 worker env HOST/PORT 派生;显式传入时必须与父子 env 三方一致,否则预检失败。
随包 worker systemd unit 按 --current-link 与各 env 参数渲染后安装(与 provision 同语义),自定义路径不会被打回模板默认值;默认参数下渲染输出与模板一致。
若传入 --database,会在重启前把 GENARRATIVE_SPACETIME_DATABASE 写入 api-server 环境文件,避免服务继续读取旧库。
若发布包包含 pingora-gateway,或传入 --require-pingora-gateway,部署脚本会要求 release manifest、二进制与 checksum 一致,再在 current 链接切换后先复核 systemd/env 仍是本机高端口 shadow 配置,启动或重启 Pingora 影子服务并复核 active。
默认在 readiness 通过后退出维护模式;传入 --keep-maintenance-mode 时保留维护文件,供人工验收后再恢复公网。
@@ -896,11 +897,21 @@ ensure_default_worker_service() {
systemctl enable --now "${default_service}"
}
escape_sed_replacement() {
printf "%s" "$1" | sed "s/[&|]/\\\\&/g"
}
# 与 provision 的 render_*_service 同语义:unit 文件在仓库里是模板,安装前必须把
# 默认 current 链接与 env 路径渲染为本次部署参数,自定义 --current-link/env 时
# 不得让服务启动旧二进制或加载默认 env(默认参数下渲染输出与模板逐字节一致)。
install_release_systemd_unit() {
local source_path="$1"
local unit_name="$2"
local label="$3"
shift 3
local unit_dir="${GENARRATIVE_SYSTEMD_UNIT_DIR:-/etc/systemd/system}"
local sed_args=()
local template_default replacement rendered
if [[ ! -f "${source_path}" ]]; then
echo "[production-api-deploy] 发布产物缺少${label}: ${source_path}" >&2
@@ -910,10 +921,27 @@ install_release_systemd_unit() {
echo "[production-api-deploy] systemd unit 目录必须使用绝对路径: ${unit_dir}" >&2
return 1
fi
while [[ $# -gt 0 ]]; do
template_default="$1"
replacement="${2:?unit 渲染替换必须成对提供模板默认值与实际值}"
shift 2
if [[ -z "${replacement}" ]]; then
echo "[production-api-deploy] ${label} 渲染值不能为空: ${template_default}" >&2
return 1
fi
sed_args+=(-e "s|${template_default}|$(escape_sed_replacement "${replacement}")|g")
done
rendered="$(mktemp)"
if [[ "${#sed_args[@]}" -gt 0 ]]; then
sed "${sed_args[@]}" "${source_path}" > "${rendered}"
else
cat "${source_path}" > "${rendered}"
fi
echo "[production-api-deploy] 安装${label}: ${unit_name}"
run_privileged install -d -m 0755 "${unit_dir}"
run_privileged install -m 0644 "${source_path}" "${unit_dir}/${unit_name}"
run_privileged install -m 0644 "${rendered}" "${unit_dir}/${unit_name}"
rm -f "${rendered}"
}
install_worker_systemd_units() {
@@ -927,7 +955,10 @@ install_worker_systemd_units() {
install_release_systemd_unit \
"${release_dir}/deploy/systemd/genarrative-bgfilter-worker.service" \
"genarrative-bgfilter-worker.service" \
"BgFilter worker systemd 单元"
"BgFilter worker systemd 单元" \
"/opt/genarrative/current" "${CURRENT_LINK}" \
"/etc/genarrative/api-server.env" "${API_ENV_FILE}" \
"/etc/genarrative/bgfilter-worker.env" "${BGFILTER_WORKER_ENV_FILE}"
installed_any=1
fi
@@ -935,7 +966,10 @@ install_worker_systemd_units() {
install_release_systemd_unit \
"${release_dir}/deploy/systemd/genarrative-external-generation-worker@.service" \
"genarrative-external-generation-worker@.service" \
"外部生成 worker systemd 模板"
"外部生成 worker systemd 模板" \
"/opt/genarrative/current" "${CURRENT_LINK}" \
"/etc/genarrative/api-server.env" "${API_ENV_FILE}" \
"/etc/genarrative/external-generation-worker.env" "${WORKER_ENV_FILE}"
installed_any=1
fi
@@ -943,7 +977,10 @@ install_worker_systemd_units() {
install_release_systemd_unit \
"${release_dir}/deploy/systemd/genarrative-external-generation-controller.service" \
"genarrative-external-generation-controller.service" \
"外部生成 worker controller systemd 单元"
"外部生成 worker controller systemd 单元" \
"/opt/genarrative/current" "${CURRENT_LINK}" \
"/etc/genarrative/api-server.env" "${API_ENV_FILE}" \
"/etc/genarrative/external-generation-controller.env" "${CONTROLLER_ENV_FILE}"
installed_any=1
fi
@@ -1093,6 +1130,8 @@ BGFILTER_WORKER_ENV_FILE="/etc/genarrative/bgfilter-worker.env"
HEALTH_URL="http://127.0.0.1:8082/readyz"
API_ENV_FILE="/etc/genarrative/api-server.env"
WORKER_ENV_FILE="/etc/genarrative/external-generation-worker.env"
# controller unit 渲染专用:deploy 不读写该 env 内容,只保证安装的 unit 指向正确路径。
CONTROLLER_ENV_FILE="/etc/genarrative/external-generation-controller.env"
DATABASE=""
SPACETIME_SERVER_URL=""
DEPLOY_COMPLETED=0
@@ -1188,6 +1227,10 @@ while [[ $# -gt 0 ]]; do
WORKER_ENV_FILE="${2:?缺少 --worker-env-file 的值}"
shift 2
;;
--controller-env-file)
CONTROLLER_ENV_FILE="${2:?缺少 --controller-env-file 的值}"
shift 2
;;
--database)
DATABASE="${2:?缺少 --database 的值}"
shift 2
@@ -1214,6 +1257,9 @@ fi
if [[ -n "${BGFILTER_WORKER_ENV_FILE}" ]]; then
require_absolute_path "${BGFILTER_WORKER_ENV_FILE}" "--bgfilter-worker-env-file"
fi
if [[ -n "${CONTROLLER_ENV_FILE}" ]]; then
require_absolute_path "${CONTROLLER_ENV_FILE}" "--controller-env-file"
fi
if [[ -n "${DATABASE}" ]]; then
validate_spacetime_database_name "${DATABASE}"