在 Raspberry Pi 2 運行 Docker


好久沒來當門外漢撞牆了,最近想到將自己的一些虛擬機(環境)需求,移到 Raspberry Pi 2 上有沒有可能,於是就想到在 Raspberry Pi 2 上跑 Docker,只是,自己既不熟 Raspberry Pi,也不熟 Docker,這次的門外漢撞牆撞得還真是不少 …

既然如此,這牆都撞了就別白撞了,就來記錄一下撞牆的經驗如何!照例,因為是門外漢,看到有錯還請鞭小力一點 … XD

Pi 運行 Docker 的幾個方案

搜尋了一下網路,要在 Pi 上運行 Docker 的方案,最常見的作業系統推薦是 Archlinux,既然如此,就先照著 Archlinux 上的 〈Raspberry Pi〉進行作業系統安裝。

我先用已經有的 Raspbian 來做 Archlinux 的 SD Card,基本上只要照著網頁做就沒什麼問題,要注意的是使用 fdisk 時,如果 SD Card 是位於 /dev/sda1,那麼一開始要下的指令是 fdisk /dev/sda(而不是 sda1),分割完成後使用 fdisk -l 就可以看到 SD Card 被分割的兩個磁區裝置名稱(例如 /dev/sda1、/dev/sda2),建立與 mount 檔案系統時,就使用這兩個名稱。

只不過安裝好之後,我才想到,因為是使用手機無線分享,所以沒辦法接網路線進行接下來的動作了,因而我轉而尋找安裝完就可以設定無線網路的方案,如果你對 Archlinux 上安裝 Docker 仍有興趣,可以參考〈在樹莓派 2 (Raspberry Pi 2) 安裝 docker 並執行範例〉,在該討論串中也有提到,Raspberry Pi 2 現在可以安裝 Ubnutu 14.04,然後在上頭安裝 Docker,這我沒試過,純綷記錄一下,你可以自己試試看。

使用 HypriotOS

我後來找到〈HypriotOS: Back Again and Better then Ever〉這篇文章,採用了當中提供的 Docker-Pi Image,這就方便多了,Windows 使用者也可以使用 Win32DiskImager 將映像檔寫到 SD Card 中,這個映像檔是基於 Raspbian,裏頭包括了 Docker 1.5。

不過,中間發生了點小插曲,我用來寫映像檔的 SD Card 跟 Raspberry Pi/Raspbian 打架了,啟動會失敗,錯誤訊息就像是〈Raspberry Pi 2 B SD card problem on boot〉,不是每個 SD Card 都會發生這個錯誤,不過我就是遇上了。

沒辦法,就像 qrtt1 說的:「門外漢都比較帶賽一點xd」。

遇上了總得解決問題,在〈Raspberry Pi 2 B SD card problem on boot〉中最後有提到,可以使用 rpi-update 更新 kernel 與靭體,問題是沒辦法開機啊?不過,討論中也有提到,可以使用另一台 Linux 來對 SD Card 中第二個磁區做 fsck(例如 fsck -f /dev/sda2),這樣就可以開機(不過後續仍有可能開機錯誤),我後來就趁 fsck 過開機成功那次,做了 rpi-update,後來就沒有開機錯誤的問題了。

安裝完成之後,先在 Raspberry Pi 上接上鍵盤,開機後登入的名稱是 pi,密碼是 raspberry,執行 docker –version,你就可以看到以下的畫面:

Docker on Raspberry Pi

設定密碼與網路

要修改 pi 的密碼,可以執行 raspi-config,在裏頭進行修改,順便也擴充一下檔案系統,運用整個 SD Card 容量:

Docker on Raspberry Pi

至於無線網路設定的部份,我是參考〈Java Embedded(3)Raspberry Pi 硬體週邊與作業系統設定〉中的設定,修改 /etc/network/interfaces,請執行:

sudo nano /etc/network/interfaces

將裏頭的內容修改為:

auto lo

iface lo inet loopback

auto wlan0

iface wlan0 inet static
    address 想要的 IP 位址
    netmask 子網路遮罩
    gateway 預設閘道/路由器
    wpa-ssid 無線網路的名稱
    wpa-psk 無線網路的密碼

修改完成以後,依序按下「Ctrl+X」、「Y」與「Enter」儲存檔案,接著修改 DNS Server 設定,因為我實際上運行時,預設的 DNS Server 設定沒有用,請執行:

nano /etc/resolv.conf

加入新的 DNS Server 設定,例如:

nameserver 8.8.8.8
nameserver 8.8.8.4

同樣依序按下「Ctrl+X」、「Y」與「Enter」儲存檔案,接著 sudo poweroff 關機,拔除電源與 USB 鍵盤,重新接上電源開機完成後,你就可以 SSH 至方才指定的 IP 位址,使用 pi 與自設的密碼登入。

建立映像檔

接下來就可以開始使用 Docker 建立映像檔了,很奇怪,我第一次要 Pull 映像檔時,Docker 服務並沒有啟動,只好先 sudo service docker start 手動一下,之後倒是就沒有這個問題。

接下來的問題是,遇上了 x509 證書過期的錯誤訊息?這 … 查了一下網路,結果是時間同步上有點問題,於是執行 sudo ntpclient -s -h pool.ntp.org 同步一下時間。

接下來就開始要 Pull Image 了,Raspberry Pi 是 ARMv7,可用的映像檔可以在 Docker 上查詢 rpi 尋找,我選擇從 resin/rpi-raspbian 開始建置,我第一個想建置的就是 Java 8 與 Gradle 2.6 的環境,我最後整理出的 Dockerfile 是:

FROM resin/rpi-raspbian
MAINTAINER Justin Lin <caterpillar@openhome.cc>

# Basic tools
RUN apt-get -qq update && \
    apt-get -qqy install wget && \
    apt-get -qqy install vim && \
    apt-get -qqy install unzip && \
    apt-get -qqy install git

# Java 8
WORKDIR /opt
RUN wget http://your-url/jdk-8u60-linux-arm32-vfp-hflt.gz && \
    tar zxvf jdk-8u60-linux-arm32-vfp-hflt.gz && \
    rm jdk-8u60-linux-arm32-vfp-hflt.gz

ENV JAVA_HOME /opt/jdk1.8.0_60

# Gradle
RUN wget https://downloads.gradle.org/distributions/gradle-2.6-bin.zip && \
    unzip gradle-2.6-bin.zip && \
    rm gradle-2.6-bin.zip

ENV GRADLE_HOME /opt/gradle-2.6

# PATH
ENV PATH $PATH:$JAVA_HOME/bin:$GRADLE_HOME/bin

WORKDIR /

要注意使用的 JDK 是 Linux ARM v6/v7 Hard Float ABI,這可以在 Oracle 網站下載,為了安裝方便,我是下載後放在自己的伺服器上,因此,想要使用以上的 Dockerfile 的話,可以將其中「your-url」改為實際上 JDK 的檔案所在網址。

最後,終於在 Raspberry Pi 上把想要的東西搞定了(初步啦!) … 懶得自己 build 的話,就直接 pull 我的 caterpillar/rpi-gradle 吧! … XD

Docker on Raspberry Pi