客製化你樹莓派上運行的 Linux

最近因為手邊有一個工作以外的專案需要搭配硬體做一些 IoT 類型的應用,雖然之前在五倍紅寶石開發的 Tamashii 系列應用已經足以應對在這個專案上開發所需的解決方案,但是依舊缺少了一些功能。

也就是我們過去並沒有考慮到的,如果裝置是交給一般使用者的狀況下,如何在透過網路的前提將裝置更新。

這是很多硬體都會有的功能,但是就目前而言 Tamashii 並不支援。

經過幾天的調查,發現有一個 Open Source 的專案似乎符合條件。

Mender 的 OTA 伺服器

這個開源專案叫做 Mender 是透過好幾種程式語言組合而成,功能也很簡單。在預先製作好的 Linux 發行版本中寫入 Mender 伺服器的位置,只要在伺服器上「認證」這一台裝置,未來就能夠收到來自 Mender 伺服器所提供的更新。

而更新的方式基本上就是製作一份新的 Rootfs 提供給硬體裝置下載,並且嘗試將這個新的版本加入到現有硬體中,並且嘗試是否能夠正常的運行,如果失敗的話再將舊版的 Rootfs 載入。

也因此,要能夠使用 Mender 來發布 OTA (Over the Air)更新的話,就必須要能夠製作自己的 Linux 發行版本才可以。

Yocto 專案

想要讓所有人都知道怎麼自己編譯完整的 Linux 作業系統是很困難的,從韌體、核心(Kernel)到各種開機所需要的套件庫(Library)等等,光是編譯的步驟就非常繁複,更何況還要配合使用不同開發版或者晶片的使用者。

所以 Mender 也採許了另一套開放原始碼的解決方案,叫做 Yocto 專案。這個專案跟另一個開源專案 OpenEmbedded 已經整合在一起,或者說能夠互通使用。

在 Yocto 之中,我們透過所謂的 Layer 的疊加就能夠製作出我們所需的 Linux 系統,而有很多硬體上所需要配合的韌體,也大多會有社群貢獻,因此在大部分的情況下都不太需要擔心。

Mender 團隊也提供付費協助處理硬體整合上的問題,也許這是主要的收入之一?

舉例來說,我想要製作一個能在 RaspberryPi 上面執行的 Linux 環境,就需要叫做 meta-respberrypi 這一個 Layer 來幫助我。

他會依賴於 oe-core 和幾個相關的 Layer 才能夠正確編譯(因為已經有的設定不用重複撰寫)

當我加入 meta-raspberry 之後,我在選擇編譯的機器類型時,就能夠用像是 MACHINE=raspberrypi3 這樣的模式告知我希望得到能在 Raspberry Pi 3 上執行的 Linux。

最棒的是,當我們完成這個動作之後,生成的 Linux 鏡像檔案燒入到 SD 卡中就能夠正常運行。

以前嘗試過使用 Buildroot 來製作,但是失敗率非常高。

簡單來說 Yocto 就是一堆社群貢獻預先撰寫好的建置腳本,因此我們只需要專注在自己需要預先加入這個 Linux 環境的部分,像是 Tamashii 或者 Ruby 的運行環境。

初次嘗試

首先,我們需要可以運行的環境。這篇文章使用的是 CentOS 7 來進行示範,所需的相關套件可以參考 Yocto 官方文件來配置,另外要注意的是目前較新版本是需要有 Python 3 的環境,但是 CentOS 7 還是使用 Python 2 需要自己配置。

下載 Poky

git clone -b sumo git://git.yoctoproject.org/poky

Poky 類似於一個基礎的樣板,裡面將生成 Yocto 版本的 Linux 必要的相關檔案都放在裡面,我們可以基於這個資料夾來進行後續的設定跟配置。

Yocto 每個版本都會有代號,目前最新的穩定版是 Sumo (2.5) 版

加入 Mender

這篇文章會以 Mender 作為例子,這樣在成品階段的時候也比較好用 Mender 來體驗。

cd poky
git clone -b sumo git://github.com/mendersoftware/meta-mender

如此一來,我們就可以在後續的階段使用由 Mender 所製作的 Layer 來提供 OTA 的功能。 不過在此之前,因為我們希望製作的是 Raspberry Pi 版本的 Linux 發行版本,所以還需要先把 Raspberry Pi 對應的 Layer 加入。

git clone -b sumo git://git.yoctoproject.org/meta-raspberrypi
git clone -b sumo git://git.openembedded.org/meta-openembedded

配置 Layer

source oe-init-build-env

因為 Yocto 已經提供好了各種設置,所以我們只需要透過上面的指令就能切換到對應的建置環境中。

預設會產生一個 build 目錄,如果想要其他目錄的話也可以在後面指定。

然後我們要告訴 Yocto 想要使用哪些 Layer 才能夠正常運作。

bitbake-layers add-layer ../meta-mender/meta-mender-core
bitbake-layers add-layer ../meta-openembedded/meta-oe
bitbake-layers add-layer ../meta-raspberrypi
bitbake-layers add-layer ../meta-mender/meta-mender-raspberrypi

加入上述的 Layer 後,我們就可以產生一個能在 Raspberry Pi 上運行,以及透過 Mender 來做 OTA 更新的 Linux 發行版本。

不過礙於篇幅的關係,這次我們直接使用 Mender 提供的 Demo Layer 來加入客製化的內容。

如果想加入自己編譯的程式、服務等等,是需要自己建立一個 Layer 來加入的,這樣也能對原有的 Layer 做擴充或者增加設定。

bitbake-layers add-layer ../meta-mender/meta-mender-demo
bitbake-layers add-layer ../meta-mender/meta-mender-raspberrypi

接下來,我們要對 conf/local.conf 進行設定,把伺服器位置等等設定值都加入到產生的 Linux 發行版本中,才能夠連接到正確的 Mender 伺服器。

# 釋出的版本,需要不同才能被辨識出來
MENDER_ARTIFACT_NAME = "release-1"

INHERIT += "mender-full"

# 指定為 Raspberry Pi 3 是預設的目標
MACHINE ?= "raspberrypi3"

# 針對 Raspberry Pi 的額外設定
RPI_USE_U_BOOT = "1"
MENDER_PARTITION_ALIGNMENT = "4194304"
MENDER_BOOT_PART_SIZE_MB = "40"
IMAGE_INSTALL_append = " kernel-image kernel-devicetree"
IMAGE_FSTYPES_remove += " rpi-sdimg"

# 你的 Mender OTA 更新伺服器
MENDER_SERVER_URL = "https://ota.tamashii.io"

DISTRO_FEATURES_append = " systemd"
VIRTUAL-RUNTIME_init_manager = "systemd"
DISTRO_FEATURES_BACKFILL_CONSIDERED = "sysvinit"
VIRTUAL-RUNTIME_initscripts = ""

ARTIFACTIMG_FSTYPE = "ext4"

# Raspberry Pi WiFi 設定(Demo 才有開啟 WiFi 功能)
MENDER_DEMO_WIFI_SSID ?= "ssid"
MENDER_DEMO_WIFI_PASSKEY ?= "password"

建置

bitbake core-image-full-cmdline

執行這個指令後,大概會要花上數小時才會完成,這段時間可以睡覺或打個遊戲。

開發階段我會推薦使用 full-cmdline 的版本,因為可以直接 SSH 到機器或者接上鍵盤除錯。

確認跑起來都沒問題之後,就可以改為使用 core-image-minimal 製作出刪減掉除了開啟 Linux 以及自己加入的額外功能之外,所有不必要的檔案來盡可能的縮小檔案大小。

後記

到這篇文章完成建置的段落,其實從頭到尾只花上數小時。不過目前還在測試如何讓有 C Extension 的 Ruby Gem 可以正常的被 Cross Compile 並且放進自訂的發行版本。

如果只是想單純的啟用 Ruby 的功能,直接在 conf/local.conf 裡面加上這行。

IMAGE_INSTALL_append = "ruby "

在自訂的發行版本就可以使用 ruby 指令(目前預設是 2.5.0 版)

總結

這篇文章其實只是很粗略的將 Yocto 可以做的事情介紹出來,實際上深入了解 bitbake 這套工具以及 Layer 機制後,就會發現還有很多東西可以做。

舉例來說能透過 .bbclass 定義一個範本(Ex. rubygem.bbclass)讓其他套件(Package)繼承使用,而除了 Layer 之外,底下還有細分了食譜(Receipe)和套件(Package)可以做很多變的調整。

或者透過 .bbappend 來對原本的套件修訂,像是目前正在製作的 Tamashii Linux 就是利用這種方法讓 Ruby Gem 的 Cross Compile 得以實現。

如果之後已經有成熟的 Tamashii Linux 案例,會在分享該如何在 Yocto 上面客製化以 Ruby 為基底的 IoT 裝置嵌入是系統。


本文同步刊載於弦而時習之