樹莓派的重啟人生

前言

最近在用 Node 來重寫原本的 Discord bot,寫到一個段落後想部屬卻碰上了開發環境和部屬環境相差過大的問題,由於花了兩到三天才完全解決,就寫一篇文章來記錄一下這段期間的 debug 過程。

正文

NodeJS 在 ARMv8 只提供 x64 Prebuilt Binary

Ubuntu Server x64

一上來就碰上這個麻煩的問題,為了要躲自行編譯 Node 的天堂路,我決定直接重灌樹梅派的 OS,將原本官方提供的 x32 OS 換成 x64 的。

樹莓派官網提供的OS清單

官方其實有做 x64 的 image,不過似乎還是測試版,所以這裡找不到

我腦筋動到了 Ubuntu Server 上,根據樹莓派官網的說明,我來到 Ubuntu 的官網,下載 Ubuntu 官方為樹莓派特別製作的 image

Ubuntu Server的樹梅派版

在樹莓派上灌好 x64 的 Ubuntu Server 20.04.3 以後卻發現無線網路無法正常連線,經過數小時的奮戰後還是修不好(網路相關的 service 沒辦法正常執行和重啟),只好換個版本的 OS 試試看。

我換上了 18.04.5,應該算是老版本了,然而一開機就發現有些 service 沒有正常執行。

1
sudo systemctl status

發現有個服務死了

1
sudo systemctl --failed

看到死掉的是systemd-modules-load.service

1
2
sudo systemctl restart systemd-modules-load
sudo systemctl status systemd-modules-load

這樣可以看到死掉服務的 PID

1
journalctl PID=????

查詢他死掉的原因,會發現系統缺少了一個 module,所以 google 到以後就把這個模組在讀取的設定註解掉,之後重啟服務後系統就會正常了。

我又開始嘗試連上 wifi,結果還是死的,因此又換了 21.04 的 OS 測試。

21.04 的系統服務沒死,但是要 apply netplan 的時候還是會失敗,於是我就把 Ubuntu 丟了。

Raspberry Pi OS x64

我從其他地方找到放在官網上面的 x64 Raspberry Pi OS (連結),所以決定裝這個試試。

這次不小心裝到桌面版本,不過進去以後發現系統基本上是正常運作,所以就開始安裝 Node 以及其他要用到的 Package。找尋作業系統的工作到此為止,噩夢卻還沒結束。

Sharp 在平台上沒有編譯好的 Binary

嘗試自行編譯

Node 的安裝不難,從官網上載下來解壓後加入 PATH 就行了。會怕的話可以參考這篇文

安裝 npm package 時發現 Sharp 在 ARM64 平台上 glibc 要>=2.29 才有編譯好的 Binary 能用,這個 OS 上面的剛好是 2.28,我就開始嘗試自行編譯了。

一開始要先安裝 Sharp 的 dependency(libvips),這個軟體一樣要自己編譯,我在嘗試編譯時失敗了,所以打算換換看 glibc 版本,就到官網載了 glibc 的 source code 跑編譯。

一開始編譯會遇上錯誤,基本上是找不到include的檔案,所以是個 PATH 問題,不難解決,

我參考這篇文章建立了一個連結讓 PATH 可以指到 compile 時要用到的檔案路徑就可以正常 compile 了,大概跑了快一小時才跑完。

編譯好後安裝,結果安裝失敗,OS 死去,回到重灌環節。

用 Docker 嘗試搞定環境

這次把腦筋動到 Docker 身上來,如果利用 Docker 的執行環境,應該就能弄出一個有不同 glibc 的環境。

Dockerfile

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
FROM node:16.8-buster

# ENV NODE_ENV=production

RUN mkdir -p /app

WORKDIR /app

# general package

RUN npm install -g node-gyp

# discord js core

RUN npm install discord.js @discordjs/rest discord-api-types @discordjs/builders \
&& npm install zlib-sync \
&& npm install bufferutil \
&& npm install utf-8-validate
# && npm install discord/erlpack

# voice support

RUN npm install @discordjs/voice \
npm install sodium \
&& npm install @discordjs/opus

# other package

RUN npm install async-lock \
&& npm install axios \
&& npm install beautify.log \
&& npm install file-type \
&& npm install sequelize \
&& npm install nodemon \
&& npm install mariadb \
&& npm install sharp

COPY ./src /app/src
COPY ./deploy-commands.js /app
COPY ./index.js /app

# deploy commands
RUN node deploy-commands.js

CMD [ "node" , "index.js"]

這裡用很暴力的方式維護 package,因為如果直接把package.json copy 進去 install 的話會發現開發機裝得起來的 package 部署機不一定裝得起來,所以最後決定把 package 分開維護。

其實開發機的環境後來也死了,最終連開發機都用 docker 跑,整個人變成 docker 的形狀

上面的環境其實還是跑不起來,主要還是 glibc 的版本問題。如果能進到 container 內的話執行ldd –version就會發現 glibc 版本還是<2.29,不過這次用的是 docker,所以直接換 node 的 image 就好。

Dockerfile

1
FROM node:16.8-bullseye

這樣程式就能跑起來了,謝天謝地。

後記

上面就是從重灌到最後部署成功走過的冤枉路,總共其實重灌了五次 OS,整個人都不好了,所以這篇文章也寫得很隨便,單純當作個踩坑紀錄。

題外話: 最近 Docker 更新了使用規約,有些小企業會開始被收費,不過對個人使用還沒有影響,不太需要擔心。