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でビルドするときと同じ問題が存在します。
[3] パッケージ名「docker」はKDE/GNOMEのシステムトレイとして使うドックアプリ で使われていたため、このような名前になっています。以前は実行バイナリ名も/usr/bin/docker.ioだったのですが、現在はシステムトレイのほうはwmdockerに変更し 、docker.ioパッケージが/usr/bin/dockerになっています。これはUbuntu 14.04にもリリース後に反映されました。
今回はバージョンは古いもののこの中では一番簡単な、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] 。
[5] DockerはAUFSが利用可能であればaufsを、そうでなければdevicemapperをStorage Driver として利用するため、ここではext4にしています。Storage Driverとしてbtrfsを利用したい場合は、そちらを指定してください。ちなみにSnappyのカーネルはAUFSパッチが当たっておらず、さらにカーネルのCONFIG_DM_THIN_PROVISIONINGも無効化されているため、btrfsでしか動作しません。
$ 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の初期設定画面
腕で抱えるにはくじらは大きすぎる
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パッケージを作成しているためです。