准备一个小型受控环境
本过程针对带有 systemd 的 Linux 系统、管理员账户、已安装的 Node.js 24 LTS 运行时、curl 和 Caddy 的打包系统服务。请先确认已安装版本和包路径; Node 的发布页面 标识支持的发布线。安装和提供商配置是单独的任务。使用您控制的机器,并保持一个经过测试的 SSH 会话和恢复路径可用。以下名称在创建此示例前必须未被使用: first-api 和 /opt/first-api 在创建此示例前必须未被使用。
对于 HTTPS,您还需要一个您控制的域、正确的 A/AAAA 记录以及暴露 Web 流量的权限。 api.example.com 下方是保留示例:请将其替换为您自己的主机名。此 API 故意不包含数据库、身份验证或客户数据。它演示可重复的过程,而不是完整产品或经过测试的 OffVPS 部署。
验证运行时并创建一个发布
command -v node &&
readlink -f "$(command -v node)" &&
node --version
示例其余部分假设已验证的共享可执行文件为 /usr/bin/node。如果您的不同,请在每个检查和 ExecStart中替换该路径。位于您登录用户私有主目录中的运行时不会自动对系统服务可用。创建服务账户和 root 拥有的发布目录,如果发现意外的现有账户或路径则停止。
sudo useradd --system --user-group --home-dir /opt/first-api \
--shell /usr/sbin/nologin first-api &&
sudo install -d -o root -g root -m 0755 /opt/first-api/releases/001
使用具有管理权限的编辑器,将以下内容保存为 /opt/first-api/releases/001/server.mjs,由 root 拥有,并可由服务用户读取。发布仅包含此文件;没有包依赖或机密。
import http from 'node:http';
const port = Number(process.env.PORT || 3000);
if (!Number.isInteger(port) || port < 1024 || port > 65535) {
throw new Error('PORT must be an integer from 1024 to 65535');
}
const server = http.createServer((req, res) => {
res.setHeader('Content-Type', 'application/json; charset=utf-8');
res.setHeader('Cache-Control', 'no-store');
if (req.method !== 'GET') {
res.writeHead(405, { Allow: 'GET' });
res.end(JSON.stringify({ error: 'method_not_allowed' }));
return;
}
if (req.url === '/healthz') {
res.writeHead(200);
res.end(JSON.stringify({ status: 'ok', release: '001' }));
} else if (req.url === '/api/message') {
res.writeHead(200);
res.end(JSON.stringify({ message: 'A small app, running clearly.' }));
} else {
res.writeHead(404);
res.end(JSON.stringify({ error: 'not_found' }));
}
});
server.requestTimeout = 10000;
server.headersTimeout = 10000;
server.keepAliveTimeout = 5000;
server.listen(port, '127.0.0.1');
process.on('SIGTERM', () => {
server.close(() => process.exit(0));
setTimeout(() => process.exit(1), 10000).unref();
});
显式回环地址将 API 监听器保留在服务器本身上。Caddy 将作为其公共入口点。 Node HTTP API 记录了请求处理、超时和服务器关闭。请以实际执行它的账户检查文件:
sudo -u first-api /usr/bin/node --check /opt/first-api/releases/001/server.mjs
为进程提供服务定义
保存 /etc/systemd/system/first-api.service 并使用以下内容:
[Unit]
Description=First API learning release
After=network.target
StartLimitIntervalSec=60
StartLimitBurst=5
[Service]
Type=simple
User=first-api
Group=first-api
WorkingDirectory=/opt/first-api/releases/001
ExecStart=/usr/bin/node /opt/first-api/releases/001/server.mjs
Environment=NODE_ENV=production
Environment=PORT=3000
Restart=on-failure
RestartSec=5
TimeoutStopSec=15
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
[Install]
WantedBy=multi-user.target
该单元使用一个专用用户、显式可执行文件和带版本的工作目录。自动恢复有速率限制;有意停止服务不会触发 Restart=on-failure。请参阅 systemd.service。文件系统限制适合此只读 API;写入数据的应用需要有意限定范围的可写存储。机密不应出现在这些环境行中。请参阅 systemd 执行设置.
sudo systemd-analyze verify /etc/systemd/system/first-api.service &&
sudo systemctl daemon-reload &&
sudo systemctl start first-api.service &&
sudo systemctl status first-api.service --no-pager &&
curl --fail --show-error http://127.0.0.1:3000/healthz
该 && 防护会在命令失败时停止粘贴的序列。启动前解决验证警告,并且检查失败后不要继续到下一个块。 单元验证 可以捕获语法和可执行文件问题,但成功检查并不能证明应用正常工作。预期的健康响应为 {"status":"ok","release":"001"}。如果失败,请检查 sudo journalctl -u first-api.service -n 50 --no-pager ,而不是反复重启。
本地检查后添加 HTTPS 路由
在未使用的文件名下备份现有 Caddy 配置。将以下块添加到 /etc/caddy/Caddyfile 中,且不要替换无关站点:
api.example.com {
reverse_proxy 127.0.0.1:3000
}
对于标准公共域流程,主机名必须解析到服务器,端口 80/443 必须可达 Caddy,并且 Caddy 的证书存储必须保持可写和持久。检查每个已发布的 A/AAAA 路由。保持 SSH 访问不受影响,并保持端口 3000 私有。这些要求来自 Caddy 自动 HTTPS;上游语法记录在 reverse_proxy.
sudo caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile &&
sudo systemctl reload caddy.service
仅在验证成功后重新加载; caddy validate 检查适配后的配置。打包服务工作流程描述在 Caddy 的服务指南。从单独的客户端,使用您的真实主机名请求 https://api.example.com/healthz 。预期获得受信任的 TLS 连接和相同的发布响应,且不绕过证书检查。然后验证 /api/message 返回其消息,未知路径返回 404。
保留已知发布和安全停止点
两项检查都正常后,使用以下命令为未来启动启用 API: sudo systemctl enable first-api.service。记录运行时版本、源文件、单元和 Caddy 配置。对于下一个发布,创建新的编号目录,以服务用户进行语法检查,更新两个单元路径,重新加载 systemd 并重启 API。在新发布被接受之前,保留先前的目录。
要停止此示例,请使用 sudo systemctl stop first-api.service。当该路由仍配置时,Caddy 将报告上游失败;退役时仅移除此路由并验证/重新加载 Caddy。选择先前的单元路径并重复检查,以回滚失败的代码发布。后续数据库迁移需要自己的恢复计划。继续阅读 从 DNS 到应用的请求路径 或 查找应用停止的原因.
使用的文档
本页的主要参考资料。检查您自己环境中安装版本的文档。