记录一次将github上的服务端工程,自动部署到Linux服务器上。
使用技术:Linux,Docker,Webhooks
创建webhook
- 进入github-> xxx工程 -> 设置 -> Webhooks
2. 添加webhooks
创建服务器端监听程序
这里使用的node创建的一个本地服务
const express = require('express');
const crypto = require('crypto');
const bodyParser = require('body-parser');
const { exec } = require('child_process');
const app = express();
const PORT = 10001; // 自定义端口
const deployPath = "/opt/webhook-listener/bin/deploy.sh";
// GitHub secret
const SECRET = 'abcd1234'; // 设置在 GitHub 的 secret
// 自定义 body parser 中间件用于验证签名前保留原始 body
app.use(bodyParser.json({
verify: function (req, res, buf) {
req.rawBody = buf;
}
}));
// 验证 GitHub 发来的请求签名
function isSignatureValid(req) {
const signature = req.headers['x-hub-signature-256'];
console.log(signature);
if (!signature) return false;
const hmac = crypto.createHmac('sha256', SECRET);
const digest = 'sha256=' + hmac.update(req.rawBody).digest('hex');
console.log(digest);
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(digest));
}
// webhook 路由
app.post('/webhook', (req, res) => {
if (!isSignatureValid(req)) {
console.log('❌ 无效的 GitHub 签名');
return res.status(401).send('签名验证失败');
}
console.log('✅ 收到 GitHub Webhook 推送,开始部署...');
// 执行你的部署脚本
exec(deployPath, (error, stdout, stderr) => {
if (error) {
console.error(`❌ 部署失败:\n${stderr}`);
return res.status(500).send('部署失败');
}
console.log(`✅ 部署成功:\n${stdout}`);
res.send('部署成功');
});
});
// 启动服务
app.listen(PORT, () => {
console.log(`🚀 Webhook 服务监听端口: ${PORT}`);
});
创建本地执行脚本
在上面本地的node服务中有exec的执行脚本,deployPath 就是本地执行的脚本,也可以将该脚本直接写在 index.js。
#!/bin/bash
set -e
APP_DIR="/opt/sharedKitchenBack/"
CI_DIR="/opt/sharedKitchenBack/ci"
echo "拉取最新代码"
cd "$APP_DIR"
git reset --hard
git clean -fd
git pull origin main
npm install
echo "重启 Docker 容器"
cd "$CI_DIR"
docker-compose pull
docker-compose down
docker-compose up -d --build
docker image prune -f
echo "部署完成"
添加DockerFile和docker-compose.yml 配置文件
在github上代码拉取下来后进行部署
FROM node:18
WORKDIR /app
COPY ../package*.json ./
RUN npm install
COPY ../ .
EXPOSE 3000
CMD ["node", "app.js"]
version: '3.8'
services:
nodeapp:
build:
context: ..
dockerfile: ci/Dockerfile
container_name: nodeapp
restart: always
expose:
- "3000"
environment:
- PORT=3000
- NODE_ENV=production
- MONGODB_URI=mongodb://mongo:27017/shared_kitchen
- REDIS_URL=redis://redis:6379
depends_on:
- mongo
- redis
nginx:
image: nginx:stable
container_name: nginx_proxy
restart: always
ports:
- "8080:8080"
volumes:
- ./nginx_default.conf:/etc/nginx/conf.d/default.conf:ro
working_dir: /etc/nginx
depends_on:
- nodeapp
mongo:
image: mongo:6
container_name: mongo
restart: always
ports:
- "27017:27017"
volumes:
- mongodata:/data/db
redis:
image: redis:7
container_name: redis
restart: always
ports:
- "6379:6379"
volumes:
- redisdata:/data
volumes:
mongodata:
redisdata: