合并远端并行的 worker unit 渲染实现
远端798098e06与本地299544719并行修复了同一问题(deploy 原文安装 unit 覆盖自定义路径)。合并采纳远端实现为准:python3 字面量渲染(含 模板占位路径存在性校验)、install_worker_systemd_units 参数化、Jenkins 双流水线暴露并透传 CONTROLLER/BGFILTER env 参数、guardrails 门禁—— 其中流水线透传补上了本地版本刻意未覆盖的传输层缺口。保留本地独有的 readyz 派生修复、connect 重连与冷启动宽限、契约文档收口;删除本地 sed 渲染实现与合并产生的重复参数定义;decision-log 双条目并存。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -5,7 +5,7 @@ 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] [--controller-env-file /etc/genarrative/external-generation-controller.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] [--controller-env-file /etc/genarrative/external-generation-controller.env] [--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]
|
||||
|
||||
说明:
|
||||
进入维护模式,校验并发布 api-server 单文件,更新 current 链接,重启 systemd 服务并执行 readiness 检查。
|
||||
@@ -37,6 +37,10 @@ require_absolute_path() {
|
||||
echo "[production-api-deploy] ${label} 必须使用绝对路径: ${value}" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ "${value}" == *$'\n'* || "${value}" == *$'\r'* ]]; then
|
||||
echo "[production-api-deploy] ${label} 不能包含换行符。" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
validate_spacetime_database_name() {
|
||||
@@ -897,21 +901,11 @@ 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
|
||||
@@ -921,27 +915,61 @@ 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 "${rendered}" "${unit_dir}/${unit_name}"
|
||||
rm -f "${rendered}"
|
||||
if ! run_privileged install -d -m 0755 "${unit_dir}"; then
|
||||
return 1
|
||||
fi
|
||||
run_privileged install -m 0644 "${source_path}" "${unit_dir}/${unit_name}"
|
||||
}
|
||||
|
||||
render_and_install_release_systemd_unit() {
|
||||
local source_path="$1"
|
||||
local unit_name="$2"
|
||||
local label="$3"
|
||||
local rendered_path
|
||||
local install_status=0
|
||||
shift 3
|
||||
|
||||
if [[ ! -f "${source_path}" ]]; then
|
||||
echo "[production-api-deploy] 发布产物缺少${label}: ${source_path}" >&2
|
||||
return 1
|
||||
fi
|
||||
if [[ "$#" -eq 0 || $(( $# % 2 )) -ne 0 ]]; then
|
||||
echo "[production-api-deploy] ${label} 缺少成对的模板路径和实际路径。" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
RENDERED_SYSTEMD_UNIT_FILE="$(mktemp)"
|
||||
rendered_path="${RENDERED_SYSTEMD_UNIT_FILE}"
|
||||
if ! python3 - "${source_path}" "${rendered_path}" "$@" <<'PY'
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
source_path = Path(sys.argv[1])
|
||||
rendered_path = Path(sys.argv[2])
|
||||
replacements = sys.argv[3:]
|
||||
content = source_path.read_text(encoding="utf-8")
|
||||
replacement_map = dict(zip(replacements[::2], replacements[1::2]))
|
||||
|
||||
for template_path in replacement_map:
|
||||
if template_path not in content:
|
||||
raise SystemExit(f"systemd unit 模板缺少占位路径: {template_path}")
|
||||
|
||||
pattern = re.compile("|".join(re.escape(path) for path in replacement_map))
|
||||
content = pattern.sub(lambda match: replacement_map[match.group(0)], content)
|
||||
|
||||
rendered_path.write_text(content, encoding="utf-8")
|
||||
PY
|
||||
then
|
||||
cleanup_rendered_systemd_unit
|
||||
return 1
|
||||
fi
|
||||
|
||||
install_release_systemd_unit "${rendered_path}" "${unit_name}" "${label}" || install_status=$?
|
||||
cleanup_rendered_systemd_unit
|
||||
return "${install_status}"
|
||||
}
|
||||
|
||||
install_worker_systemd_units() {
|
||||
@@ -949,38 +977,43 @@ install_worker_systemd_units() {
|
||||
local pattern="$2"
|
||||
local controller_service="$3"
|
||||
local bgfilter_service="$4"
|
||||
local current_link="$5"
|
||||
local api_env_file="$6"
|
||||
local worker_env_file="$7"
|
||||
local controller_env_file="$8"
|
||||
local bgfilter_env_file="$9"
|
||||
local installed_any=0
|
||||
|
||||
if [[ "${bgfilter_service}" == "genarrative-bgfilter-worker.service" ]]; then
|
||||
install_release_systemd_unit \
|
||||
render_and_install_release_systemd_unit \
|
||||
"${release_dir}/deploy/systemd/genarrative-bgfilter-worker.service" \
|
||||
"genarrative-bgfilter-worker.service" \
|
||||
"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}"
|
||||
"/opt/genarrative/current" "${current_link}" \
|
||||
"/etc/genarrative/api-server.env" "${api_env_file}" \
|
||||
"/etc/genarrative/bgfilter-worker.env" "${bgfilter_env_file}"
|
||||
installed_any=1
|
||||
fi
|
||||
|
||||
if [[ "${pattern}" == "genarrative-external-generation-worker@*.service" ]]; then
|
||||
install_release_systemd_unit \
|
||||
render_and_install_release_systemd_unit \
|
||||
"${release_dir}/deploy/systemd/genarrative-external-generation-worker@.service" \
|
||||
"genarrative-external-generation-worker@.service" \
|
||||
"外部生成 worker systemd 模板" \
|
||||
"/opt/genarrative/current" "${CURRENT_LINK}" \
|
||||
"/etc/genarrative/api-server.env" "${API_ENV_FILE}" \
|
||||
"/etc/genarrative/external-generation-worker.env" "${WORKER_ENV_FILE}"
|
||||
"/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
|
||||
|
||||
if [[ "${controller_service}" == "genarrative-external-generation-controller.service" ]]; then
|
||||
install_release_systemd_unit \
|
||||
render_and_install_release_systemd_unit \
|
||||
"${release_dir}/deploy/systemd/genarrative-external-generation-controller.service" \
|
||||
"genarrative-external-generation-controller.service" \
|
||||
"外部生成 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}"
|
||||
"/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
|
||||
|
||||
@@ -1143,6 +1176,7 @@ MAINTENANCE_FILE="${GENARRATIVE_MAINTENANCE_FILE:-/var/lib/genarrative/maintenan
|
||||
CURRENT_LINK_SWITCHED=0
|
||||
RELEASE_DIR=""
|
||||
STAGING_RELEASE_DIR=""
|
||||
RENDERED_SYSTEMD_UNIT_FILE=""
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
@@ -1194,6 +1228,10 @@ while [[ $# -gt 0 ]]; do
|
||||
WORKER_CONTROLLER_SERVICE="${2:?缺少 --worker-controller-service 的值}"
|
||||
shift 2
|
||||
;;
|
||||
--controller-env-file)
|
||||
CONTROLLER_ENV_FILE="${2:?缺少 --controller-env-file 的值}"
|
||||
shift 2
|
||||
;;
|
||||
--no-worker-controller)
|
||||
WORKER_CONTROLLER_SERVICE=""
|
||||
shift
|
||||
@@ -1227,10 +1265,6 @@ 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
|
||||
@@ -1254,12 +1288,12 @@ require_absolute_path "${API_ENV_FILE}" "--api-env-file"
|
||||
if [[ -n "${WORKER_ENV_FILE}" ]]; then
|
||||
require_absolute_path "${WORKER_ENV_FILE}" "--worker-env-file"
|
||||
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 "${BGFILTER_WORKER_ENV_FILE}" ]]; then
|
||||
require_absolute_path "${BGFILTER_WORKER_ENV_FILE}" "--bgfilter-worker-env-file"
|
||||
fi
|
||||
|
||||
if [[ -n "${DATABASE}" ]]; then
|
||||
validate_spacetime_database_name "${DATABASE}"
|
||||
@@ -1312,8 +1346,16 @@ cleanup_staging_release() {
|
||||
fi
|
||||
}
|
||||
|
||||
cleanup_rendered_systemd_unit() {
|
||||
if [[ -n "${RENDERED_SYSTEMD_UNIT_FILE:-}" && -f "${RENDERED_SYSTEMD_UNIT_FILE}" ]]; then
|
||||
rm -f "${RENDERED_SYSTEMD_UNIT_FILE}"
|
||||
fi
|
||||
RENDERED_SYSTEMD_UNIT_FILE=""
|
||||
}
|
||||
|
||||
on_exit() {
|
||||
local exit_code=$?
|
||||
cleanup_rendered_systemd_unit
|
||||
if [[ "${exit_code}" -ne 0 && "${DEPLOY_COMPLETED}" -ne 1 ]]; then
|
||||
cleanup_staging_release
|
||||
if [[ "${MAINTENANCE_ENABLED_BY_DEPLOY}" -eq 1 && "${CURRENT_LINK_SWITCHED}" -ne 1 ]]; then
|
||||
@@ -1620,7 +1662,16 @@ if [[ "${PINGORA_INCLUDED}" -eq 1 ]]; then
|
||||
ensure_pingora_shadow_service "${PINGORA_SERVICE_NAME}" "${PINGORA_SHADOW_ENV_FILE}"
|
||||
fi
|
||||
|
||||
install_worker_systemd_units "${RELEASE_DIR}" "${WORKER_SERVICE_PATTERN}" "${WORKER_CONTROLLER_SERVICE}" "${BGFILTER_WORKER_SERVICE}"
|
||||
install_worker_systemd_units \
|
||||
"${RELEASE_DIR}" \
|
||||
"${WORKER_SERVICE_PATTERN}" \
|
||||
"${WORKER_CONTROLLER_SERVICE}" \
|
||||
"${BGFILTER_WORKER_SERVICE}" \
|
||||
"${CURRENT_LINK}" \
|
||||
"${API_ENV_FILE}" \
|
||||
"${WORKER_ENV_FILE}" \
|
||||
"${CONTROLLER_ENV_FILE}" \
|
||||
"${BGFILTER_WORKER_ENV_FILE}"
|
||||
|
||||
restart_and_wait_for_bgfilter_worker "${BGFILTER_WORKER_SERVICE}" "${BGFILTER_WORKER_HEALTH_URL}"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user