本文為 Lidemy [BE201] 部落格以及抽獎網站部署筆記,部署 express 到 Heroku 並串接 cleardb 資料庫,參考文件:
Deploying Node.js Apps on Heroku
ClearDB MySQL
零、內容摘要
1. 前置作業:安裝所需軟體、申請 heroku 帳號、登入後新增一個 cleardb 的 add-on
2. 修改檔案內容:
a. 新增 .env
b. 修改 config.json
c. 新增 .gitignore
d. 修改 index.js
e. 修改 package.json
3. 使用 git 部署
4. 使用 Sequel Pro 連進 heroku 的線上 db
5. debug 紀錄
6. 部署心得
一、前置作業:官方文件
1. 申請 Heroku 帳號,且必須綁信用卡,否則不能用有資料庫的部署服務
註:綁信用卡似乎會預扣款台幣十元左右,來驗證信用卡是否能用,但過一天就會取消了。
2. 安裝並檢查需要的軟體
a. 安裝 heroku cli
$ brew install heroku/brew/heroku
出現 heroku/7.59.2 darwin-x64 node-v12.21.0
就表示成功了,或可用 $ heroku -v
檢查版本號
註:$ xcode-select --install
我還需要額外安裝這個
b. 檢查軟體版本
b.1 $ node -v
b.2 $ npm -v
b.3 $ git --version
c. 登入 heroku: $ heroku login
d. $ heroku create 專案名稱
或不指定名稱,heroku 會自動生成一個
e. 使用 cleardb 來設定資料庫: $ heroku addons:create cleardb:ignite
註:若是沒有綁定信用卡,這一步會失敗
如果出現錯誤訊息:Error: Missing required flag:
,執行以下步驟:
e.1 $ git init
e.2 $ heroku git:remote -a 專案名稱
e.3 $ heroku addons:create cleardb:ignite
註:這裡會出現錯誤是因為我有兩個以上的專案,heroku 只會部署在 master or main 上面,所以要使用 git 指定在專案上。
二、修改檔案內容
關於環境變數的部分,參考筆記 利用 dotenv 套件,設置在 Node.js 裡面的環境變數
1. 在專案根目錄底下,新增一個名稱叫 .env 的檔案,並增加以下內容
// this is fake data
PORT=5001
DB_USERNAME=b71addwgsssddfa
DB_PASSWORD=1bcedwddfaew56
DB_HOST=us-cdbr-east-04.cleardb.com
DB_DATABASE=heroku_0322wqdsawdds8d
// 上面這四行資料是由下面 CLEARDB_DATABASE_URL 得來的
// DB_USERNAME:DB_PASSWORD@DB_HOST/DB_DATABASE
SESSION_SECRET=keyboard cat
CLEARDB_DATABASE_URL=mysql://b71addwgsssddfa:1bcedwddfaew56@us-cdbr-east-04.cleardb.com/heroku_0322wqdsawdds8d?reconnect=true
註:CLEARDB_DATABASE_URL 在 heroku 後台,進入專案裡的 settings > Config Vars > Reveal Config Vars
2. 修改 config.json -> config.js 及其內容
// config.js
require("dotenv").config();
const config = {
development: {
username: process.env.DEV_USERNAME,
password: process.env.DEV_PASSWORD,
database: process.env.DEV_DATABASE,
host: process.env.DEV_HOST,
dialect: "mysql",
},
test: {
username: process.env.DEV_USERNAME,
password: process.env.DEV_PASSWORD,
database: process.env.DEV_DATABASE,
host: process.env.DEV_HOST,
dialect: "mysql",
},
production: {
username: "process.env.PROD_USERNAME",
password: "process.env.PROD_PASSWORD",
database: "process.env.PROD_DATABASE",
host: "process.env.PROD_HOST",
dialect: "mysql",
use_env_variable: "CLEARDB_DATABASE_URL",
},
};
module.exports = config;
註:production 記得加上 cleardb 的環境變數 "use_env_variable": "CLEARDB_DATABASE_URL"
註:特別注意有引號跟沒有引號的區別,在本地端的時候,環境變數不能有引號;但是部署上 Heroku 時,就要有引號。
3. 在專案根目錄底下,建立 .gitignore 檔案,把敏感資料都放進去
/node_modules
/*.env
.DS_Store
4. 修改 index.js 內容
require("dotenv").config();
const port = process.env.PORT || 5001;
// 但是這個 port 不用加引號也可以,我真搞不懂為什麼😭
app.use(
session({
secret: "process.env.SESSION_SECRET",
// 這裡也要特別注意,要部署上 Heroku 的都要加引號
resave: false,
saveUninitialized: true,
})
);
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`);
});
5. 修改 package.json
a. 確認 node 版本 $ node -v
,目前是 14.x 版本
b. 新增 engines 標籤
告訴 heroku 此專案使用 node 14.x 的版本執行,把以下程式碼貼進 package.json 裡面
"engines": {
"node": "14.x"
},
c. scripts 標籤:新增 db:migrate 及 start 兩項
"scripts": {
"db:migrate": "npx sequelize-cli db:migrate",
"start": "npm run db:migrate && node index.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
d. 先在本地測試:$ npm run start
開啟本地資料庫,此時在本地應該可以跑起來,網站一切正常
註:另一種方式是建立 Procfile,可以用 $ heroku local web
看有沒有跑起來,但我沒用過
三、用 git 部署
在
$ git add .
之前,檢查 .gitignore 需要的檔案是否都有加進去
註:要切換到 main 才能夠部署到 Heroku 喔。
1. $ git add .
2. $ git commit -m 'deploy heroku'
3. $ git push heroku main
4. $ heroku open
會在預設瀏覽器自動開啟部署的網址
5. 部署上去以後,不會有本地寫好的資料,可以把本地的資料匯出,接著用 Sequel Pro 連進去再匯入資料。
四、使用 Sequel Pro 連進 heroku 的線上 db
在 heroku 部署後的資料庫是全新的,不會有之前本地寫好的資料庫內容,可用 Sequel Pro 連進 heroku 的線上 db 直接設定帳密比較快。
1. 到 Heroku 專案裡的 settings > Config Vars
2. CLEARDB_DATABASE_URL:
// this is fake data
CLEARDB_DATABASE_URL:
mysql://b71addwgsssddfa:1bcedwddfaew56@us-cdbr-east-04.cleardb.com/heroku_0322wqdsawdds8d?reconnect=true
3. 在 Sequel Pro 登入
帳號:b71addwgsssddfa
密碼:1bcedwddfaew56
host: us-cdbr-east-04.cleardb.com
database: heroku_0322wqdsawdds8d
五、debug 時間:請愛用 heroku logs --tail
1. 部署顯示成功了,可是連到網址出現訊息:Internal Server Error
嘗試1: 在 heroku 後台 restart app,沒用
檢討:這個錯誤起因於環境變數沒有設定好,所以我又把 app 刪掉重來
檢討:我覺得我應該要調整心態,不要一個錯誤就全部刪掉重來,這樣其實很浪費資源跟時間
2. 第二次部署有出現網站了,可是無法登入
好像該設定的都設定了,可是
a. 登入時如果沒有輸入帳密,沒有錯誤訊息提示
b. 登入後導到首頁,可是沒有登出按鈕
c. 沒有顯示發表文章的選項
d. 結論:登入功能是壞的
e. 顯示首頁的文章功能是好的
檢討:把 index.js 裡 session 的 cookie 拿掉就好了
app.use(
session({
secret: "process.env.SESSION_SECRET",
resave: false,
saveUninitialized: true,
// 這裡應該不需要設 cookie
cookie: {
sameSite: "none",
secure: true,
},
})
);
3. 第三次部署:
問題 1:全部的檔案都設定好,要在本地試跑時,出現錯誤訊息:
ERROR: connect ETIMEDOUT
以為經過一夜休眠會自動好轉,nope。
檢討 1:電腦重開機就好了。
問題 2:部署前在本地端測試,功能一切正常;部署後新增獎項時,id 一次增加 10,順序變成:1, 14, 24, 34 這種奇怪的數字,找了資料庫的設定,看不出端倪,所以刪掉重來吧。
4. 第四次部署:
此問題沒有解決 -> 後來交作業時老師提供了解答 ClearDB id 連續遞增問題。
預期 id 自動增加的順序及間隔為 1 → 2 → 3 → 4,實際情況是 4 → 14 → 24 → 34。
部署前在本地端測試沒有此問題,但部署到 heroku 後,在後台新增獎項時,id 沒有依序增加。
關鍵字:
auto_increment_offset
auto_increment_increment
MySQL
lampp/etc/my.cnf
4. 第五次部署:
有 build 成功,但是網頁出現 Application error
,本地測試都沒問題,估計是 config.js 在 produciton mode 的時候變數設定問題吧。
error 1: command not found: sequelize
-> 修改如下 package.json
"scripts": {
"db:migrate": "npx sequelize-cli db:migrate",
// 原本是寫 npx sequelize
"start": "npm run db:migrate && node index.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
error 2: ERROR: Error reading "config/config.js". Error: ReferenceError: CLEARDB_DATABASE_URL is not defined
production: {
// 然後下面的環境變數都要用引號括起來
username: "process.env.PROD_USERNAME",
password: "process.env.PROD_PASSWORD",
database: "process.env.PROD_DATABASE",
host: "process.env.PROD_HOST",
dialect: "mysql",
use_env_variable: "CLEARDB_DATABASE_URL",
// CLEARDB_DATABASE_URL 要用引號括起來
},
error 3: Error: secret option required for sessions
把 index.js 裡面的 session secret 後面加上引號如下:
app.use(
session({
secret: "process.env.SESSION_SECRET",
resave: false,
saveUninitialized: true,
})
);
六、心得感想
終於成功部署了,好開心,能夠把步驟清楚地記下來,下次再部署就不用重頭開始了。新建的資料庫沒有帳號密碼,我就直接把使用者寫在資料庫裡面,成功那一刻真的好感動,yoyoyo 水水水。
可以參考筆記 利用 dotenv 套件,設置在 Node.js 裡面的環境變數,環境變數真的是部署的成敗關鍵,在本地跟部署上有點不一樣,本地嘗試的結果是「不要用引號括起來才能跑」,但是部署上 Heroku 的時候,卻是「一定要用引號括起來才部署成功」。