主题
Windows 部署
适用于 Windows 10 / 11 与 Windows Server 2016 及以上的 x64 版本。
使用交付包:windows-amd64.zip。Windows 包里只有 apexpm.exe、config.example.yaml 和 changelog/,没有启停脚本,需要自己把它注册成 Windows 服务。本文用 WinSW(推荐,单个 exe、配置是 XML)和 NSSM 两种工具各给出一套步骤,选一种即可。
以下命令都在以管理员身份运行的 PowerShell 中执行。
1. 准备
1.1 放置文件
建议部署到 C:\apexpm,路径里不要有中文和空格:
powershell
New-Item -ItemType Directory -Force C:\apexpm | Out-Null
Expand-Archive -Path "$env:USERPROFILE\Downloads\windows-amd64.zip" -DestinationPath C:\apexpm -Force
New-Item -ItemType Directory -Force C:\apexpm\data | Out-Null从我方拿到的授权文件放到 C:\apexpm\data\license.lic。
解除文件锁定
从网络下载的 exe 可能被标记为「来自其他计算机」,运行时被拦截。执行一次:
powershell
Get-ChildItem C:\apexpm -Recurse | Unblock-File1.2 放行端口
powershell
New-NetFirewallRule -DisplayName "ApexPM" -Direction Inbound -Protocol TCP -LocalPort 9000 -Action Allow云服务器还要在控制台的安全组里放行同一端口。
1.3 确认授权 IP
powershell
Get-NetIPAddress -AddressFamily IPv4 | Select-Object IPAddress, InterfaceAlias签发授权时提供的 IP 必须出现在这个列表里。
常见错误
| 现象 | 原因 | 处理 |
|---|---|---|
| 双击 exe 提示「此应用无法在你的电脑上运行」 | 32 位系统或 ARM 版 Windows | 只支持 x64 版 Windows |
| 「Windows 已保护你的电脑」(SmartScreen) | exe 未做代码签名 | 执行上面的 Unblock-File;或点「更多信息 → 仍要运行」 |
杀毒软件删除或隔离了 apexpm.exe | 误报 | 把 C:\apexpm 加入杀毒软件白名单后重新解压 |
Expand-Archive 报找不到路径 | zip 路径不对 | 核对下载位置 |
New-NetFirewallRule 报「拒绝访问」 | PowerShell 不是管理员身份 | 右键 PowerShell →「以管理员身份运行」 |
2. 配置
powershell
cd C:\apexpm
Copy-Item config.example.yaml config.yaml
notepad config.yamlWindows 上至少改这几项:
| 字段 | 怎么改 |
|---|---|
jwt.secret | 填一串 32 位以上的随机字符,可用下面的命令生成 |
server.public_base_url | 填 http://<本机内网IP>:9000 |
license.webhook.url | 填飞书 / 钉钉机器人地址 |
powershell
-join ((48..57)+(97..102) | Get-Random -Count 64 | ForEach-Object {[char]$_})路径类的配置保持默认的相对路径(./data/...)即可。一定要写绝对路径时,用正斜杠或单引号,避免反斜杠被 YAML 当成转义:
yaml
license:
path: 'D:\apexpm-data\license.lic' # 单引号:反斜杠原样保留
# 或 path: D:/apexpm-data/license.lic字段完整说明见配置文件 config.yaml。
记事本的编码
Windows 10 1903 之后的记事本默认保存为 UTF-8,没有问题。更早的系统请用「另存为 → 编码 UTF-8」,否则中文注释会让 YAML 解析失败。
先前台试跑一次
注册服务之前,先在前台跑一次,确认配置没问题:
powershell
cd C:\apexpm
.\apexpm.exe看到 "message":"apexpm starting" 后,另开一个窗口验证:
powershell
curl.exe -fsS http://127.0.0.1:9000/health
curl.exe -fsS http://127.0.0.1:9000/api/v1/license/status确认无误后按 Ctrl+C 停止,进入下一步。
注意用 curl.exe
Windows PowerShell 5.1 里的 curl 是 Invoke-WebRequest 的别名,参数不同。请写 curl.exe。
常见错误
| 日志 / 现象 | 原因 | 处理 |
|---|---|---|
config load failed ... yaml: line N | YAML 格式错,常见于反斜杠路径写在双引号里 | 按上文用单引号或正斜杠 |
bind: Only one usage of each socket address | 端口被占用 | netstat -ano | findstr :9000 找到 PID,tasklist /FI "PID eq N" 查看是哪个程序;或改 server.port |
bind: An attempt was made to access a socket in a way forbidden by its access permissions | 端口落在 Windows 保留端口范围内(常见于装了 Hyper-V / WSL) | netsh interface ipv4 show excludedportrange protocol=tcp 查看保留范围,换一个不在范围内的端口 |
授权状态 License 文件缺失 | data\license.lic 不存在,或文件被存成了 license.lic.txt | 在资源管理器里打开「查看 → 文件扩展名」,确认真实文件名 |
3. 注册为 Windows 服务
方式一:WinSW(推荐)
- 从 WinSW Releases 下载
WinSW-x64.exe,放到C:\apexpm\,重命名为apexpm-service.exe。 - 在同目录创建
apexpm-service.xml(文件名必须与 exe 同名):
xml
<service>
<id>apexpm</id>
<name>ApexPM</name>
<description>ApexPM 项目管理系统</description>
<executable>%BASE%\apexpm.exe</executable>
<!-- 必须是部署目录:程序在这里找 config.yaml 并创建 data\ -->
<workingdirectory>%BASE%</workingdirectory>
<startmode>Automatic</startmode>
<delayedAutoStart>true</delayedAutoStart>
<onfailure action="restart" delay="10 sec"/>
<stoptimeout>10 sec</stoptimeout>
<logpath>%BASE%\logs</logpath>
<log mode="roll-by-size">
<sizeThreshold>10240</sizeThreshold>
<keepFiles>8</keepFiles>
</log>
</service>- 安装并启动:
powershell
cd C:\apexpm
.\apexpm-service.exe install
.\apexpm-service.exe start
.\apexpm-service.exe status日志在 C:\apexpm\logs\apexpm-service.out.log(按 10 MB 滚动,保留 8 份)。
日常操作:
powershell
.\apexpm-service.exe restart # 改完 config.yaml 后
.\apexpm-service.exe stop
.\apexpm-service.exe uninstall # 卸载服务(不删除文件)也可以在「服务」管理器(services.msc)里操作名为 ApexPM 的服务。
方式二:NSSM
- 从 nssm.cc 下载,把
win64\nssm.exe放到C:\apexpm\。 - 注册:
powershell
cd C:\apexpm
.\nssm.exe install apexpm C:\apexpm\apexpm.exe
.\nssm.exe set apexpm AppDirectory C:\apexpm
.\nssm.exe set apexpm DisplayName ApexPM
.\nssm.exe set apexpm Start SERVICE_DELAYED_AUTO_START
.\nssm.exe set apexpm AppStdout C:\apexpm\logs\apexpm.log
.\nssm.exe set apexpm AppStderr C:\apexpm\logs\apexpm.log
.\nssm.exe set apexpm AppRotateFiles 1
.\nssm.exe set apexpm AppRotateBytes 10485760
.\nssm.exe set apexpm AppExit Default Restart
New-Item -ItemType Directory -Force C:\apexpm\logs | Out-Null
.\nssm.exe start apexpmAppDirectory 这一行不能省,它就是工作目录。
日常操作:.\nssm.exe restart apexpm / stop / status,卸载用 .\nssm.exe remove apexpm confirm。
常见错误
| 现象 | 原因 | 处理 |
|---|---|---|
WinSW 报 Unable to locate apexpm-service.xml | XML 文件名与 exe 名不一致 | 两者去掉扩展名后必须同名 |
WinSW 报 .NET Framework 相关错误 | 下载的是依赖 .NET 的版本 | 下载 WinSW-x64.exe(自包含版本) |
install 报「拒绝访问」 | PowerShell 不是管理员身份 | 以管理员身份运行 |
| 「指定的服务已存在」 | 之前装过同名服务 | 先 uninstall(或 nssm remove apexpm confirm)再装 |
| 服务启动后很快变为「已停止」 | 程序启动即退出 | 看 logs\ 里的日志,找 "level":"fatal" 那一行,对照故障排查总表 |
| 服务在跑,但数据是空的、配置没生效 | 工作目录没设对,程序在 C:\Windows\System32 下找配置、建 data\ | WinSW 检查 <workingdirectory>;NSSM 检查 AppDirectory。改好后删掉 C:\Windows\System32\data 这个误建的目录 |
| 服务以 LocalSystem 运行,访问网络共享盘失败 | LocalSystem 没有网络共享权限 | 数据不要放共享盘;确有需要时在服务属性「登录」页改用有权限的域账户 |
升级
powershell
cd C:\apexpm
# 1) 停服务
.\apexpm-service.exe stop # NSSM:.\nssm.exe stop apexpm
# 2) 备份(必做)
$ts = Get-Date -Format "yyyyMMdd-HHmmss"
Copy-Item -Recurse data "data.bak-$ts"
# 3) 只替换 exe
Expand-Archive -Path "$env:USERPROFILE\Downloads\windows-amd64.zip" -DestinationPath "$env:TEMP\apexpm-new" -Force
Copy-Item "$env:TEMP\apexpm-new\apexpm.exe" C:\apexpm\apexpm.exe -Force
Unblock-File C:\apexpm\apexpm.exe
# 4) 启动并验证
.\apexpm-service.exe start # NSSM:.\nssm.exe start apexpm
curl.exe -fsS http://127.0.0.1:9000/health| 现象 | 原因 | 处理 |
|---|---|---|
| 替换 exe 时报「文件正在使用」 | 服务没停干净 | Get-Process apexpm 确认进程已退出;仍在就 Stop-Process -Name apexpm |
完整说明与回滚见版本升级。
卸载
powershell
cd C:\apexpm
.\apexpm-service.exe stop; .\apexpm-service.exe uninstall # 或 nssm stop / remove
Remove-NetFirewallRule -DisplayName "ApexPM"
# 确认 data\ 已备份后再删除 C:\apexpm