2025 部落格 CI/CD 部署實戰與心得
這篇紀錄我把這個用 Hexo 建置的部落格從「自寫 Webhook」轉換到 GitLab CI/CD,以及中間 Nginx / Certbot 踩雷的調整心得。
部署方式的回顧
一開始是用自己寫的
Webhook(Node.js + Express):- 收到 GitLab Push 事件 → 直接
git pull && hexo generate。 - 簡單、快速上線,但 安全性與可維護性差。
- 收到 GitLab Push 事件 → 直接
後來切換成 GitLab CI/CD:
- 使用
node:20-alpine容器建置 →npx hexo generate。 - 部署時用
rsync --backup,並保留最近 10 次 備份。 deploy使用者只擁有對/usr/share/nginx/html/myblog的權限,安全許多。
- 使用
Nginx server block 踩雷:
- 曾經 root 指到
/myblog/public,導致頁面只出現文字、樣式 404。 - 最後拆成
/etc/nginx/conf.d/hazel.style.conf,domain 與 root 一一對應。
- 曾經 root 指到
Certbot 續期問題:
- 因為
flex.hazel.style沒有 A 記錄,續期失敗。 - 解法是重新簽發時 移除不存在的子網域,只保留
hazel.style與www.hazel.style。
- 因為
GitLab CI/CD Pipeline 設定
這次的核心是 .gitlab-ci.yml,分成兩個 stage:
- build → 負責在乾淨的 Node 環境產生 Hexo 靜態檔。
- deploy → 用 SSH + rsync 部署到伺服器,並自動保留備份。
gitlab-ci.yaml 如下:
1 | stages: [build, deploy] |
設定細節說明
Artifacts: build job 產生的 /public 會存成 artifacts,給 deploy job 用。
SSH Key: 金鑰存在 GitLab Variables,Pipeline 裡還原,再用 ssh-agent 加入。
rsync 備份機制: 每次 deploy 都會把舊版放進 .trash/$CI_PIPELINE_ID。
自動刪掉多於 10 份,避免磁碟爆滿。安全性:
deploy使用者只管理/usr/share/nginx/html/myblog的內容,權限分明。
Webhook vs. CI/CD
| 面向 | 自寫 Webhook | GitLab CI/CD |
|---|---|---|
| 安全性 | 無驗證,任何人可打;若 root 執行 = 高風險 | 部署帳號最小權限,金鑰存在 CI Variables |
| 穩定性 | 沒鎖、沒佇列,併發可能互相覆蓋 | pipeline 階段分明,job 可重試 |
| 可重現性 | VM 直接跑,Node/套件版本易漂移 | 容器化環境固定 node 版本,產出穩定 |
| 回滾備份 | 沒有 → 出事只能手動復原 | rsync --backup + .trash,自動保留 10 份 |
| 可觀測性 | 全靠 console.log | GitLab job logs,可追蹤歷史 pipeline |
| 成本與便利 | 簡單快速上線 | 前期需設定,但長期維護更安心 |
改動成果
- 成功用
rsync做出 部署即備份,只保留 10 個版本,減少磁碟占用。 - Nginx 設定拆分,解決
conflicting server name。 - Certbot 續期確認通過,
renew --dry-run綠燈。 - 部署流程從「SSH 上伺服器手動測」→「自動化 CI/CD」,效率提升。
後續規劃
✏️ 完善 GitLab CI/CD,增加「一鍵回滾到上一個 pipeline」的 script。
✏️ 部署後加上自動化 smoke test(檢查首頁 / CSS / JS 是否正常)。
✏️ 加上即時翻譯套件 ✅