本文山姆鍋分享如何安裝與設定一個 NodeBB 論壇的正式生產環境(production environment)。由於整個環境不只包含 NodeBB 本身,會從主機系統的開始設定開始到 Nginx web 服務。安裝過程參考了多篇文章,對於特定元件的安裝細節會直接連結到對應的教學文章,本文盡量只做重點說明。

歡迎您加入 #Developers.TW ,A.K.A. #呆丸開發者。

安裝目標

本文是以 #Developers.TW 作為實際案例,並以符合需求的最少安裝為目標。選擇性的元件,如 iframely 網頁內嵌剖析或者 Solr 搜尋服務並不在本文說明。因為目標是可用在正式上線的最小安裝,擴充性、可用性等等自然也就先不要求了。

下圖是部署完成後粗略的系統架構圖:

準備工作

底下是一些 NodeBB 論壇安裝設定需要的前置需求:

  1. 有 NodeBB 論壇的專屬域名或子域名,像 #Developers.TW 的域名是 developers.tw。
  2. 域名服務器已經設定完畢。本文是採用 CloudFlare 作為 DNS 以及 CDN 服務供應商。
  3. 讀者知道如何設定域名的 DNS 紀錄。
  4. 部署使用的工作機 (筆電或者 PC) 已經有 SSH 登入的金鑰對(key pair)。

主機系統

對於經濟實惠又需要完整系統的部署環境,山姆鍋通常都會選用 DigitalOcean 的 VPS 虛擬私有伺服器,這次也不例外,選用一個標準型 1GB 記憶體、1 核 vCPU 的 Droplet 作為部署環境。

註冊 DigitalOcean 帳戶以及建立一個 Droplet 可以參考 Digital Ocean: 申請自己的虛擬主機伺服器

基本環境 (Ubuntu 18.04)

項目 方案
主機環境 DigitalOcean Droplet
主機組態 1 core vCPU, 1GB RAM, 25GB SSD
作業系統 Ubuntu 18.04 64-bit

郵件寄送

強烈建議使用雲端電子信件寄送服務,不要在主機自行安裝 Postfix, Exim 等郵件軟體。現在雲端郵件寄送服務功能完整、成功送達率較高,又有每月免費使用額度。#Developers.TW 使用的是 SendGrid 提供的郵寄服務,只是因為山姆鍋有使用經驗,讀者可以選擇自己熟悉的服務。

不管採用哪個服務,需要取得下列郵件寄送服務的網路參數:

項目 說明
SMTP 伺服器位址 IP 或域名
SMTP 監聽埠 如 487
SMTP 登入名稱 SendGrid 使用 ‘apikey’ 作為登入名稱
SMTP 登入密碼 從 SendGrid 申請的 api key 內容

有了雲端郵件寄送服務後,要讓主機系統可以寄出信件,尚須安裝與設定下列套件:

  • mailutils
  • ssmtp

使用下列指令安裝:

1
$ sudo apt-get install mailutils ssmtp

ssmtp 需要根據雲端郵寄服務提供的網路參數來設定,底下是 /etc/ssmtp/ssmtp.conf 檔案的內容:

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
#
# Config file for sSMTP sendmail
#
# The person who gets all mail for userids < 1000
# Make this empty to disable rewriting.
[email protected] # 管理者的 email

# The place where the mail goes. The actual machine name is required no
# MX records are consulted. Commonly mailhosts are named mail.domain.com
mailhub=smtp.sendgrid.net:587 # 雲端郵件寄送服務提供接口

# Where will the mail seem to come from?
#rewriteDomain=
rewriteDomain=developers.tw #改寫收件人看到的郵件來源域名

# The full hostname
hostname=nodebb-test # 主機名稱

# Are users allowed to set their own From: address?
# YES - Allow the user to specify their own From: address
# NO - Use the system generated From: address
#FromLineOverride=YES

AuthUser=apikey # SMTP 登入名稱
AuthPass=xxxxxxxxxx # SMTP 登入密碼

UseSTARTTLS=YES
UseTLS=YES

餐考文章:

安全強化

完成主機系統的基本安裝後,作為網路服務主機,主機安全性也不容輕忽。

SSH

底下是一些安全強化重點(只是基本):

  1. 只允許使用憑證 SSH 遠端登入主機,禁用密碼。
  2. SSH 不允許直接以 root 帳戶登入。
  3. (選擇性) 更改 SSH 服務的監聽埠(port),例如:939:這個不會增加多少安全性,但可以有效減少系統日誌的被攻擊的紀錄。

參考文章:

自動更新安全修補

當主機系統被發現有安全漏洞時,山姆鍋希望能夠自動更新修補程式。採考此文章 How To Setup And Enable Automatic Security Updates On Ubuntu

安裝 unattended-upgrade 套件

使用下列指令安裝 unattended-upgrade 套件:

1
$ sudo apt install unattended-upgrades

unattended-upgrades 的設定檔為 /etc/apt/apt.conf.d/50unattended-upgrades,山姆鍋直接使用預設值。如果需要增加自動更新的套件庫(repository),可以用文字編輯器修改此文件,移除對應行前 // 註解符號。

1
2
3
4
5
6
7
8
9
10
11
12
13
Unattended-Upgrade::Allowed-Origins {
"${distro_id}:${distro_codename}";
"${distro_id}:${distro_codename}-security";
// Extended Security Maintenance; doesn't necessarily exist for
// every release and this system may not have it installed, but if
// available, the policy for updates is such that unattended-upgrades
// should also install from here by default.
"${distro_id}ESMApps:${distro_codename}-apps-security";
"${distro_id}ESM:${distro_codename}-infra-security";
//"${distro_id}:${distro_codename}-updates";
//"${distro_id}:${distro_codename}-proposed";
//"${distro_id}:${distro_codename}-backports";
};

啟用自動更新

/etc/apt/apt.conf.d/20auto-upgrades 設定檔控制系統自動更新的行為。

1
2
3
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";
APT::Periodic::AutocleanInternal "7";

其中,“1” 表示啟用,要禁用則改為 “0”。

測試自動更新

使用下列指令進行空轉測試:

1
sudo unattended-upgrades --dry-run -–debug

會顯示類似下面的結果:

1
2
3
4
5
6
7
8
9
... 前面省略

adjusting candidate version: python3-zope.interface=4.3.2-1build2
pkgs that look like they should be upgraded:
Fetched 0 B in 0s
fetch.run() result: 0
blacklist: []
whitelist: []
No packages found that can be upgraded unattended and no pending auto-removals

啟用防火牆

不少雲端基礎服務商,如 DigitalOcean, 都有提供雲端防火牆,如果是多台主機的情況下,使用雲端防火牆比較方便。以本文的單一主機來說,直接使用 Ubuntu 內建的 ufw 來提供簡易主機防火牆。

1
2
3
4
ufw allow 80
ufw allow 443
ufw allow ssh
ufw enable

其中,假如 SSH 服務的監聽埠不是預設的 22 的話,要把 ssh 改成對應的埠號。

注意: 不開啟 NodeBB 的 4567, 這個只允許從本機 (localhost) 連線

資料庫安裝 (Redis)

直接安裝系統預設的 Redis 套件,使用下列指令安裝:

1
$ sudo apt-get install -y redis-server

安裝後,為了讓 Redis 服務開機時自動啟動需要執行下列指令:

1
$ sudo systemctl enable redis.service

Redis 是作為 NodeBB 的主資料庫,所以資料持久性 (persistence) 需要特別設定以減少系統不正常當機時資料遺失的風險。為了這個目的,啟用 Redis 的 append-only log,底下是 append-only log 以及幾個相關的設定值(檔案 /etc/redis/redis.conf):

1
2
3
4
daemonize yes
supervised systemd
requirepass dsfjklewreriier # redis 客戶端連線密碼,請不要直接套用,讀者需自訂。
appendonly yes

讀者需要將設定項目改成對應的設定值。

設定修改完成後,使用下列指令重啟 Redis:

1
$ sudo systemctl restart redis.service

Redis 安裝可參考 如何在 Ubuntu 18.04 上安裝和配置 Redis

Web 安全憑證 (Let’s Encrypt)

Web 伺服器憑證是伺服器域名的身分證明,需要向一個 CA 組織申請核發,Let’s Encrypt 是個免費且受歡迎的 CA。 由於 Let’s Encrypt 已經支援 wildcard 域名憑證,本文使用 DNS 認證機制來申請 wildcard 憑證。

參考下列文章安裝 certbot 進行申請憑證:

因為希望憑證同時支援裸域名 (developers.tw) 以及子域名(*.developers.tw),申請指令要稍微修改

1
sudo certbot --server https://acme-v02.api.letsencrypt.org/directory -d *.developers.tw -d developers.tw --manual --preferred-challenges dns-01 certonly

按照畫面指示新增指定名稱的 DNS TXT 紀錄並進行驗證,如通過憑證檔案位於 /etc/letsencrpyt 目錄。

Web 服務 (Nginx)

Web 服務有下列目的:

  • 作為 NodeBB 負載均衡器(load balancer)。
  • 支援 TLS/SSL 網路加密結定。
  • 服務 NodeBB 靜態檔案資源。

#Developers.TW 使用 Nginx 作為 web 服務軟體。

安裝 Nginx 套件

1
$ sudo apt-get install -y nginx

設定 Nginx

本文的安裝,NodeBB 服務只接受本地 (127.0.0.1) 連線,監聽埠是 4567。底下將 Nginx 設為 NodeBB 服務的代理伺服器。

新增 /etc/nginx/sites-available/developers.tw,檔案內容如下:

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
upstream backend {
server 127.0.0.1:4567;
}

server {
listen 80;

access_log off;
error_log off;

server_name developers.tw www.developers.tw;

return 301 https://developers.tw$request_uri;
}

server {
listen 443 ssl;
listen [::]:443 ssl;

access_log off;
error_log off;

server_name www.developers.tw;

ssl on;
ssl_certificate /etc/letsencrypt/live/developers.tw/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/developers.tw/privkey.pem; # managed by Certbot

return 301 https://developers.tw$request_uri;
}

server {
listen 443 ssl http2;
listen [::]:443 ssl http2;

error_log /var/log/nginx/developers.tw-error.log;
access_log /var/log/nginx/developers.tw-access.log;

server_name developers.tw;

root /opt/nodebb/public;
index index.html;

gzip on;
gzip_min_length 1000;
gzip_proxied off;
gzip_types text/plain application/xml text/javascript application/javascript application/x-javascript text/css application/json;


ssl on;
ssl_stapling on;
ssl_stapling_verify on;
ssl_certificate /etc/letsencrypt/live/developers.tw/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/developers.tw/privkey.pem; # managed by Certbot
ssl_protocols TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
ssl_session_cache shared:SSL:10m;

proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;

proxy_redirect off;

# Socket.IO Support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";


location @backend {
proxy_pass http://backend;
}

location ~ ^/assets/(.*) {
root /opt/nodebb/;
try_files /build/public/$1 /public/$1 @backend;
}

location /plugins/ {
root /opt/nodebb/build/public/;
try_files $uri @backend;
}


location / {
proxy_pass http://backend;
}
}

同樣地,developers.tw 需要更換為論壇實際使用的域名。 除了來自 NodeBB 官方文件的建議設定外,也設定了強制將 www.developers.tw 轉址為 developers.tw,且 http 協定轉成 `https’,也就是說: 只接受 HTTPS 連線。

啟用 NodeBB 的站點

1
2
3
$ sudo ln -s /etc/nginx/sites-available/developers.tw /etc/nginx/sites-enabled/
$ sudo nginx -t
$ sudo systemctl restart nginx

到這個步驟,沒問題的話可以使用瀏覽器連線到 https://developers.tw , 但因為後端的 NodeBB 還沒啟用,網頁會出現 503 錯誤。

執行環境安裝 (NodeJS)

NodeJS 以及 NodeBB 的安裝都是參考這篇文章 How To: Install NodeBB on DigitalOcean/Ubuntu 18.04

使用下列指令安裝 NodeJS:

1
2
3
$ sudo curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash -
$ sudo apt update
$ sudo apt install nodejs -y

應用服務安裝(NodeBB)

新增 nodebb 帳戶

為了權限控管,NodeBB 服務使用 nodebb 帳戶執行。使用下列指令新增帳戶:

1
$ sudo adduser nodebb

複製 NodeBB 原始程式

1
2
3
4
$ cd /opt
$ sudo git clone https://github.com/NodeBB/NodeBB.git nodebb
$ sudo chown -R nodebb:nodebb ./nodebb
$ sudo su - nodebb

初始設定 NodeBB

1
2
$ cd /opt/nodebb
$ ./nodebb setup

./nodebb setup 會詢問一系列問題,除下列外其餘使用預設值:

項目 設定值
url https://developers.tw
database 選擇 redis
redis password 在 < code > 資料庫安裝 (Redis) 章節中,requirepass 的設定值

url 需要根據實際的域名做修改,上面範例是 #Developers.TW 的域名。

讓 NodeBB 隨系統自動啟動

使用文字編輯器新增 / 修改檔案 < code>/etc/systemd/system/nodebb.service,內容為:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[Unit]
Description=NodeBB
Documentation=https://docs.nodebb.org
After=system.slice multi-user.target redis.service

[Service]
Type=forking
User=nodebb

StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=nodebb

WorkingDirectory=/opt/nodebb
ExecStart=/usr/bin/node loader.js
Restart=always

[Install]
WantedBy=multi-user.target
1
2
3
$ sudo systemctl daemon-reload
$ sudo systemctl enable nodebb.service
$ sudo systemctl start nodebb.service

啟動完成後,就可以開啟瀏覽器連到 https://developers.tw/admin , 登入控制台進行 NodeBB 的管理與設定。

小結

經過冗長的來來回回步驟後,終於有個可以上線的部署環境。但有了基本部署環境,NodeBB 還要根據需要進行設定與客製化,這個才是重中之重。NodeBB 的設定可以參考 官方的文件 ,而 NodeBB 的進階功能,不少需要額外申請雲端服務(如 Facebook SSO) 或者安裝軟體(如 Solr 等),讀者可視目的來決定是否有需要。