Portaler Zone 是一款為 Albion Online 打造的社群工具,其目的是共享社群間的地圖資訊。
註:事實上我也不知道這到底是啥,這次單純是因為有個人委託我去幫他建構這個開源專案。因為前前後後遇到的坑有點多,所以這邊做個筆記。說明上主要以自己理解為主,可能會有不正確的地方,還請各位玩家多多包涵。
架構分析
這是個標準的前後端分離專案,雖然所有的程式碼都存放在一個 Repository 底下,但是基本上被切得很乾淨。
值得一提的是,作者幾乎所有內容都是用 Typescript 寫的,這大大降低了在 Debug 時的困難度。
後端
後端一共有三個服務
- API: 用於提供 HTTP API,以 Express 建構
- Bin ETL: 用於解析 Albion Online 的相關資源
- Discord Bot: 用於與 Discord Server 溝通,方便在社群之間分享地圖
無論如何,作者為這三個後端 服務都提供了符合開放容器標準(OCI)的 Image(更白話一點的說,Docker Image),基本上這三個服務架設並沒有什麼難度。
順帶一提,這個服務還另外需要 PostgreSQL 與 Redis,在 docker/docker-compose.yml
中有定義相關的資訊。
前端
前端共有兩個服務
比較麻煩的是,這兩個前端專案都沒有提供 Image,必須自行建構。
建置步驟
註:事實上,文件 其實寫得很清楚,基本上我這邊只提在 Raspberry Pi 上面構建時需要注意的事項。
系統環境
- 裝置:Raspberry Pi 4 Model B, 4GB RAM
- 作業系統:Manjaro Linux ARM x64
因為自己習慣使用 Arch Linux,但是因為官方並未提供 ARM 版本的,於是這邊用 Manjaro Linux 代替。
考量到系統資源的限制(畢竟只有 4GB + 32GB 的 SD 卡),我選擇使用 ARM 8 Raspberry Pi Minimal,下載後用 Raspberry Pi Imager 將作業系統燒進 SD 卡即可。
設定系統與更新
在 /etc/pacman.d/mirrorlist
中加入以下內容,以使用台灣(國網中心)及日本的鏡像
Server = http://free.nchc.org.tw/manjaro/arm-stable/$repo/$arch
Server = http://ftp.tsukuba.wide.ad.jp/Linux/manjaro/arm-stable/$repo/$arch
Server = http://ftp.riken.jp/Linux/manjaro/arm-stable/$repo/$arch
然後執行 sudo pacman -Syyu
更新系統,第一次會花費較久時間
注意:如果沒有更新的話,無論安裝什麼軟體都會憑證錯誤
安裝 Container Runtime
在 Linux 上我傾向使用 containerd 取代 docker,主因是更加輕量化。
$ sudo pacman -S containerd
$ sudo systemctl start containerd
$ sudo systemctl enable containerd
之後,你應該可以用 sudo systemctl status containerd
看到 containerd 正常運作。
為了更方便使用 containerd,我通常會安裝 nerdctl,這樣使用體驗就更趨近於 Docker CLI。
注意:請不要從 AUR 上面安裝 nerdctl,目前 AUR 上提供只有 x86_64 架構的 PKGBUILD
$ wget -O nerdctl.tar.gz https://github.com/containerd/nerdctl/releases/download/v0.14.0/nerdctl-0.14.0-linux-arm64.tar.gz
$ tar -xf nerdctl.tar.gz
$ sudo cp nerdctl /usr/local/bin/nerdctl
之後,你應該可以使用 sudo nerdctl images
看到目前系統上有哪些 Images。
註:你可以自行設定 Rootless Containerd,不過這超出本文的範圍所以先略過
下載 Portaler Zone 程式碼
$ sudo pacman -S git
$ git clone https://github.com/Portaler-Zone/portaler-core.git
你應該可以看到當前資料夾下有 portaler-core 的資料夾,裡面就是 Portaler Zone 的程式碼
建構後端 OCI Image
事實上,作者在 Docker Hub 是有提供現成的 Image:
很顯然地,它沒有支援 ARM 架構,所以我們只好自己建構。
在建構之前,我們需要為 containerd 安裝 buildkit:
$ wget -O buildkit.tar.gz https://github.com/moby/buildkit/releases/download/v0.9.3/buildkit-v0.9.3.linux-arm64.tar.gz
$ tar -xf buildkit.tar.gz
$ sudo cp -R bin /opt/containerd
$ sudo cp -R bin/buildkitd /usr/local/bin
$ sudo buildkitd
註:
buildkitd
會佔用一個 Shell,看要用 Systemd 或是另起一個 Shell 都行
然後便可以正常在 ARM 下建構 Image:
註:以下指令要位於
portaler-zone
的專案資料夾下執行 註2:受限於 Raspberry Pi 的系統性能與 SD 卡的寫入速度,以下指令可能會花較多執行執行
$ sudo nerdctl build -t mawburn/portaler -f docker/api.dockerfile .
$ sudo nerdctl build -t mawburn/portaler-bot -f docker/bot.dockerfile .
$ sudo nerdctl build -t mawburn/portaler-etl -f docker/etl.dockerfile .
建構前端
因為前端程式並沒有提供 Image,相較於後端服務而言它的麻煩程度較高。
註:以下指令要位於
portaler-zone
的專案資料夾下執行
$ sudo pacman -S nvm python2
$ source /usr/share/nvm/init-nvm.sh
$ nvm install 14
$ nvm use 14
$ npm install -g corepack
$ corepack enable
$ yarn
$ yarn build:frontend
因為這個專案使用的 node-sass
版本為 ^4.14.1
(定義於 packages/frontend/package.json
),需要使用 Node 14,所以我們先用 nvm 安裝 Node 14。
又因為 Node 14 尚未支援 corepack
(直到 Node 16 才支援),所以我們需要用 npm install -g corepack
安裝它並啟用。
murmur:作者完全沒講清楚,浪費我 10 分鐘 Debug
註:事實上,在 Raspberry Pi 上面建構不太明智,主要是因為 SD 卡的寫入速度限制(當然,如果你的財力可以買 V90 那就當我沒說)與硬體效能跑 yarn 實在有點勉強,也是可以在 x86_64 的系統上建構好再直接丟過來。
啟動後端服務
因為 ARM 架構上的問題,我們必須編輯 docker/docker-compose.yml
才能正常啟動服務:
networks:
portaler:
driver: 'bridge'
volumes:
db_data: {}
services:
pgdb:
image: postgres:13-alpine
env_file:
- .env.example
volumes:
- db_data:/var/lib/postgresql/data
networks:
- portaler
rediscache:
image: 'redis:6.0-alpine'
env_file:
- .env.example
networks:
- portaler
discord_bot:
image: mawburn/portaler-bot
env_file:
- .env.example
environment:
- API_URL=https://api_server/
depends_on:
- pgdb
- rediscache
networks:
- portaler
api_server:
image: mawburn/portaler
env_file:
- .env.example
ports:
- '127.0.0.1:7777:4242'
depends_on:
- pgdb
- rediscache
networks:
- portaler
bin_etl:
image: mawburn/portaler-etl
env_file:
- .env.example
depends_on:
- pgdb
- rediscache
- api_server
networks:
- portaler
因為原始的 bitnami/redis:6.0
並不支援 ARM,所以更改為 Docker 官方提供的 redis:6.0-alpine
另一方面因為使用了 bridge
這個 network driver,我們必須先行安裝
$ wget -O cni-plugins.tgz https://github.com/containernetworking/plugins/releases/download/v1.0.1/cni-plugins-linux-arm-v1.0.1.tgz
$ sudo mkdir -p /opt/cni/bin
$ sudo tar -C /opt/cni/bin/ -xf cni-plugins.tgz
最後,把 .env.example
裡面的 REDIS_PASSWORD
註解掉(前方加入 #
),即可用以下指令啟動服務
$ sudo nerdctl compose -p portaler up -d
註:啟動前請記得要填寫
.env.example
裡面的資訊,例如HOST
改成自己的域名、ADMIN_KEY
要強一點,以及設定 DISCORD 相關的 Token 及 Role。
啟動 Nginx 服務
因為目前有後端(位於 localhost:7777
)及前端(HTML, CSS, JS 檔),官方文件中說明應該啟動一個 Nginx 服務去整合兩者。
$ sudo pacman -S nginx
$ sudo mkdir -p /var/www/
$ sudo ln -s ~/portaler-core/packages/frontend/build /var/www/portaler
然後建立 /etc/nginx/conf.d/portaler.conf
(如果 conf.d
資料夾不存在的話就自行建立),內容如下:
server {
listen 80;
location /api/ {
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $http_host;
proxy_pass http://localhost:7777/api/;
}
location / {
root /var/www/portaler/;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
並 且編輯 /etc/nginx/nginx.conf
為下列內容
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type text/html;
sendfile on;
include /etc/nginx/conf.d/*.conf;
}
註:這邊的僅為設定範例,你應該自行設置適合的參數
確認服務
使用 curl localhost
及 curl localhost/api/health
確認服務狀態之後,就可以打完收工。
結論
這次的委託其實還滿有趣的,因為是在 ARM 的架構上,跟以往 x86_64 的體驗很不同(例如常常噴一堆錯,偶爾還要自己通靈)
不得不說 containerd 真的是很棒的 Docker 替代方案,雖然只有在 Linux 上的體驗(在其它系統基本上還是要依賴虛擬機,與 Docker 原生支援的體驗就明顯有差)。
順帶一提,這樣整套下來大概會吃掉 500MB 的記憶體,其實這個服務完完全全是可以開 Digital Ocean, Linode 與 AWS Lightsail 上的,每個月花費約 5 美金,其實如果自架的話可以考慮不用這麼在 Raspberry Pi 上折騰。