將應用程式部署到 Raspberry Pi 時,手動 SSH 登入再執行 git pull 的方式既麻煩又容易出錯。透過 GitHub Webhook 搭配內網穿透工具,可以實現每次推送程式碼到 GitHub 時,Raspberry Pi 自動拉取更新並重啟服務的 CI/CD 流程。
整體架構
1
|
開發者 push → GitHub → Webhook → Localtunnel/ngrok → Raspberry Pi → 自動 pull + 重啟
|
由於 Raspberry Pi 通常位於家用網路內,沒有固定的公開 IP,需要使用內網穿透工具(如 Localtunnel 或 ngrok)將本地的 Webhook 接收服務暴露到公網。
第一步:在 Raspberry Pi 上建立 Webhook 接收服務
使用 Node.js 建立一個簡單的 HTTP 伺服器來接收 GitHub 的 Webhook 通知:
建立 webhook.js:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
const express = require('express');
const crypto = require('crypto');
const { exec } = require('child_process');
const app = express();
const SECRET = 'your-webhook-secret';
app.use(express.json());
app.post('/webhook', (req, res) => {
// 驗證 GitHub 簽名
const signature = req.headers['x-hub-signature-256'];
const hmac = crypto.createHmac('sha256', SECRET);
const digest = 'sha256=' + hmac.update(JSON.stringify(req.body)).digest('hex');
if (signature !== digest) {
return res.status(401).send('Unauthorized');
}
console.log('收到 Webhook,開始部署...');
res.status(200).send('OK');
// 執行部署腳本
exec('/home/pi/deploy.sh', (error, stdout, stderr) => {
if (error) {
console.error('部署失敗:', error);
} else {
console.log('部署成功:', stdout);
}
});
});
app.listen(3000, () => {
console.log('Webhook server 監聽 port 3000');
});
|
第二步:建立部署腳本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
# /home/pi/deploy.sh
#!/bin/bash
cd /home/pi/myapp
# 拉取最新程式碼
git pull origin main
# 安裝相依套件
npm install --production
# 重啟服務(使用 pm2)
pm2 restart myapp
echo "部署完成:$(date)"
|
賦予執行權限:
1
|
chmod +x /home/pi/deploy.sh
|
第三步:使用 Localtunnel 或 ngrok 暴露本地服務
使用 Localtunnel
1
2
3
4
5
|
npm install -g localtunnel
# 將 port 3000 暴露到公網
lt --port 3000 --subdomain my-pi-webhook
# 取得 URL:https://my-pi-webhook.loca.lt
|
使用 ngrok
1
2
3
|
# 安裝後執行
ngrok http 3000
# 取得 URL:https://xxxxxxxx.ngrok.io
|
注意:免費版 ngrok 每次重啟會更換 URL,需要更新 GitHub 的 Webhook 設定。Localtunnel 可以固定子域名。
第四步:在 GitHub 設定 Webhook
- 進入 GitHub 倉庫 → Settings → Webhooks → Add webhook
- Payload URL:填入 Localtunnel/ngrok 的 URL +
/webhook
- 例如:
https://my-pi-webhook.loca.lt/webhook
- Content type:選擇
application/json
- Secret:填入與
webhook.js 中相同的 SECRET 值
- Events:選擇
Just the push event
- 儲存後 GitHub 會自動送出一個 ping 測試
第五步:讓服務開機自動啟動
使用 pm2 管理 Webhook 服務,確保重開機後自動啟動:
1
2
3
4
5
6
7
8
9
10
11
|
npm install -g pm2
# 啟動 webhook 服務
pm2 start webhook.js --name webhook
# 啟動 localtunnel
pm2 start "lt --port 3000 --subdomain my-pi-webhook" --name tunnel
# 設定開機自動啟動
pm2 startup
pm2 save
|
安全性注意事項
- 一定要驗證 GitHub 的 HMAC 簽名,避免任何人都能觸發部署
- 部署腳本中避免使用
sudo,使用最小權限原則
- 考慮使用 SSH Deploy Key 而非全域 token 進行
git pull
- Localtunnel 的 URL 是公開的,務必加上簽名驗證
參考資料