非root权限下部署 Cronicle 单主集群实战

本文提供了 非root权限下 Cronicle 的现代化单主机群部署方案。通过 pnpm 独立版(Standalone),实现 Node 运行时与应用依赖的完全解耦,打造绿色、便携、可复现的调度系统环境。

🛠️ 技术特点

  1. 环境隔离:采用 pnpm env use lts -g方式,无需 nvm/fnm。

  2. 源码构建:通过 Tarball 方式安装最新版 Cronicle,利用 pnpm 内置 Node 编译资源,摆脱系统环境依赖。

  3. 数据生命周期:配置 job_data_expire_days: 15自动清理历史记录,配合 log_archive_path实现日志归档分离,兼顾存储成本与审计需求。

  4. 基于IP通信:Master和Worker之间通过IP地址通信,禁止通过主机名;

🏗️ 部署架构

  • Master 节点:执行 control.sh setup初始化存储,负责调度与 Web 管理。

  • Worker 节点:通过复制 Master 配置并直接 start,由 UDP 机制自动注册加入集群。

  • 运维管理:提供完整的 Systemd User Mode 配置,实现非 root 用户下的开机自启与进程守护。

🎯 适用人群

  • 追求环境纯净、拒绝污染系统库的运维工程师。

  • 需要在离线环境受限服务器部署 Node 应用的开发者。

  • 希望构建高可用 Cronicle 集群但受限于网络环境(跨 VPC/云服务器)的技术团队。


一、安装部署脚本

流程:安装独立 pnpm → 利用其内置 Node 安装依赖 → 构建 Cronicle → 通过Systemd User Mode实现非 root 用户下的开机自启。

1. 创建cronicle安装目录并赋予普通用户权限

sudo mkdir -p /opt/cronicle
sudo chown -R [username]:[usergroup] /opt/cronicle
cd /opt/cronicle

2. 保存为 deploy_cronicle_tarball.sh,直接运行

#!/bin/bash
set -e

# ========== 配置区 ==========
BASE_DIR="/opt/cronicle"
CONFIG_FILE="$BASE_DIR/conf/config.json"
CRONICLE_VERSION="master"  # 或指定如 "v0.9.117"
BASE_APP_URL="http://192.168.1.100:13012" # 请把ip地址改为所在设备的局域网IP
SECRET_KEY="512a4c43fd3d680ea25bd87658bb5cc2"
# ============================

# 进入Cronicle安装目录
cd "$BASE_DIR"

echo "[INFO] 部署目录: $BASE_DIR"
echo "[INFO] 配置文件: $CONFIG_FILE"
echo "[INFO] 安装版本: $CRONICLE_VERSION"
echo "[INFO] 访问地址: $BASE_APP_URL"

# 阶段 1:安装独立版 pnpm (自带 Node 运行时)
echo -e "\n[1] 安装独立版 pnpm..."
wget https://get.pnpm.io/install.sh -O "$BASE_DIR/install_pnpm.sh"
sed -i 's|https://github.com/|https://ghfast.top/github.com/|g' "$BASE_DIR/install_pnpm.sh"
chmod +x "$BASE_DIR/install_pnpm.sh"
sh "$BASE_DIR/install_pnpm.sh"
source ~/.bashrc # 如果使用zsh,则应 source ~/.zshrc
export PNPM_HOME="$HOME/.local/share/pnpm"
export PATH="$PNPM_HOME/bin:$PATH"
pnpm config set registry https://registry.npmmirror.com
pnpm config set @pnpm:registry https://registry.npmmirror.com

# 安装 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

# 强制pnpm使用包的版本
echo -e "解决Tools.normalizeTime is not a function问题"
echo -e "overrides:\n  pixl-tools: \"2.0.3\"" > /opt/cronicle/pnpm-workspace.yaml

# 阶段 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"

# 设置密钥
sed -i 's#"secret_key"[[:space:]]*:[^,]*#"secret_key": "'"${SECRET_KEY}"'"#' "$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广播端口 13014(udp_broadcast_port)用于局域网内的自动发现。在跨网段或云环境等无法进行 UDP 广播时设置为0。
sed -i 's#"udp_broadcast_port"[[:space:]]*:[^,]*#"udp_broadcast_port": 13014#' "$CONFIG_FILE"

# 设置为关闭,优先使用 IP 地址通信;若开启则使用主机名(hostname)通信​,
sed -i 's#"server_comm_use_hostnames"[[:space:]]*:[^,]*#"server_comm_use_hostnames": false#' "$CONFIG_FILE"

# Web UI 只会连接 URL 中的 hostname/port,适合单服务器架构
sed -i 's#"web_direct_connect"[[:space:]]*:[^,]*#"web_direct_connect": false#' "$CONFIG_FILE"

# 设置为关闭,优先使用 IP 地址通信;若开启则使用主机名(hostname)通信​,
sed -i 's#"web_socket_use_hostnames"[[:space:]]*:[^,]*#"web_socket_use_hostnames": false#' "$CONFIG_FILE"

# 禁用单个 Job 的最大内存限制, 默认值为 1073741824(1GB)(单位:字节),超过会被强制终止
sed -i 's#"job_memory_max"[[:space:]]*:[^,]*#"job_memory_max": 0#' "$CONFIG_FILE"

grep -E '"base_app_url"|"secret_key"|"job_data_expire_days"|"log_archive_path"|"http_port"|"udp_broadcast_port"|"server_comm_use_hostnames"|"web_direct_connect"|"web_socket_use_hostnames"|"job_memory_max"' "$CONFIG_FILE"
echo -e "\n[SUCCESS] Cronicle 安装完成!"

echo -e "\n运行以下脚本初始化存储系统。只需在主服务器(Master)上执行一次。不要在任何工作服务器(Worker)上运行:"
echo "${BASE_DIR}/bin/control.sh setup"

二、Systemd 用户自启动配置

  1. 创建 ~/.config/systemd/user/cronicle.service

    mkdir -p ~/.config/systemd/user && cat > ~/.config/systemd/user/cronicle.service << EOF
    [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
    
    Environment=PATH=$HOME/.local/share/pnpm/bin:/usr/local/bin:/usr/bin:/bin
    WorkingDirectory=/opt/cronicle
    Restart=always
    RestartSec=10
    
    [Install]
    WantedBy=default.target
    EOF
  2. 启动服务

    systemctl --user daemon-reload
    systemctl --user enable --now cronicle  # 启用并立即启动
  3. 停止/重启服务

    systemctl --user stop cronicle
    systemctl --user restart cronicle
  4. 卸载服务

    systemctl --user stop cronicle
    systemctl --user disable cronicle
    rm -f ~/.config/systemd/user/cronicle.service
    systemctl --user daemon-reload

三、Worker 部署步骤

按上述第二步已部署有一个运行正常的 Master(TCP 端口 13012, UDP 端口 13014),现在要部署 Worker:

1. 准备配置文件(关键步骤)

Worker 的 config.json必须与 Master 完全一致(除了个别网络参数),否则无法加入集群。

必须保持一致的参数:

  • secret_key必须相同,这是集群的“密码”,不同则无法连接

  • udp_broadcast_port:必须相同(13014)

  • base_app_url:通常指向 Master 的地址(用于 Web 界面跳转)

2. 启动 Worker 的绝对禁忌⚠️

Worker 节点严禁执行 /opt/cronicle/bin/control.sh setupsetup命令会初始化新的存储库/数据库,导致它与 Master 数据不一致。

正确的启动命令:

# 执行
systemctl -user start cronicle

Linux系统配置优化 2026-05-06
VMware反虚拟机检测的解决方法(Workstation 16+/17+) 2026-05-27