在 Node.js 项目开发进程中,随着项目规模的不断扩大,手动部署所带来的弊端愈发凸显。
频繁的代码更新需要人工操作来完成部署,不仅耗费大量时间,还容易因人为失误导致环境不一致、依赖缺失等问题,严重影响开发效率与项目稳定性。
而一套设计合理的持续集成 / 持续部署(CI/CD)流水线,能有效解决这些痛点。本文将详细介绍如何利用 GitHub Webhooks、PM2 和 Shell 脚本,搭建一个自定义的 CI/CD 服务器,实现 Node.js 应用的自动化部署。
一、了解自定义 CI/CD 服务器核心功能
本次搭建的轻量级 Node.js CI/CD 服务器,具备以下关键能力,为 Node.js 项目部署保驾护航:
- 实时监听代码变动:能够时刻监听 GitHub 仓库特定分支的代码提交事件,一旦有新的代码推送,立即触发后续部署流程,确保项目能及时获取最新代码。
- 请求合法性验证:在接收到 GitHub Webhook 发送的请求时,会对请求 payload 进行验证,只有确认请求来自 GitHub,才会执行部署操作,保障服务器安全,防止恶意请求干扰部署流程。
- 自动化部署执行:通过执行 Shell 脚本(deploy.sh),完成一系列部署操作,包括切换到对应项目目录、拉取最新代码、检查并安装缺失的依赖、使用 PM2 重启项目,确保项目以最新状态稳定运行。
- 部署状态反馈:在部署过程中,会将部署的相关状态信息发送给 GitHub 进行日志记录,方便开发人员随时查看部署情况,及时发现并解决部署中出现的问题。
二、搭建前的准备工作
在正式搭建 CI/CD 服务器之前,需要确保服务器满足以下 prerequisites,为后续搭建工作奠定基础:
- Node.js 环境:需安装 16 版本及以上的 Node.js。可通过 vim 工具下载,也能直接从包管理器中安装,Node.js 是运行 CI/CD 服务器代码的基础环境。
- PM2 进程管理工具:用于高效管理 Node.js 应用,能实现应用的启动、停止、重启等操作,保证应用稳定运行。可通过
npm install -g pm2
命令进行全局安装。 - Git 版本控制工具:用于从 GitHub 仓库拉取最新代码,是获取项目更新的关键工具。在 AlmaLinux 系统中,可通过
sudo dnf install git -y
命令安装。 - Web 服务器(可选):若需要通过域名暴露 CI/CD 服务器,可选择安装 NGINX 或 Apache 作为反向代理,实现域名与服务器的映射。
- GitHub 仓库设置:确保待部署的项目已托管在 GitHub 上,并且拥有配置 Webhooks 的权限,以便实现代码提交与部署流程的联动。
三、逐步搭建 CI/CD 服务器
(一)初始化 Node.js 项目
首先,为 CI/CD 服务器创建一个专门的目录,并在该目录下初始化 Node.js 项目,具体操作如下:
- 打开服务器终端,执行
mkdir ~/cicd-server && cd ~/cicd-server
命令,创建并进入 CI/CD 服务器目录。 - 执行
npm init -y
命令,快速初始化 Node.js 项目,生成 package.json 文件,该文件将记录项目的依赖信息等配置。 - 安装项目所需的依赖包,执行
npm install express body-parser crypto
命令。其中,express 用于搭建 Web 服务器,body-parser 用于解析请求体,crypto 用于进行数据加密,验证 GitHub Webhook 请求的合法性。
(二)构建 Webhook 监听器
Webhook 监听器是 CI/CD 服务器的核心部分,负责接收 GitHub Webhook 发送的请求并进行处理。创建一个名为 index.js 的文件,写入以下代码:
const express = require("express"); const bodyParser = require("body-parser"); const crypto = require("crypto"); const { exec } = require("child_process"); const app = express(); const PORT = 4000; // 从环境变量获取GitHub密钥,若未设置则使用默认密钥,实际生产环境建议从环境变量获取,提高安全性 const GITHUB_SECRET = process.env.GITHUB_SECRET || "your-secret-key"; // 解析JSON格式的请求体 app.use(bodyParser.json()); // 处理GitHub Webhook发送的POST请求 app.post("/webhook", (req, res) => { // 生成请求签名,用于验证请求是否来自GitHub const signature = `sha256=${crypto .createHmac("sha256", GITHUB_SECRET) .update(JSON.stringify(req.body)) .digest("hex")}`; // 对比请求头中的签名与生成的签名,验证请求合法性 if (req.headers["x-hub-signature-256"] !== signature) { return res.status(401).json({ message: "Invalid signature" }); } // 获取仓库名称 const repoName = req.body.repository.name; console.log(`Received update for: ${repoName}`); // 执行部署脚本,传入仓库名称作为参数 exec(`bash ./deploy.sh ${repoName}`, (error, stdout, stderr) => { if (error) { console.error(`Deployment failed: ${stderr}`); return res.status(500).json({ message: "Deployment failed", error: stderr }); } console.log(`Deployment successful: ${stdout}`); res.json({ message: "Deployment successful", output: stdout }); }); }); // 启动服务器,监听指定端口 app.listen(PORT, () => console.log(`CI/CD server running on port ${PORT}`));
(三)编写部署脚本
部署脚本(deploy.sh)用于执行具体的部署操作,包括拉取代码、安装依赖、重启项目等。在 CI/CD 服务器目录下创建 deploy.sh 文件,写入以下代码:
#!/bin/bash # 获取传入的仓库名称参数 REPO_NAME=$1 # 项目基础目录,可根据实际情况修改 BASE_DIR="/var/www/projects" # 具体项目目录 PROJECT_DIR="$BASE_DIR/$REPO_NAME" echo "Starting deployment for $REPO_NAME..." # 检查项目目录是否存在,若不存在则输出错误信息并退出 if [ ! -d "$PROJECT_DIR" ]; then echo "Error: Directory $PROJECT_DIR does not exist." exit 1 fi # 切换到项目目录 cd "$PROJECT_DIR" echo "Pulling latest changes..." # 从GitHub仓库主分支拉取最新代码,若项目使用其他分支,需将main改为对应分支名称 git pull origin main # 获取上一次提交与本次提交之间的文件变更列表 CHANGES=$(git diff --name-only HEAD@{1} HEAD) # 检查变更文件中是否包含package.json,若包含则说明依赖可能发生变化,执行npm install安装依赖 if [[ $CHANGES == *"package.json"* ]]; then echo "Detected dependency changes. Running npm install..." npm install fi # 使用PM2重启项目,确保项目以最新代码运行 echo "Restarting application..." pm2 restart $REPO_NAME echo "Deployment completed."
编写完成后,执行chmod +x deploy.sh
命令,为脚本添加可执行权限,使其能够正常运行。
(四)配置 GitHub Webhooks
要实现 GitHub 仓库代码提交与 CI/CD 服务器部署的联动,需要在 GitHub 仓库中配置 Webhooks,具体步骤如下:
- 登录 GitHub 账号,进入对应的项目仓库,点击仓库页面右上角的 “Settings” 选项,进入仓库设置页面。
- 在设置页面左侧的导航栏中,找到并点击 “Webhooks” 选项,进入 Webhooks 配置页面。
- 点击页面右上角的 “Add webhook” 按钮,进入添加 Webhook 页面。
- 在 “Payload URL” 输入框中,填写 CI/CD 服务器的 Webhook 接收地址,格式为
http://server-ip:4000/webhook
。若已配置 NGINX 或 Apache 反向代理,可将 server-ip 替换为对应的域名,如http://example.com/webhook
。
- 在 “Content type” 下拉菜单中,选择 “application/json”,指定请求体的格式为 JSON。
- 在 “Secret” 输入框中,填写与 CI/CD 服务器 index.js 文件中 GITHUB_SECRET 相同的值,用于验证请求的合法性。
- 在 “Which events would you like to trigger this webhook?” 部分,选择 “Let me select individual events.”,然后勾选 “Just the push event.”,确保只有在代码推送时才触发 Webhook。
- 确认所有配置信息无误后,点击页面底部的 “Add webhook” 按钮,完成 GitHub Webhooks 的配置。
(五)启动 CI/CD 服务器
配置完成后,启动 CI/CD 服务器,使其能够监听 GitHub Webhook 发送的请求,具体操作如下:
- 在 CI/CD 服务器目录下,执行
pm2 start index.js --name cicd-server
命令,使用 PM2 启动 CI/CD 服务器,并为其指定名称 “cicd-server”,方便后续管理。
- 执行
pm2 save
命令,将当前 PM2 管理的进程列表保存到配置文件中,确保服务器重启后,CI/CD 服务器能够自动启动。
(六)配置反向代理(可选)
若需要通过域名访问 CI/CD 服务器,可配置 NGINX 或 Apache 作为反向代理,以下分别介绍两种服务器的配置方法:
1. NGINX 配置
- 执行
sudo nano /etc/nginx/conf.d/cicd.conf
命令,创建并编辑 NGINX 配置文件。
- 在配置文件中写入以下内容:
server { listen 80; # 替换为实际使用的域名 server_name example.com; location / { # 代理到CI/CD服务器的地址和端口 proxy_pass http://localhost:4000; # 设置请求头信息,确保客户端信息能正确传递到CI/CD服务器 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }
- 保存并退出配置文件,执行
sudo nginx -t
命令,检查 NGINX 配置是否正确。若配置正确,会输出 “test is successful” 的提示信息。
- 执行
sudo systemctl reload nginx
命令,重新加载 NGINX 配置,使配置生效。
2. Apache 配置
- 执行
sudo nano /etc/httpd/conf.d/cicd.conf
命令,创建并编辑 Apache 配置文件。
- 在配置文件中写入以下内容:
<VirtualHost *:80> # 替换为实际使用的域名 ServerName example.com # 将请求代理到CI/CD服务器 ProxyPass / http://localhost:4000/ ProxyPassReverse / http://localhost:4000/ # 配置错误日志和访问日志的路径 ErrorLog /var/log/httpd/cicd-error.log CustomLog /var/log/httpd/cicd-access.log combined </VirtualHost>
- 保存并退出配置文件,执行
sudo systemctl restart httpd
命令,重启 Apache 服务,使配置生效。
四、测试 CI/CD 服务器功能
完成 CI/CD 服务器的搭建和配置后,需要对其功能进行测试,确保能够正常实现自动化部署,具体测试步骤如下:
- 对本地 Node.js 项目进行修改,例如修改代码中的某个功能或添加一些注释。
- 将修改后的代码提交到 GitHub 仓库的对应分支(与 Webhooks 配置中选择的分支一致),执行
git add .
、git commit -m "测试CI/CD部署"
、git push origin main
(若分支不是 main,需替换为实际分支名称)命令。 - 登录 CI/CD 服务器,执行
pm2 logs cicd-server
命令,查看 CI/CD 服务器的日志信息,观察是否接收到 GitHub Webhook 发送的请求,以及部署脚本是否正常执行。 - 检查项目目录下的代码是否已更新为最新版本,若 package.json 文件有变更,查看依赖是否已重新安装。
- 执行
pm2 status
命令,查看项目是否已被 PM2 成功重启,确保项目以最新代码正常运行。
五、总结
通过以上步骤,我们成功搭建了一个自托管的 CI/CD 流水线,实现了 Node.js 应用的自动化部署。
该方案具有 lightweight、cost-effective 的特点,能够有效减少手动部署的工作量,提高开发效率,保障项目部署的稳定性和一致性。
同时,它还支持多项目管理,可通过在 CI/CD 服务器上配置多个项目的部署脚本和 GitHub Webhooks,实现对多个 Node.js 项目的自动化部署管理。
在实际使用过程中,可根据项目的具体需求,对部署脚本和服务器配置进行进一步的优化和扩展,例如添加部署失败的邮件通知功能、实现多环境部署等,使 CI/CD 服务器更好地满足项目的发展需求。