今天下午花了點時間怎麼部署自己在本地端用了一陣子的 Rails 應用,原本考慮用 Railway 這類對入門者友善的平台,但想了想,這種「方便」的方案往往之後會變得麻煩——綁定太深、客製化困難、價格不透明。既然如此,乾脆趁機試試 Rails 官方推薦的 Kamal,搭配 Linode 來解決。
Linode 主機設定
首先在 Linode 建立一台新的主機,拿到 IP 後,把 SSH key 複製過去:
ssh-copy-id root@Linode_IP
之後操作就不用一直輸入密碼。
Rails 專案設定
根據 Rails 官方指南,更新 config/deploy.yml 設定檔。
Docker Hub
Kamal 需要從 Docker registry 拉取 image,所以要:
- 建立 Docker Hub 帳號
- 產生一個 access token
本地環境
記得安裝 Docker for Mac,沒裝的話會沒辦法 build image。
Kamal 部署
在本地終端機先設定環境變數:
export KAMAL_REGISTRY_PASSWORD=your-docker-hub-access-token
然後執行:
bin/kamal setup
新的 Linode 主機上沒有 Docker,Kamal 會自動安裝,並上傳應用程式。
有一點要注意:Rails 預設要求 HTTPS,所以沒辦法直接用 IP 訪問。
Cloudflare DNS 設定
我的 DNS 服務是用 Cloudflare。新增一筆 A record 指向 Linode IP,例如 my-app.domain.com。
透過 Cloudflare 的 proxy,就能用 HTTPS 連到新網站了。
SQLite 資料庫備份
既然用 SQLite,備份就得自己處理。SSH 進主機後設定 cron job:
ssh root@你的Linode_IP
# 建立備份資料夾
mkdir -p ~/backups
# 編輯 crontab
crontab -e
加入以下排程:
# 每天凌晨 3 點備份
0 3 * * * cp /var/lib/docker/volumes/my-rails-app_db/_data/production.sqlite3 ~/backups/production_$(date +\%Y\%m\%d).sqlite3
# 每天凌晨 4 點清理 30 天前的備份
0 4 * * * find ~/backups -name "*.sqlite3" -mtime +30 -delete
Kamal 常用指令
Kamal 在主機上多疊了一層 Docker,要進入容器操作時可以用:
bin/kamal console # 進入 Rails console
bin/kamal shell # 進入容器 shell
bin/kamal logs # 查看 logs
整體來說,Kamal 的部署體驗比想像中順暢。雖然前置設定比 PaaS 多一些,但換來的是完整的控制權和透明的成本結構。也慶幸過去累積了一些背景知識,加上 AI 輔助除錯,大大縮短了排除問題的時間。