主题
Linux 部署
生产环境首选。适用于 x86_64 的 Ubuntu、Debian、CentOS / Rocky / AlmaLinux、openEuler、麒麟 V10(x86 版)、统信 UOS(x86 版)等发行版。可执行文件是静态编译的,不依赖 glibc 版本,也不需要安装任何运行库。
使用交付包:linux-amd64.zip。
三种托管方式任选其一:
| 方式 | 适合 | 开机自启 | 崩溃自动拉起 |
|---|---|---|---|
| 一、systemd 托管(推荐) | 长期运行 | ✅ | ✅ |
| 二、start.sh 脚本 | 临时验证 | ❌ | ❌ |
| 三、deploy.sh 远程推送 | 包在你的电脑上,目标是远程服务器 | 取决于服务器上的托管方式 | 同左 |
想用 Docker 的看 Linux(Docker)。
1. 准备服务器
1.1 确认架构
bash
uname -m输出 x86_64 才能用 linux-amd64 包。输出 aarch64 说明是 ARM 服务器,需要联系我方单独构建。
1.2 创建运行用户与目录
不要用 root 运行服务:
bash
sudo useradd --system --create-home --home-dir /opt/apexpm --shell /usr/sbin/nologin apexpm1.3 放置文件
把交付包传到服务器并解压:
bash
# 在你的电脑上
scp acme-apexpm-vX.Y.Z.tar.gz user@服务器IP:/tmp/
# 在服务器上
sudo tar xzf /tmp/acme-apexpm-vX.Y.Z.tar.gz -C /opt/apexpm --strip-components=1
sudo chmod +x /opt/apexpm/apexpm /opt/apexpm/*.sh
sudo chown -R apexpm:apexpm /opt/apexpm收到的是 zip 时用 unzip linux-amd64.zip -d /opt/apexpm。
1.4 放行端口
bash
# firewalld(CentOS / Rocky / openEuler / 麒麟)
sudo firewall-cmd --permanent --add-port=9000/tcp && sudo firewall-cmd --reload
# ufw(Ubuntu / Debian)
sudo ufw allow 9000/tcp云服务器还要在控制台的安全组里放行同一端口。
常见错误
| 现象 | 原因 | 处理 |
|---|---|---|
cannot execute binary file: Exec format error | 架构不符,比如把 amd64 包放到了 ARM 服务器上 | 按 1.1 确认架构,换对应的包 |
Permission denied(执行 ./apexpm 时) | 文件没有可执行权限,或所在分区挂载了 noexec | chmod +x apexpm;mount | grep noexec 检查,换到其他分区 |
useradd: user 'apexpm' already exists | 用户已存在 | 可以忽略,直接进行下一步 |
浏览器访问超时,服务器上 curl 127.0.0.1:9000 正常 | 防火墙或安全组没放行 | 按 1.4 放行,检查云控制台安全组 |
2. 配置
bash
cd /opt/apexpm
[ -f config.yaml ] || sudo -u apexpm cp config.example.yaml config.yaml
sudo -u apexpm vi config.yamlLinux 上至少改这几项:
| 字段 | 怎么改 |
|---|---|
jwt.secret | openssl rand -hex 32 生成后填入 |
server.port | 需要换端口时修改,同时改防火墙规则 |
server.public_base_url | 填 http://<服务器内网IP>:9000,多网卡时尤其要填 |
license.webhook.url | 填飞书 / 钉钉机器人地址 |
每个字段的完整说明见配置文件 config.yaml。
确认授权文件就位:
bash
ls -l /opt/apexpm/data/license.lic确认授权绑定的 IP 与本机网卡一致:
bash
ip -4 addr show | grep inet常见错误
| 现象 | 原因 | 处理 |
|---|---|---|
config.yaml 保存后启动报 yaml: line N | 缩进用了 Tab 或冒号后没有空格 | vi 里 :set list 显示 Tab,改成空格 |
ls: cannot access 'data/license.lic' | 授权文件没放进去 | 把我方提供的 .lic 复制到 data/license.lic,注意文件名 |
ip addr 里没有签发授权时提供的 IP | 服务器 IP 变过,或提供的是公网 IP(云服务器公网 IP 不在网卡上) | 把 ip addr 里看到的内网 IP 发给我方重新签发 |
方式一:systemd 托管(推荐)
创建服务文件 /etc/systemd/system/apexpm.service:
ini
[Unit]
Description=ApexPM
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=apexpm
Group=apexpm
# 必须是部署目录:程序在这里找 config.yaml 并创建 data/
WorkingDirectory=/opt/apexpm
ExecStart=/opt/apexpm/apexpm
Restart=on-failure
RestartSec=5
# 停止时给 10 秒优雅退出
TimeoutStopSec=10
LimitNOFILE=65535
[Install]
WantedBy=multi-user.target启用并启动:
bash
sudo systemctl daemon-reload
sudo systemctl enable --now apexpm
sudo systemctl status apexpm查看日志:
bash
journalctl -u apexpm -f # 实时
journalctl -u apexpm -n 100 --no-pager # 最近 100 行日常操作:
bash
sudo systemctl restart apexpm # 改完 config.yaml 后
sudo systemctl stop apexpm常见错误
| 现象 | 原因 | 处理 |
|---|---|---|
status=203/EXEC | ExecStart 路径错或文件不可执行 | 核对路径,chmod +x /opt/apexpm/apexpm |
status=200/CHDIR | WorkingDirectory 目录不存在 | 核对路径 |
status=217/USER | 服务文件里的 User 不存在 | 按 1.2 创建用户,或改成已有用户 |
日志 create data dir failed ... permission denied | 运行用户对部署目录没有写权限 | sudo chown -R apexpm:apexpm /opt/apexpm |
日志 bind: address already in use | 端口被占用 | sudo ss -lntp | grep 9000 找到占用进程;或改 server.port |
Unit apexpm.service not found | 没执行 daemon-reload,或文件名、位置不对 | 确认文件在 /etc/systemd/system/ 下后执行 daemon-reload |
进程反复重启,systemctl status 显示 activating (auto-restart) | 启动即退出,被 Restart=on-failure 不断拉起 | journalctl -u apexpm -n 50 看退出前的 "level":"fatal" 日志,对照故障排查总表 |
方式二:start.sh 脚本
适合临时验证。不开机自启,进程挂了也不会自动拉起。
bash
cd /opt/apexpm
sudo -u apexpm ./start.sh # 后台启动,pid 写 apexpm.pid,日志追加到 apexpm.log
sudo -u apexpm ./stop.sh # 优雅停止,10 秒未退出则强制结束
tail -f apexpm.log不要和 systemd 混用
同一个部署目录只用一种托管方式。已经用 systemd 托管时再执行 start.sh,第二个进程会因端口被占用而退出。
常见错误
| 输出 | 原因 | 处理 |
|---|---|---|
Error: config.yaml not found. | 部署目录下没有 config.yaml | 按第 2 步从模板复制 |
apexpm is already running (pid N) | 已经在运行 | 不需要处理;要重启就先 ./stop.sh |
apexpm started 但访问不了 | 进程启动后立即退出了 | tail -50 apexpm.log 看 fatal 日志 |
stop.sh 输出 timed out, sending SIGKILL | 10 秒内没退出,被强制结束 | 偶发可忽略;频繁出现请保留日志联系我方 |
apexpm is not running (stale pid N) | 进程已经不在,pid 文件是残留 | 脚本已自动清理,直接 ./start.sh |
方式三:deploy.sh 远程推送
交付包在你的电脑上(Linux 或 macOS),服务要跑在远程 Linux 服务器上时使用。脚本不编译任何东西,只推送文件并重启。
前提:本机有 ssh 和 rsync,且能免密登录目标服务器(ssh-copy-id user@服务器IP)。服务器上也需要装 rsync 和 curl。
bash
cd apexpm-vX.Y.Z # 解压后的交付包目录
SERVER=ubuntu@1.2.3.4 ./deploy.sh
SERVER=ubuntu@1.2.3.4 DEPLOY_DIR=/opt/apexpm ./deploy.sh # 默认 /home/deploy/apexpm脚本依次完成四步:
- 检查:本机工具、交付包完整性、包内二进制与服务器架构是否匹配
- 推送:二进制先推为
apexpm.new,同步start.sh/stop.sh/config.example.yaml/data/license.lic - 备份 + 重启:已有数据库时先把
data/备份为data.bak-时间戳(保留最近 5 份),然后停服、换二进制、启动。首次部署会从模板生成config.yaml - 健康检查:最多等 18 秒请求
/health,成功后打印授权状态
首次部署后必须改配置
首次部署时 config.yaml 是从模板直接生成的,jwt.secret 仍是默认值。登录服务器修改后执行 ./stop.sh && ./start.sh。
与 systemd 搭配
deploy.sh 用 start.sh / stop.sh 启停进程。服务器已改用 systemd 托管时,不要再用 deploy.sh,改用手工升级。
常见错误
| 输出 | 原因 | 处理 |
|---|---|---|
请设置 SERVER | 没带 SERVER 变量 | SERVER=user@IP ./deploy.sh |
!! 本机缺少 rsync | 本机没装 rsync | macOS:brew install rsync;Ubuntu:sudo apt install rsync |
!! 当前目录没有 apexpm | 不在交付包目录里执行 | cd 到解压后的目录再执行 |
Permission denied (publickey) | 没配置免密登录 | ssh-copy-id user@IP 后重试 |
!! 包里的二进制与服务器架构不符 | 包与服务器架构不一致 | 换对应架构的包;ARM 服务器联系我方 |
!! 未知服务器架构 | 服务器既不是 x86_64 也不是 aarch64 | 不支持该架构 |
rsync: command not found(远端) | 服务器上没装 rsync | 在服务器上安装 rsync |
mkdir: cannot create directory ... Permission denied | SSH 用户对 DEPLOY_DIR 的上级目录没有写权限 | 换一个有权限的 DEPLOY_DIR,或先在服务器上建好目录并 chown |
!! 服务未就绪,日志末尾: | 启动失败或 18 秒内没起来 | 看脚本打印的日志末尾,对照故障排查总表。数据已备份,可以放心排查 |
可选:Nginx 反向代理与 HTTPS
需要用域名或 HTTPS 访问时,在前面加一层 Nginx:
nginx
server {
listen 443 ssl;
server_name pm.example.com;
ssl_certificate /etc/nginx/certs/pm.example.com.pem;
ssl_certificate_key /etc/nginx/certs/pm.example.com.key;
# 附件上传大小上限,按需调整
client_max_body_size 200m;
location / {
proxy_pass http://127.0.0.1:9000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 300s;
}
}同时把 config.yaml 的 server.public_base_url 改成 https://pm.example.com,否则电子签名二维码里的链接会指向内网地址。
| 现象 | 原因 | 处理 |
|---|---|---|
上传附件报 413 Request Entity Too Large | Nginx 默认只允许 1 MB 的请求体 | 调大 client_max_body_size |
导出大报表时报 504 Gateway Time-out | 代理超时 | 调大 proxy_read_timeout |
| 扫码签名打开的是内网地址 | 没配 public_base_url | 按上文配置 |
升级
升级只换可执行文件,config.yaml 和 data/ 一律保留。完整说明与回滚见版本升级。
systemd 托管:
bash
cd /opt/apexpm
sudo systemctl stop apexpm
sudo -u apexpm cp -a data "data.bak-$(date +%Y%m%d-%H%M%S)" # 必做
sudo unzip -o /tmp/linux-amd64.zip apexpm -d /opt/apexpm
sudo chmod +x apexpm && sudo chown apexpm:apexpm apexpm
sudo systemctl start apexpm
curl -fsS http://127.0.0.1:9000/health && journalctl -u apexpm -n 30 --no-pagerstart.sh 托管: 同上,把 systemctl stop/start 换成 ./stop.sh / ./start.sh。
deploy.sh: 在本机的新版本交付包目录里重新执行一次 SERVER=... ./deploy.sh,备份与替换都会自动完成。
卸载
bash
sudo systemctl disable --now apexpm
sudo rm /etc/systemd/system/apexpm.service && sudo systemctl daemon-reload
# 确认 data/ 已备份后再删
sudo rm -rf /opt/apexpm && sudo userdel apexpm