Contents

Raspberry PI Git Webhook...

將應用程式部署到 Raspberry Pi 時,手動 SSH 登入再執行 git pull 的方式既麻煩又容易出錯。透過 GitHub Webhook 搭配內網穿透工具,可以實現每次推送程式碼到 GitHub 時,Raspberry Pi 自動拉取更新並重啟服務的 CI/CD 流程。

整體架構

1
開發者 push → GitHub → Webhook → Localtunnel/ngrok → Raspberry Pi → 自動 pull + 重啟

由於 Raspberry Pi 通常位於家用網路內,沒有固定的公開 IP,需要使用內網穿透工具(如 Localtunnelngrok)將本地的 Webhook 接收服務暴露到公網。

第一步:在 Raspberry Pi 上建立 Webhook 接收服務

使用 Node.js 建立一個簡單的 HTTP 伺服器來接收 GitHub 的 Webhook 通知:

1
npm install -g express

建立 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

  1. 進入 GitHub 倉庫 → Settings → Webhooks → Add webhook
  2. Payload URL:填入 Localtunnel/ngrok 的 URL + /webhook
    • 例如:https://my-pi-webhook.loca.lt/webhook
  3. Content type:選擇 application/json
  4. Secret:填入與 webhook.js 中相同的 SECRET
  5. Events:選擇 Just the push event
  6. 儲存後 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

安全性注意事項

  1. 一定要驗證 GitHub 的 HMAC 簽名,避免任何人都能觸發部署
  2. 部署腳本中避免使用 sudo,使用最小權限原則
  3. 考慮使用 SSH Deploy Key 而非全域 token 進行 git pull
  4. Localtunnel 的 URL 是公開的,務必加上簽名驗證

參考資料