Ubuntu Weekly Recipe

第364回Raspberry Pi 2でDockerとownCloudを動かす

Raspberry Pi 2をサーバーとして動かすとなると、やはりコンテナ型仮想化を使えると便利です。そこで今回はRaspberry Pi 2上でDockerを使う方法を紹介します。

よりUbuntuらしいインストールイメージ

先月の第362回では、Raspberry Pi 2にUbuntuをインストールする方法として、Snappyのカーネルやinitramfsと、Ubuntu Coreのルートファイルシステムを組み合わせて使う方法を紹介しました。この方法は手元でカーネルやinitramfsのビルドは必要ないものの、カーネルのアップグレードは手作業でbootパーティションを変更しなくてはいけない、カーネルのコンフィグがUbuntuのそれと微妙に異なるなど、いろいろと不便な点も存在していました。

そんな中、この記事と前後してCanonicalのRyan FinnieがRaspberry Pi 2のカーネルやファームウェアをパッケージング、PPAで公開し、さらにインストール済みイメージを配布しています。こちらのイメージはカーネルも含めてパッケージ管理システムで管理できるので、より「Ubuntuらしい」イメージになっています[1]⁠。配布イメージはrawのイメージファイルなので、SDカードにddするだけと、とてもお手軽です。

今回はまず最初に、Ryan Finnieのイメージを使ってRaspberry Pi 2にUbuntuをインストールする方法を紹介します。

イメージのインストール

4GB以上のmicroSDHCカードを用意してください。インストール済みのイメージをダウンロードし、展開します。最新のイメージのURLはWikiを参考にしてください。

$ wget http://www.finnie.org/software/raspberrypi/2015-02-19-ubuntu-trusty.zip
$ unzip 2015-02-19-ubuntu-trusty.zip

imgファイルとbmapファイルの2つのファイルが現れるはずです。bmapファイルはrawのイメージファイルの使用しているブロックを記録したファイルで、ddの代わりにbmaptoolコマンドを利用すると、イメージの書き込み時間を高速化できます。たとえばmicroSDHCカードが/dev/sdbだとすると、次のように実行します。

$ sudo apt install bmap-tools
$ sudo bmaptool copy --bmap 2015-02-19-ubuntu-trusty.bmap 2015-02-19-ubuntu-trusty.img /dev/sdb

もちろん、従来のddでもかまいません。

$ sudo dd if=2015-02-19-ubuntu-trusty.img of=/dev/sdb bs=1M

この時点で第362回の「ルートファイルシステムの作成」まで終わっている状態です。ただし、openssh-serverとavahi-daemonはインストールされていませんので、そのままだとUSBキーボード&HDMIディスプレイがないと操作できません。そこで「ルートファイルシステムの作成」と同様にchrootして上記のパッケージをインストールしておいてください。

ちなみにetc/resolv.confのみ操作が異なります。

362回の方法
$ sudo mount -o bind /etc/resolv.conf etc/resolv.conf
...
$ sudo umount etc/resolv.conf

今回の方法
$ sudo mkdir run/resolvconf
$ sudo mount -o bind /etc/resolv.conf run/resolvconf/resolv.conf
...
$ sudo umount run/resolvconf/resolv.conf
$ sudo rm -rf run/resolvconf

ルートパーティションの拡張

microSDHCカードをRaspberry Pi 2に接続し起動します。今回のイメージのホスト名は「ubuntu」になっているので、⁠ssh ubuntu@ubuntu.local」で接続してください。ユーザー名とパスワードはどちらも「ubuntu」です。

この状態で「df」コマンドを実行すると、microSDHCカードのサイズに関わらずルートパーティションのサイズは約1.8GBになっているはずです。そこでsshログインできたら、最初にルートパーティションをmicroSDHCカードのサイズに合わせて拡張します。

$ sudo fdisk /dev/mmcblk0
(中略)
Command (m for help): d
Partition number (1-4): 2

Command (m for help): n
Partition type:
   p   primary (1 primary, 0 extended, 3 free)
   e   extended
Select (default p): p
Partition number (1-4, default 2): 2
First sector (133120-15556607, default 133120):
Using default value 133120
Last sector, +sectors or +size{K,M,G} (133120-15556607, default 15556607):
Using default value 15556607

Command (m for help): w
The partition table has been altered!

Calling ioctl() to re-read partition table.

WARNING: Re-reading the partition table failed with error 16: Device or resource busy.
The kernel still uses the old table. The new table will be used at
the next reboot or after you run partprobe(8) or kpartx(8)
Syncing disks.
$ reboot

上記ではパーティションテーブルから第2パーティション(=ルートパーティション)の情報を一度削除し、末尾のセクタをデフォルト値(つまり空き領域一杯)に変更しています。この状態で一度再起動してパーティションテーブルを再読み込みさせ、最後にext4ファイルシステムをresize2fsコマンドで拡張します。

$ sudo resize2fs /dev/mmcblk0p2

あとは第362回の「ベースシステムのインストール」と同じようにアップグレードやメタパッケージを導入してください。今回は「Ubuntuサーバーのインストール」までを行ったとして話を進めます。

Dockerのインストール

これでようやくDockerの話に入れます。Dockerについては一般的な方ならおおまかなことはご存知だと思いますので、詳しい説明は飛ばします[2]⁠。真面目に調べたい方は、Software Designの2014年12月号の第1特集には経緯や基礎技術、実例が載っていますので、そちらも参考にすると良いでしょう。

DockerのARM対応

DockerにはDocker ClientとDocker Daemonの2種類の機能が存在します。Docker Clientのほうはマルチプラットフォームなバイナリが公式から提供されていますが、Docker Daemonのほうは今のところ「linux/amd64版」しか正式には対応していません。よってRaspberry Pi 2をDockerのホストとして動かすためには、linux/armhf版のDocker Daemonを入手しなくてはならないのです。

linux/armhf版Docker Dameonを入手する手順はいくつか存在します。

Ubuntuのdocker.ioパッケージを利用する

Ubuntuにはdocker.ioというパッケージ名でDockerが存在します[3]⁠。このためaptから簡単にDockerをインストールできます。さらにUbuntuのリポジトリにあるarmhf版のdocker.ioは、Daemon機能が有効になっているのです。ただしUbuntuパッケージのポリシー上、インストールできるバージョンは古めです。14.04の場合は1.0.1相当となります。

Dockerのビルド済みバイナリを利用する

最新のDockerを使いたい場合は、Docker公式のドキュメントに従って、バイナリをインストールする方法が一番簡単でしょう。ただし前述のように、linux/armhf版は配布していないので、今回の目的には利用できません。

DockerをDockerでビルドする

現在のDockerは、DockerfileでDocker上に開発環境を構築しビルドするシステムになっています。つまりDockerをビルドするためにはそのホストで動くDockerが必要です。ビルド時のDockerは多少古くても大丈夫なので、docker.ioパッケージを使えば良いでしょう。

ただarmhfマシンの上でlinux/armhf用のDockerを構築するとなると、Dockerfileで利用するベースイメージをarmhfに変更したり、ビルドスクリプトで指定しているDocker Daemonのビルドオプションをlinux/armhfでも動くようにしたりと、いろいろと修正が必要になります。

Go言語を使っているので、linux/amd64上でarmhfをクロスコンパイルすれば良いのでは、と思う向きもあるでしょう。実際、Docker Clientはこの方法でビルドできます。ただ、Docker DaemonについてはAppArmorやLXC、LVMをビルドし、cgoを使ってGoのコードからそれらのライブラリを利用しているため、クロスコンパイルは思いのほか大変です。

Dockerを自分でビルドする

Dockerfileでやっていることをホスト上で実行すれば、あらかじめDockerを用意しなくてもDockerをビルドできます。おそらく最初のうちはターゲット上で試行錯誤しながらビルドして、最終的にDockerfileに落とし込んだほうが良いかもしれません。ただしコマンドの実行だけでなく、スクリプトの変数や環境変数の流れも把握しながら実行しなくてはならないため、それなりに大変です。またクロスコンパイルという点では、Dockerでビルドするときと同じ問題が存在します。

今回はバージョンは古いもののこの中では一番簡単な、docker.ioパッケージを利用する方法を使います[4]⁠。

Dockerのインストール

長々と説明してしまいましたが、インストールそのものは簡単です。

$ sudo apt install docker.io

これでDocker Daemonが立ち上がっているはずです。dockerコマンドを一般ユーザー権限で使いたい場合は、そのユーザーをdockerグループに追加しておきましょう。

$ sudo gpasswd -a ${USER} docker

一度ログアウトし再度ログインすれば、dockerコマンド実行時にsudoする必要はなくなります。

$ docker version
Client version: 1.0.1
Client API version: 1.12
Go version (client): go1.2.1
Git commit (client): 990021a
Server version: 1.0.1
Server API version: 1.12
Go version (server): go1.2.1
Git commit (server): 990021a
$ docker info
Containers: 0
Images: 0
Storage Driver: aufs
 Root Dir: /var/lib/docker/aufs
 Dirs: 0
Execution Driver: native-0.2
Kernel Version: 3.18.0-14-rpi2
WARNING: No swap limit support

Dockerの設定

docker infoの「Root Dir」にもあるように、このままでは/var/lib/docker以下にイメージを保存することになります。もともとmicroSDHCはそれほどサイズが大きいわけではなく、しかもあまり頻繁なDisk I/Oは行いたくないところです。そこで容量の大きな外付けUSBドライブを用意し、/etc/default/docker.ioを変更してイメージの保存先を外付けドライブに変更しましょう。

ちなみに外付けディスクのような大きめの電力が必要なデバイスは、Raspberry Pi 2のUSB端子に直接接続するのではなく、ACアダプター付きのセルフパワーUSBハブなどを利用してください。

このドライブがext4でフォーマットされて/mnt/dockerにマウントされているものとします[5]⁠。

$ sudo mkdir /mnt/docker/tmp /mnt/docker/docker

/etc/default/docker.ioを次のように編集してください。

DOCKER_OPTS="-g /mnt/docker/docker"
export TMPDIR="/mnt/docker/tmp"

設定を変更したので、Docker Daemonを再起動します。

$ sudo restart docker.io

docker infoコマンドで、Root Dirが変わっていることを確認してください。

armhfベースイメージを使う

Dockerは設定済みのイメージと、必要な設定やコマンドを記述したDockerfileを用いてコンテナを構築します。設定済みのイメージのうち最も基本となるものが「ベースイメージ」と呼ばれ、Ubuntuでも公式のイメージがDocker Hubで公開されています

Raspberry Pi 2はアーキテクチャがarmhfなので、armhfバイナリで作成したベースイメージが必要です。ベースイメージの作成はそれほど難しくないものの、既に非公式のUbuntu Core/armhfベースのイメージが存在するので、今回はこれを使うことにしましょう。

以下のように、このイメージで実際に動くかどうかを試してみます。

$ docker run --rm -ti mazzolino/armhf-ubuntu bash
# apt-cache policy docker.io
docker.io:
  Installed: (none)
  Candidate: 1.0.1~dfsg1-0ubuntu1~ubuntu0.14.04.1
  Version table:
     1.0.1~dfsg1-0ubuntu1~ubuntu0.14.04.1 0
        500 http://ports.ubuntu.com/ubuntu-ports/ trusty-updates/universe armhf Packages
     0.9.1~dfsg1-2 0
        500 http://ports.ubuntu.com/ubuntu-ports/ trusty/universe armhf Packages
# exit
$ docker images
REPOSITORY               TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
mazzolino/armhf-ubuntu   latest              02330bc3eb88        11 weeks ago        284.5 MB

ホストにインストールしたdocker.ioは、コンテナ上だと未インストール状態になっていることがわかります。またimagesコマンドで、新規にイメージが追加されたこともわかるでしょう[6]⁠。

ownCloudコンテナを作る

最後にこのベースイメージを使ってownCloudコンテナを作成してみましょう。Dokcer HubでownCloudで検索するとたくさんのイメージが見つかります。今回はその中からNginxにも対応しているjchaney/owncloudイメージを使うことにします。

jchaney/owncloudはベースイメージとしてubuntu:14.04を利用していますが、今回はarmhfにする必要があります。そこでイメージのソースコードをクローンし、ローカルにイメージをビルドします。

$ git clone https://github.com/jchaney/owncloud.git
$ cd owncloud
$ sed -i "s,ubuntu:14.04,mazzolino/armhf-ubuntu," Dockerfile
$ sed -i "s,^\(ADD         \)\(extensions.sh\) \(extensions.conf\) \(/var/www/owncloud/apps/\),\1\2 \4\n\1\3 \4," Dockerfile
$ sudo mkdir -p /mnt/docker/owncloud
$ sudo chown ${USER}: /mnt/docker/owncloud
$ docker build -t ubuntu/owncloud .

Dockerfileのapt-get部分は、Ubuntu Coreの状態から必要なパッケージをインストールするため、それなりに時間がかかります。もし途中で失敗するようなら、⁠docker build」の行を再度実行してください。キャッシュを使うのでステップの途中から再開するはずです。

使用するDockerのバージョンによっては、Dockerfileを変更しなくてはいけないかもしれません。たとえば今回使用した1.0.1ではADDに複数のソースファイルを指定できないため、ADDを2行に分ける必要がありました。

最後にこのイメージを起動しましょう。今回はSSLなどは利用せずにシンプルな方法で起動します。

$ docker run -p 80:80 --name nginx -v /mnt/docker/owncloud:/var/www/owncloud/data ubuntu/owncloud

「http://ubuntu.local/owncloud」にアクセスすると図1のように、ownCloudの初期設定画面が表示されるはずです。

図1 ownCloudの初期設定画面
図1 ownCloudの初期設定画面

腕で抱えるにはくじらは大きすぎる

DockerはDaemonがarmhfを正式にはサポートしていないこともあって、本来のDockerほどはお手軽には利用できません。特にベースイメージを変更しなくてはいけないという制約のために、Docker Hubのイメージのほとんどをそのままでは再利用できないという点は、Dockerのメリットの1つを損なってしまっています。

それでも普段使いなれたツールで仮想環境を構築したい方にとって、Dockerは便利なツールであることに違いはありません。Raspberry Pi 2上でのDockerユーザーが増えることで、DockerのARM対応がより一層進むことを期待しています。

ところでSnappy Ubuntu Coreのほうであれば、SnappyのDockerフレームワークは使えるんじゃないの? と思われるかもしれません。残念ながら今のSnappyのDockerフレームワークはamd64しか対応していません。これは「DockerのARM対応」で説明した「Dockerのビルド済みバイナリを利用する」と同じ方法でsnappパッケージを作成しているためです。

おすすめ記事

記事・ニュース一覧