本文提供了 非root权限下 Cronicle 的现代化单主机群部署方案。通过 pnpm 独立版(Standalone),实现 Node 运行时与应用依赖的完全解耦,打造绿色、便携、可复现的调度系统环境。
🛠️ 技术特点
环境隔离:采用
pnpm env use lts -g方式,无需 nvm/fnm。源码构建:通过 Tarball 方式安装最新版 Cronicle,利用 pnpm 内置 Node 编译资源,摆脱系统环境依赖。
集群通信:禁用 UDP 广播,采用
udp_broadcast_port: 0+web_direct_connect: true强制 TCP 直连,适配跨主机与云原生环境。数据生命周期:配置
job_data_expire_days: 15自动清理历史记录,配合log_archive_path实现日志归档分离,兼顾存储成本与审计需求。
🏗️ 部署架构
Master 节点:执行
control.sh setup初始化存储,负责调度与 Web 管理。Worker 节点:通过复制 Master 配置并直接
start,由 TCP 机制自动注册加入集群。运维管理:提供完整的 Systemd User Mode 配置,实现非 root 用户下的开机自启与进程守护。
🎯 适用人群
追求环境纯净、拒绝污染系统库的运维工程师。
需要在离线环境或受限服务器部署 Node 应用的开发者。
希望构建高可用 Cronicle 集群但受限于网络环境(跨 VPC/云服务器)的技术团队。
一、完整部署脚本
流程:安装独立 pnpm → 利用其内置 Node 安装依赖 → 构建 Cronicle → 通过Systemd User Mode实现非 root 用户下的开机自启。
保存为 deploy_cronicle_tarball.sh,直接运行即可:
#!/bin/bash
set -e
# ========== 配置区 ==========
BASE_DIR="/opt/cronicle"
CONFIG_FILE="$BASE_DIR/conf/config.json"
CRONICLE_VERSION="master" # 或指定如 "v0.9.116"
BASE_APP_URL="http://localhost:13012"
# ============================
echo "[INFO] 部署目录: $BASE_DIR"
echo "[INFO] 配置文件: $CONFIG_FILE"
echo "[INFO] 安装版本: $CRONICLE_VERSION"
echo "[INFO] 访问地址: $BASE_APP_URL"
mkdir -p "$BASE_DIR"
cd "$BASE_DIR"
# 阶段 1:安装独立版 pnpm (自带 Node 运行时)
echo -e "\n[1] 安装独立版 pnpm..."
wget -qO- https://get.pnpm.io/install.sh | sh -
source ~/.bashrc
export PNPM_HOME="$HOME/.local/share/pnpm"
export PATH="$PNPM_HOME/bin:$PATH"
# 安装 Node.js 的 LTS 版本
pnpm runtime set node lts -g
echo "[OK] pnpm 版本: $(pnpm -v) | NodeJS 版本: $(node -v)"
# 阶段 2:下载 Cronicle 源码 Tarball
echo -e "\n[2] 下载 Cronicle 源码包 (版本: $CRONICLE_VERSION)..."
TARBALL_URL="https://ghfast.top/github.com/jhuckaby/Cronicle/archive/${CRONICLE_VERSION}.tar.gz"
curl -L "$TARBALL_URL" | tar zxvf - --strip-components 1
# 阶段 3:使用 pnpm 安装依赖并构建
echo -e "\n[3] 安装依赖(使用独立版 pnpm)..."
pnpm install
# 构建前端资源(关键:使用 pnpm 自带的 Node 运行时)
echo -e "\n[4] 构建前端资源..."
node bin/build.js dist
echo -e "\n[5] 修改 Cronicle 配置..."
cp "$CONFIG_FILE" "${CONFIG_FILE}.bak.$(date +%F_%T)"
# 设置访问和通信地址
sed -i 's#"base_app_url"[[:space:]]*:[^,]*#"base_app_url": "'"${BASE_APP_URL}"'"#' "$CONFIG_FILE"
# 设置自动清理已完成 Job 的运行数据和日志的时间阈值
sed -i 's#"job_data_expire_days"[[:space:]]*:[^,]*#"job_data_expire_days": 15#' "$CONFIG_FILE"
# 设置日志归档路径
sed -i 's#"log_archive_path"[[:space:]]*:[^,]*#"log_archive_path": "logs/archives/[yyyy]-[mm]-[dd]/[filename]-[yyyy]-[mm]-[dd].log.gz"#' "$CONFIG_FILE"
# 设置http访问端口,必须大于8000
sed -i 's#"http_port"[[:space:]]*:[^,]*#"http_port": 13012#' "$CONFIG_FILE"
# UDP 广播(udp_broadcast_port)仅用于局域网内的自动发现。在跨网段或云环境等无法进行 UDP 广播的场景下,可完全禁用 UDP,改用手动指定 Master 的方式建立连接。
# 设为 0 禁用 UDP 广播
sed -i 's#"udp_broadcast_port"[[:space:]]*:[^,]*#"udp_broadcast_port": 0#' "$CONFIG_FILE"
# 开启后:使用主机名(hostname)通信,关闭时:优先使用 IP 地址通信
sed -i 's#"server_comm_use_hostnames"[[:space:]]*:[^,]*#"server_comm_use_hostnames": false#' "$CONFIG_FILE"
# 强制直连(不走广播)
sed -i 's#"web_direct_connect"[[:space:]]*:[^,]*#"web_direct_connect": true#' "$CONFIG_FILE"
grep -E '"base_app_url"|"job_data_expire_days"|"log_archive_path"|"http_port"|"udp_broadcast_port"|"server_comm_use_hostnames"|"web_direct_connect"' "$CONFIG_FILE"
echo -e "\n[SUCCESS] Cronicle 安装完成!"
echo -e "\n运行以下脚本初始化存储系统。只需在主服务器(Master)上执行一次。不要在任何工作服务器(Worker)上运行:"
echo "${BASE_DIR}/bin/control.sh setup"二、Systemd 用户自启动配置
创建
~/.config/systemd/user/cronicle.service:[Unit] Description=Cronicle Scheduler (User Mode) After=network.target [Service] Type=forking PIDFile=/opt/cronicle/logs/cronicled.pid ExecStart=/opt/cronicle/bin/control.sh start ExecStop=/opt/cronicle/bin/control.sh stop ExecReload=/opt/cronicle/bin/control.sh restart # 注意把 /home/rpa/.local/share/pnpm/bin 修改你的 node 所在目录 Environment=PATH=/home/rpa/.local/share/pnpm/bin:/usr/local/bin:/usr/bin:/bin WorkingDirectory=/opt/cronicle Restart=always RestartSec=10 [Install] WantedBy=default.target启动服务
systemctl --user daemon-reload systemctl --user enable --now cronicle # 启用并立即启动停止/重启服务
systemctl --user stop cronicle systemctl --user restart cronicle卸载服务
systemctl --user stop cronicle systemctl --user disable cronicle rm -f ~/.config/systemd/user/cronicle.service systemctl --user daemon-reload
三、Worker 和 Master 的两种通信方式
在 Cronicle 中,Worker 与 Master 的通信方式只有两种,且互斥(二选一)。
1. 两种通信方式总览
2. 方式一:UDP 广播发现
🔧 工作原理
Worker 启动后,向 UDP 广播地址(如
255.255.255.255)发送发现包。Master 监听
udp_broadcast_port(默认3014)。Master 收到广播后,通过反向连接将 Worker 加入集群。
⚙️ 所需配置(双方一致)
"base_app_url": "http://master:13012" // ✅ Master 访问地址
"udp_broadcast_port": 3014,
"server_comm_use_hostnames": false,
"web_direct_connect": false3. 方式二:TCP / HTTP 直连
🔧 工作原理
Worker 启动后,直接读取自己的
config.json。使用
base_app_url作为 Master 地址。通过 HTTP / WebSocket 主动向 Master 注册。
后续所有心跳、任务分发均通过 TCP 进行。
⚙️所需配置(双方一致)
"base_app_url": "http://master:13012" // ✅ Master 访问地址
"udp_broadcast_port": 0, // ✅ 禁用 UDP
"server_comm_use_hostnames": false, // ✅ 使用IP进行通信,为true时使用主机名hostname进行通信
"web_direct_connect": true, // ✅ 强制 TCP 直连✅ 网络要求
4. 两种方式的关键差异对照表
5. Worker 连接 Master 的完整数据流(TCP 模式)
[Worker 启动]
↓
读取 config.json
↓
检测到 udp_broadcast_port = 0
↓
启用 web_direct_connect = true
↓
向 base_app_url 发起 HTTP 注册
↓
Master 验证 secret_key
↓
建立 WebSocket 长连接
↓
开始接收调度任务四、Worker 部署步骤
按上述第二步已部署有一个运行正常的 Master(端口 13012),现在要部署 Worker:
1. 准备配置文件(关键步骤)
Worker 的 config.json必须与 Master 完全一致(除了个别网络参数),否则无法加入集群。
必须保持一致的参数:
secret_key:必须相同,这是集群的“密码”,不同则无法连接。udp_broadcast_port:必须相同(默认 3014), 基于TCP应设置为 0。base_app_url:通常指向 Master 的地址(用于 Web 界面跳转)。
Worker 独有的配置(可选):
如果 Worker 位于 NAT 或负载均衡后,需设置
hostname和ip为对外可见的地址。
2. 启动 Worker 的绝对禁忌⚠️
Worker 节点严禁执行 /opt/cronicle/bin/control.sh setup。setup命令会初始化新的存储库/数据库,导致它与 Master 数据不一致。
正确的启动命令:
# 执行
/opt/cronicle/bin/control.sh start
# 或者执行
systemctl -user start cronicle