部署 Node.js app 在 AWS EC2(Nginx + MySQL)


Posted by ai86109 on 2020-10-20

前言

上次我們成功在 AWS 主機上部署了我們的 PHP 專案,詳情可以參考之前的部署心得:

部署 AWS EC2 主機 + LAMP server + phpMyAdmin
上傳檔案到雲端主機 + 更改域名

這次我們則是要將 Node.js 專案如法炮製的放到遠端主機上讓他跑起來,但不同的是這次要來順便玩玩 Nginx 這個反向代理伺服器(reverse proxy server)。

Btw 如果以下內容有誤,請讓我知道~謝謝


反向代理是什麼?

在講到反向代理前,要先說什麼是代理伺服器(proxy server)。

平常我們在說發送 request,都是 client 直接發送給 server,而代理伺服器就是出現在 client 和 server 之間的角色,client 會先將 request 送給 proxy server,再由 proxy server 將這個 request 傳給 server端,根據代理伺服器幫助的對象不同又可分為正向代理或是反向代理。

假設我想去一個日本的網站,但這個網站有鎖 IP,只允許日本的 IP 進入,在台灣的我要進去就得透過日本的 proxy server,先發 request 給這個 proxy server,再請他送給這個網站。這種客戶端透過代理伺服器,向遠端網站發 request 的行為就稱為正向代理(Forward Proxy)。

所以反過來說,client 發送 request 之後會先到 server 端設置的 proxy server,再由 proxy server 發給其他的 server,就稱為反向代理(reverse proxy)

那為什麼需要反向代理?

最主要的原因是因為只有一個 service 可以佔用 port 80。

什麼意思呢?

因為 port 80 是 HTTP 預設的 port,所以假設我有一個網站網址是 derekyen.tw,IP 是 5.5.6.6,所以當我想要連到此網站時,只要在網址列輸入 derekyen.tw:80 即可(但因為 HTTP 連線,80 是預設的,所以不輸入 :80 也可以連到)。

這是我只有一個服務的狀況下,但如果我還有其他像是縮網址、留言板服務,因為 port 80 已經被佔據了,所以我就必須使用其他 port。

  • derekyen.tw:5000 縮網址服務
  • derekyen.tw:5001 留言板服務

雖然這樣一樣可以連到該網站的服務,但網址就會變得很醜。為了解決這個問題,我們可以先設置一個 reverse proxy server 讓他聽 port 80,並且將各服務加上 subdomain 的網址都導向這個 reverse proxy server,最後再由這個 reverse proxy server 導到各自的服務,就像這樣:

aaa.derekyen.tw -> 5.5.6.6 -> 反向代理伺服器 -> port 5000
bbb.derekyen.tw -> 5.5.6.6 -> 反向代理伺服器 -> port 5001

這次要用的 Nginx 就是一個反向代理伺服器。


安裝 Nginx

我們首先可以先把 Nginx 裝起來,大部分是參考這篇

  1. 先連到遠端主機

  2. 更新套件的最新資訊
    sudo apt update

  3. 安裝 Nginx
    sudo apt install nginx

  4. 把防火牆開給 Nginx
    sudo ufw allow 'Nginx HTTP',有關防火牆設定可參考這篇

  5. 看 Nginx 狀態
    systemctl status nginx,可以看到目前是 inactive

  6. 啟動 Nginx
    sudo systemctl start nginx
    .
    6-1. 預期應該要跑起來,但用 systemctl status nginx 檢查發現卻是failed
    .
    6-2. 出現錯誤訊息

    nginx.service - A high performance web server and a reverse proxy server
    Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
    Active: failed (Result: exit-code) since Sat 2020-10-17 04:33:45 UTC; 14s ago
      Docs: man:nginx(8)
    Process: 31380 ExecStart=/usr/sbin/nginx -g daemon on; master_process on; (code=exited, status=1
    Process: 31366 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited,
    

    .
    6-3. 查了一下發現,應該是 port 80 被佔用了
    .
    6-4. sudo lsof -i:80,發現是 apache2 正在跑(之前部署 PHP 時候用的)
    .
    6-5. 把 apache2 停掉,sudo service apache2 stop,再重新啟動 Nginx

  7. 確認 Nginx 狀態為 active,就代表成功了

    因為中間我有把 Nginx 玩壞了,所以把 Nginx 移除再重裝,但要記得移除乾淨!
    完全移除可以參考這篇


設定 subdomain

先前部署 PHP 時,域名的服務是使用 gandi,所以這邊繼續沿用。

設定方式滿簡單的

  1. 點下你的域名,選擇區域檔紀錄

  2. 新增,類型選 A

  3. 名稱那邊就是 subdomain,自己取

  4. IPv4 位址就是你 AWS EC2 的 IPv4

  5. 建立之後就完成了

到這邊就完成了這一段

aaa.derekyen.tw -> 5.5.6.6 -> 反向代理伺服器

接著我們先來部署 Node.js 專案


上傳檔案

將你 Node.js 專案 port 改好,我這邊是用 port 5000

上傳檔案為了方便起見我用 FTP 丟(記得要開防火牆)。

協定:Sftp
主機:就是你主機的 IP
登入形式:金鑰檔案
使用者:ubuntu
金鑰檔案:就是你的金鑰位置

連上後找個你喜歡的位置把檔案丟進來,我是在 /var/www/html 開了個資料夾。

記得要開啟防火牆(你專案的 port),除了 ufw 設定之外,也要去 EC2 設定 Security group 的 rule,不然你會發現你連不上。
AWS Security Group 參考資料


套件

除了 Node.js 專案之外,我們也要在遠端主機上將該裝的套件裝上。

安裝方式和 local 端一樣,但在安裝 bcrypt 時出現問題,解決方法可以參考這篇


資料庫設定

這邊 DB 我想沿用 MySQL,因為之前部署的時候就已經裝好了,所以就不另外裝,原以為可以速速解決沒想到卡最久...

Node.js 的 config 設定好之後,想說 sudo npx sequelize db:migrate 應該就可以了,卻噴出 npx: command not found,可以參考這篇解決,sudo npm i -g npx 之後就可以用 npx 了。

沒想到又出現錯誤 Unknown database,所以看來我應該手動去建一個 database。

進入 MySQL mysql -u root -p,建立資料庫 CREATE DATABASE 'my_db';(這邊 my_db 填自己的 DB 名稱,還有分號很重要,我忘了加所以想說怎麼都建不了,搞了好久),這邊參考這篇

建好之後退出 MySQL exit;

我們再次輸入 sudo npx sequelize db:migrate,想說應該可以了吧,卻出現新的錯誤。

Host '127.0.0.1' is not allowed to connect to this MySQL server

查了之後發現要去改設定檔,設定檔在這: /etc/mysql/mysql.conf.d/mysqld.cnf

我是用 nano 開啟的(不知為何用 vim 會沒辦法,因為是唯讀?)
sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf

將 bind-address 的值改成 0.0.0.0,這裡參考這個這個,改好之後儲存、離開(指令參考),最後記得再檢查上傳的專案裡 config 裡面帳密、DB 那些都要對。

我們再次 sudo npx sequelize db:migrate
node index.js
就可以看到跑起來拉~可喜可賀!

要注意資料庫 encoding 編碼,否則可能會寫不進去!


用 Sequel Pro 管理 DB

還不夠!我想要用 sequel pro 去管理 DB 比較方便,但防火牆都開了,卻一直登不上,後來才發現是權限的問題。

我是參考這篇的改表法:

  1. 一樣先進到 MySQL
    mysql -u root -p

  2. 輸入 use mysql,會出現 Database changed

  3. 再依序輸入 update user set host = '%' where user = 'root';
    flush privileges;

  4. 改好之後再連連看就可以了!


設定 Nginx 導向其他 port

我們前面已經完成了將我們設定的網址導到 Nginx,也成功將 Node.js 專案放在主機並連上資料庫,現在就剩下將 Nginx 導向這個專案了。

/etc/nginx/ 裡面有 config 檔,把它打開來可以看到裡面有 include sites-enable 這個位置。

接著我們到 sites-available 來建立一個設定檔,名字自己取可以辨識就好 sudo vim justabit.derekyen.tw

檔案放的內容,可以參考這篇

server {
    listen       80;
    server_name  justabit.derekyen.tw;

    # 把 request 轉給 localhost 的 5000 port
    location / {
      proxy_pass http://127.0.0.1:5000;
    }
}

這邊表示 Nginx 聽 port 80,如果 server name 是 justabit.derekyen.tw,就會導向 port 5000

因為我們剛在 config 檔裡面看到,Nginx 是 include sites-enable 這個位置的,而我們剛剛檔案放在 sites-available,所以我們要將這兩個位置連結起來,產生 symbolic link。
sudo ln -s /etc/nginx/sites-available/justabit.derekyen.tw /etc/nginx/sites-enabled/

之所以不直接把設定檔放在 sites-enable,是因為如果當這個設定檔目前不用的時候,就只能刪除或搬到其他位置。但如果放在 sites-available,就可以把 symbolic link 取消就可以了。

改好之後再來 reload Nginx 看有沒有吃到剛剛設定
sudo systemctl reload nginx

使用 node index.js,嘗試用剛剛的網址 justabit.derekyen.tw,連上就代表成功部署了!

BUT!
就是這個 BUT!

仔細想想,剛剛我是用 node index.js 讓 server 跑起來的,如果要保持連線,我不就要一直跑著,都不能做其他事嗎?那該怎麼辦。

所以我們需要有一個管理 server 執行的東西,也就是 PM2。


PM2

使用方式很簡單,一樣在遠端主機安裝好之後,輸入指令就可以用

  • 開始連線 index.js
    pm2 start index.js

  • 暫停
    pm2 stop 數字(這個數字是 app 的 id)

  • 重新連線
    pm2 restart 數字(這個數字是 app 的 id)

  • 刪除這個連線
    pm2 delete 數字(這個數字是 app 的 id)

這樣就可以一次跑好幾個服務,並且跑在背景程式。


總結

到這邊部署就完成了,一路上踩了不少坑,也一度找不到問題的方向,但過了那個坎之後,就會覺得自己懂了更多(才怪

Again 如果以上內容有誤,請讓我知道~謝謝

部署的過程中,感謝同學們以及網路上各種資料的幫助,希望可以把我踩坑的過程記錄下來,對卡關的人有所幫助摟,祝各位 Happy coding!


#nginx #aws #ec2 #node.js #MySQL #pm2 #部署 #Deploy







Related Posts

[ Day 06 ] 用 InstaPy 打造 Instagram Bot 吧

[ Day 06 ] 用 InstaPy 打造 Instagram Bot 吧

[Week 1] 認識版本控制與初級 Git 時光機

[Week 1] 認識版本控制與初級 Git 時光機

前端、後端差異 & 瀏覽器運作流程

前端、後端差異 & 瀏覽器運作流程


Comments