Debian armhfなDocker imageを作ってみた

組込みLinuxを使っていると、ライブラリなどをソースコードからビルドしたいことが度々あるのですが、通常はクロスコンパイルをするか、ターゲットマシンにDebianなどのOSをインストールしてビルドするといった方法を取ります。

しかし、クロスコンパイルする時には大抵クロスコンパイル用のオプションで嵌りますし、ターゲットマシンでビルドするにも遅すぎで、いつも困っていました。

そんな折、QEMUユーザーモードエミュレーションとbinfmtを使うと、x86マシンの上でarmバイナリを動かすことができることを知りました。

KMC Staff Blog:QEMUのもうひとつの使い方: ユーザーモードエミュレーションとbinfmtとchrootの組み合わせ

このテクニックを使うと、chrootした後の環境で動くプログラムは、あたかもARMマシン上で動いているように錯覚します。*1そのため、x86マシンの強力なCPUパワーを使い、arm用のバイナリをビルドすることができるようになります。

しばらくはこの環境のお世話になっていたのですが、自分一人だけならまだしも、他の人に同じ環境を作らせようとすると、手順を教えるのがなかなか大変なです。

Web業界では、アプリケーションを実行するサーバーの環境を同一に保ち、保守をしやすくするために、仮想化やコンテナと呼ばれる技術を使うようです。これを使えばarm用のバイナリをビルドする環境を簡単に構築できるんじゃないか?と思い、試してみました。

Docker

コンテナとは、ホストOS上に*2名前空間を分離した環境を構築する技術です。Linuxのコンテナとしてはlxcなどもありますが、今回はDocker Hubを使って作成したイメージの配布が容易にできるDockerを使ってみます。

Dockerのインストール

Debian では、jessieやsidにはDocker 1.0のパッケージが追加されていますが、wheezyにはまだ入っていないようです。そのため、get.docker.io からパッケージを取得してインストールします。

$ wget https://get.docker.io/ -O - | sh

$ sudo gpasswd -a user docker

 

Docker imageの作成

Dockerでは、Docker imageと呼ばれるもので環境を管理します。DebianUbuntu, Redhat, CentOSなど、大抵のOS用のイメージは最初から用意されているので、通常はそのイメージを使い、自分用の環境を構築します。しかし、さすがにarmhfバイナリを動かすような環境はないでしょうから*3、イメージを自前で作成します。

Docker imageの作成方法は、公式ドキュメントのCreating a Base Imageで解説されています。これを見ると、debootstrapを使ってルートファイルシステムを作り、それをDocker imageに変換するようです。

Docker imageを作成するスクリプトは、dockerの公式リポジトリのcontribディレクトリに含まれています。これを利用して、QEMUユーザーモードエミュレーションとbinfmtを使ってdebootstrapでルートファイルシステムを作り、Docker imageを作成できるようにしてみました。*4

KoyoTakenoshita/docker at debootstrap-emul · GitHub

下記の手順で、Debian wheezy armhfなDocker imageを作成できます。

$ sudo apt-get install qemu-user-static

$ git clone https://github.com/KoyoTakenoshita/docker.git -b debootstrap-emul

$ cd docker/contib

$ sudo ./mkimage.sh -t USER/REPOSITORY debootstrap-emul --arch=armhf --interpreter-path=/usr/bin/qemu-arm-static wheezy

 作成したDocker imageは、Docker Hubに上げてあります。

kytknst/debian-wheezy-armhf Repository | Docker Hub Registry - Repositories of Docker Images

動かしてみる

実際に作成したDocker imageを動かしてみると、下記のようになり、armアーキテクチャと錯覚していることが分かります。

$ docker pull kytknst/debian-wheezy-armhf

$ docker run -i -t kytknst/debian-wheezy-armhf uname -a
Linux ed64794e1dba 3.2.0-4-amd64 #1 SMP Debian 3.2.57-3+deb7u2 armv7l GNU/Linux

 

 

*1:例えば、uname -m に対しては "armv7l" が返ってきます。

*2:仮想化とは異なるものなので、"ホストOSの中に"と書いた方が直観的かも?

*3:Docker Hubを探したらあるかもしれませんが。。

*4:ルートファイルシステムを作るスクリプトKranz Kornerさんのブログ記事を参考にしました。

Debianでtestingやunstableからパッケージを借りる時の手順

普段はDebian stableを使っているけど、たまにtestingやunstableにしか入っていないパッケージを使いたくなる時がある。そんな時の対応方法*1

1. /etc/apt/apt.conf.d/99target を(普通はないので)作成

下記行を追加

APT::Default-Release "stable"; 

2. /etc/apt/source.list 

     下記行を追加

deb http://ftp.jp.debian.org/debian testing main contrib non-free
deb-src http://ftp.jp.debian.org/debian testing main contrib non-free 

unstableのパッケージをインストールしたい場合は、testingの部分を置き換える。

3. apt-get update

   ※この時点でapt-get upgradeしても、testingやunstableのパッケージは入らない。

4. パッケージのインストール

$ sudo apt-get install (パッケージ名)/testing
$ sudo apt-get install (パッケージ名)/jessie

でtesting(jessie)のパッケージをインストールできる。

※ apt-get sourceでも/testingの指定は使える。

より詳しくはこちら。
D: AptGet - Debian GNU/Linux スレッドテンプレ
http://debian.fam.cx/index.php?AptGet#f2d52afb

 

*1:一時的に/etc/apt/source.listを書き換えるという面倒な方法をしていたら、会社の先輩にもっといいやり方があると教えてもらった。

Cloud9 IDEをローカルマシンにインストールする

Cloud9 IDE は、Webブラウザで動作するIDE。開発元がWebサービスとして提供しているが、IDE自体のソースを公開しているので、自前でサービスを立てることもできる。

いつものごとく、Debian 7 Wheezyにインストールしてみる。基本的には、READMEの記述に従うだけ。

Node.jsのインストール

事前に、Node.jsとnpmをインストールしておく必要がある。過去の記事を参照。

依存するパッケージのインストール

$ sudo apt-get install libxml2-dev

Cloud9 IDEのインストール

$ git clone https://github.com/ajaxorg/cloud9.git

$ cd cloud9

$ npm install

※ git cloneの後に、git submodule update --init --recursive しておく必要があるかもしれない。

Mercurialのインストール

Mercurial(hg)をインストールしていない環境で実行すると、ワークスペースを開くときに エラーで落ちてしまうので、インストールしておく。参考: cloud9 + node 0.10 · Issue #2741 · ajaxorg/cloud9 · GitHub

$ sudo apt-get install mercurial

サービスの起動

$ mkdir ~/workspace/hoge

$ bin/cloud9.sh local -l 0.0.0.0 -w ~/workspace/hoge

おまけ: C/C++コンパイル

個人的に、Cloud 9 IDEはNode.jsやRubyなどのプログラムを作るのが主流らしいが、個人的にはC/C++言語で書いたプログラムをコンパイルしたい。そういった場合でも、特に困ることもなく、普通にC言語でソースを書き、IDE下部の Command Line で

gcc hoge.c

とかしてやれば良い。

 

デバッグするには、どうしたら良いのかな?

 

Debian wheezyでNode.jsをdebパッケージからインストール

Debian 7 WheezyにはNode.jsのパッケージは含まれていなかったのだけれど、backportsにnodejsパッケージが追加された。そのため、ソースからビルドしなくても、apt-get installコマンドで簡単にNode.jsをインストールできるようになった。

インストール

/etc/apt/sources.list.d/backports.list あたりに、下記を追記しておく。

deb http://ftp.jp.debian.org/debian/ wheezy-backports main contrib non-free
deb-src http://ftp.jp.debian.org/debian/ wheezy-backports main contrib non-free

nodejsパッケージと、nodejs-legacyパッケージ*1をインストール。

$ sudo apt-get update

$ sudo apt-get install nodejs nodejs-legacy

nodeパッケージにはnpmは含まれていないので、下記コマンドでインストールしておく。

# curl -L https://npmjs.org/install.sh | sh 

 

*1:nodejsパッケージに含まれるNode.jsの実行ファイルは /usr/bin/nodejs という名前になっている。npmなど通常のNode.js関連プログラムは、実行ファイルの名前として"node"を期待するので、都合が悪い。しかし、Debianには既にnodeという名前を使う別のパッケージが存在するので、名前がコンフリクトする。仕方がないので、Debianではnodejs-legacyパッケージを作って、nodejsからnodeにシンボリックリンクを張るようにしているらしい。

ARMマシンでNode.js

Node.jsを使ってのアプリの書き方に少し慣れてきたので、最終的なターゲットであるARMマシンでも動かしてみようかと思ったら、少しハマッた。

nvmを使ってインストール

x86マシンと同様に、単純に nvm installすればインストールはされる。一応、安定版と呼ばれているv0.10.xの最新版を入れることにする。

# nvm install v0.10.16

"バイナリが無いからソースからインストールするよ"的なメッセージが表示され、Node.js自体のコンパイルが始まる。

 

コンパイル自身は特に問題なく終了し、nodeコマンドがインストールされるが、実行しようとすると、Segmentation fault で落ちてしまう。どうも、v0.10.xはARMマシンではうまく動かないらしい。ちなみに、試した環境は Debian wheezy armhf 環境。

pcDuino Segmentation fault · Issue #4946 · joyent/node · GitHub

↑のリンク先では、v.0.11.2-preだと動くよと書いてある気がするので、v0.11.xの最新版v0.11.6で試してみたら、うまく動いてくれた。

ちょっと気になったRaspberry Pi対応

nvmでは、Raspberry Pi用のバイナリが用意されているらしく、Ras Piの場合はそれをインストールしようとする。ただ、その判定処理がちょっとイケてない。

 

~/nvm/nvm.shの一部

  # Try to figure out the os and arch for binary fetching

  local uname="$(uname -a)"

  local os=

  local arch="$(uname -m)"

  case "$uname" in

    Linux\ *) os=linux ;;

    Darwin\ *) os=darwin ;;

    SunOS\ *) os=sunos ;;

    FreeBSD\ *) os=freebsd ;;

  esac

  case "$uname" in

    *x86_64*) arch=x64 ;;

    *i*86*) arch=x86 ;;

    *armv6l*) arch=arm-pi ;;

  esac

 

この処理だと、ARM11等armv6アーキテクチャのCPUを積んでるボードはRas Piだと誤認識されるんじゃないかな。せめて、uname -nで判別すればいいのに。と思ったが、v0.10.xやv0.11.xはそもそもRas Pi用のバイナリが用意されてないようなので、結局ソースから入れることになるので、あんまり関係ないなと思ったり。