Raspbian イメージの改造
Raspberry Pi の OS といえば、Raspbian が有名です。Raspberry Pi をサーバとして運用するなら、無駄なデスクっトプ環境のない「RASPBIAN STRETCH LITE」がいい感じです。
Raspbian はデフォルトだと ssh リモートログインができません。ssh を有効化するには、イメージ書き込み後に一旦マウントして、boot パーティションに ssh
という空ファイル作る必要があります。でも、面倒です。ラズパイをサーバとして使うなら、ssh をデフォで有効化したオレオレイメージを作りたい!そこで、Raspbian イメージの改造をやってみたのですが、意外とハマりポイントがあったので、覚書を残しておこうと思います。
ちなみに、作業環境は Linux を想定しています。
TL;DR
2017-11-29-raspbian-stretch-lite.img
の boot パーティションをマウント:
# losetup /dev/loop0 2017-11-29-raspbian-stretch-lite.img # kpartx -a /dev/loop0 # mkdir /mnt/raspbian-boot # mount /dev/mapper/loop0p1 /mnt/raspbian-boot
ssh ログインの有効化:
# touch /mnt/raspbian-boot/ssh
アンマウント:
# umount /mnt/raspbian-boot # kpartx -d /dev/loop0 # losetup -d /dev/loop0 # rmdir /mnt/raspbian-boot
改造済みの .img を MicroSD に書き込む:
# dd if=2017-11-29-raspbian-stretch-lite.img of=/dev/sdx status=progress
予備知識:ISO ファイルのマウント
OS(ディストリビューション)を入れるときはインストーラの ISO ファイルを落としてきて、CD とか USB に dd
コマンドで焼きますよね。例えば、hogehoge-linux.iso
を /dev/sdx
に焼くときはこんな感じ:
# dd if=hogehoge-linux.iso of=/dev/sdx status=progress 1775919616 bytes (1.8 GB, 1.7 GiB) copied, 10.0328 s, 177 MB/s 3629056+0 レコード入力 3629056+0 レコード出力 1858076672 bytes (1.9 GB, 1.7 GiB) copied, 10.9957 s, 169 MB/s
昔は、dd
コマンドは進捗表示してくれなかったのに、最近は便利になりましたね。
当然、/dev/sdx
は適切なファイルシステムを指定すると、マウントすることができます。
# mkdir /mnt/hogehoge-linux # マウント先ディレクトリを作る # mount -t vfat /dev/sdx1 /mnt/hogehoge-linux # FAT ファイルシステムとしてマウント # ls /mnt/hogehoge-linux/ COPYING.linux bcm2708-rpi-b-plus.dtb bcm2710-rpi-3-b.dtb config.txt ... # umount /mnt/hogehoge-linux # アンマウント
イメージの書き込みに使った dd
コマンドは単純にバイト列をファイルからデバイスに転送しているだけです。なので、ISO ファイル自体を直接マウントすることができます。
# mount -t iso9660 hogehoge-linux.iso /mnt/hogehoge-linux # ls /mnt/hogehoge-linux/ COPYING.linux bcm2708-rpi-b-plus.dtb bcm2710-rpi-3-b.dtb config.txt ...
この状態で /mnt/hogehoge-linux/
以下のファイルを編集すると、hogehoge-linux.iso
も同じように変更されます。これで、OS イメージを改造できます。
IMG ファイルのマウント
IMG は ISO ファイルのようにはマウントできない
Raspbian のイメージは上記の方法では巧くいきません。
# mkdir /mnt/raspbian # mount -t iso9660 /usr/local/src/2017-11-29-raspbian-stretch-lite.img /mnt/raspbian mount: wrong fs type, bad option, bad superblock on /dev/loop0, missing codepage or helper program, or other error In some cases useful info is found in syslog - try dmesg | tail or so.
ちなみに、 /dev/loop0
はループ・デバイスと言って、通常のファイルをブロック・デバイスように操作するためのものです。
イメージの中身をチラ見してみます。
# file /usr/local/src/2017-11-29-raspbian-stretch-lite.img /usr/local/src/2017-11-29-raspbian-stretch-lite.img: DOS/MBR boot sector; partition 1 : ID=0xc, start-CHS (0x0,130,3), end-CHS (0x5,204,60), startsector 8192, 85045 sectors; partition 2 : ID=0x83, start-CHS (0x5,220,24), end-CHS (0xe1,229,4), startsector 94208, 3534848 sectors
複数のパーティションが含まれているみたいです。partition 1 (ID=0xc)
がブート用パーティションで、partition 2 (ID=0x83)
がルートのパーティションでしょう。
どうも、複数パーティションを含むイメージファイル(.iso ではなく .img)の場合は、losetup, kpartx というコマンドでパーティションをバラして、マウントする必要があるみたいです。同じようなことをやっている人がいましたw:Xenのイメージファイルをマウントする
作業としては、
という流れになります。
1. ループ・デバイスを設定
/dev/loop0
にイメージを設定します。これで、/dev/loop0
を通じてパーティションが見えるようになります。
# losetup /dev/loop0 /usr/local/src/2017-11-29-raspbian-stretch-lite.img # losetup -a /dev/loop0: [2052]:4340343 (/usr/local/src/2017-11-29-raspbian-stretch-lite.img)
ちゃんと見えてるよ、ほら!
# kpartx -l /dev/loop0 loop0p1 : 0 85045 /dev/loop0 8192 loop0p2 : 0 3534848 /dev/loop0 94208
2. パーティション・マッピングを作る
# kpartx -a /dev/loop0 # ls /dev/mapper/ control loop0p1 loop0p2
/dev/mapper/
以下に loop0p1
、loop0p2
というデバイス・ファイルが追加されます。
3. mount する
/dev/mapper/
以下のファイルを普通にマウントします。
# mkdir /mnt/raspbian1 /mnt/raspbian2 # mount /dev/mapper/loop0p1 /mnt/raspbian1 # mount /dev/mapper/loop0p2 /mnt/raspbian2
これで、boot と rootfs の中身が見えるようになりました。
boot:
# ls /mnt/raspbian1/ COPYING.linux bcm2708-rpi-b-plus.dtb bcm2710-rpi-3-b.dtb config.txt fixup_x.dat overlays start_x.elf LICENCE.broadcom bcm2708-rpi-b.dtb bcm2710-rpi-cm3.dtb fixup.dat issue.txt start.elf LICENSE.oracle bcm2708-rpi-cm.dtb bootcode.bin fixup_cd.dat kernel.img start_cd.elf bcm2708-rpi-0-w.dtb bcm2709-rpi-2-b.dtb cmdline.txt fixup_db.dat kernel7.img start_db.elf
rootfs:
# ls /mnt/raspbian2/ bin boot dev etc home lib lost+found media mnt opt proc root run sbin srv sys tmp usr var
イメージの編集
Raspberry Pi 起動時に ssh ログインを有効化するには、boot に ssh
という名前の空ファイルを作るだけです。
# touch /mnt/raspbian1/ssh
他にも色々できるはずですが、それは、また今度にしましょう。
アンマウント
やることはやったので、後はお片付けの時間です。
# umount /mnt/raspbian1 /mnt/raspbian2 # kpartx -d /dev/loop0 # losetup -d /dev/loop0 # rmdir /mnt/raspbian1 /mnt/raspbian2
はい、これで元通りです。
ssh ログインの確認
編集が終わったイメージを MicroSD に書き込みます。
# dd if=/usr/local/src/2017-11-29-raspbian-stretch-lite.img of=/dev/sdx status=progress
この MicroSD をラズパイに入れて起動し、ssh でログインできれば成功です。デフォルトのパスワードは raspberry
です。
$ ssh pi@192.168.0.42 pi@192.168.0.4's password: Linux raspberrypi 4.9.59-v7+ #1047 SMP Sun Oct 29 12:19:23 GMT 2017 armv7l The programs included with the Debian GNU/Linux system are free software; the exact distribution terms for each program are described in the individual files in /usr/share/doc/*/copyright. Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent permitted by applicable law. Last login: Sat Jan 20 10:37:59 2018 from 192.168.0.5 SSH is enabled and the default password for the 'pi' user has not been changed. This is a security risk - please login as the 'pi' user and type 'passwd' to set a new password. pi@raspberrypi:~ $
最後に
多分、ssh 鍵ファイルを突っ込んだり、初歩的な設定を事前にやっておく程度のことは、できるんじゃないでしょうか。ただし、apt/deb パッケージを入れたり、コンパイル済みのファイルを事前にインストールとかやるときは、バイナリ互換性を考えないと…。そういうことは、そもそも構成管理ツールでやるべきですかね。
OCaml でデータ分析 ― チュートリアル編
関数型プログラミング言語 OCaml を Jupyter 上で動かすため、OCaml Jupyter kernel を開発しています。 仕事でも、OCaml+Jupyter 環境でデータ分析を行っており、それなりに実用的だと思います。 ML数値計算勢の方々と情報共有し、同志を増やしていければ良いな、と思って、この記事を書いています。
OCaml でデータ分析 ― 紹介編(第2回ML勉強会)
OCaml でデータ分析する話については、第2回ML勉強会で発表した資料があるので、こちらをご覧いただければ、大体わかると思います。
要約: Jupyter という、対話環境に画像の埋め込みや Markdown (LaTeX) でのコメント機能などを追加したような超便利なソフトウェアがあります。 データ分析作業(解析の試行錯誤、その可視化や保存・管理)にとても便利で、おそらく、データ分析系のお仕事の人は皆知っているのではないでしょうか? Jupyter の対話環境の実行部分は「カーネル」と呼ばれており、一番メジャーなのは Python を動かす IPython カーネルです。 カーネルと Jupyter の通信プロトコルは公開されているので、自分の好きな言語のカーネルを実装できます。 OCaml には IOCaml というカーネルがすでに存在しますが、
- PPX(OCaml のプリプロセッサ機能)が使えない
- 補完機能が不完全(ロードしたライブラリに含まれる識別子が候補に出ない、など)
- 外部コマンド呼び出しで IOCaml がフリーズすることがある(原因不明)
など、いろいろ問題があるのですが、事実上メンテナンスされてないため、PR も無視され、何も改善できない状態でした。
仕方がないので、自分でカーネルをスクラッチで作り直すことをML勉強会で宣言しました。 今日はその続きです。
OCaml Jupyter の紹介
IOCaml をフルスクラッチで再実装する件は完了しており、OCaml Jupyterという名前でリリースしています。 IOCaml に実装されている機能は OCaml Jupyter でも(たぶん)全て対応済みなので、乗り換えない理由は無いでしょう。 PPX もサポートされていますし、merlinによる補完機能が実装されています。 他にも、細々としたバグ修正や機能追加があり、IOCaml で致命的だった不具合や機能不足は概ね解消したと感じています。
インストール
Jupyter 自体は Python で書かれています。 Python のパッケージマネージャは Anaconda や pip など幾つかあるのですが、ここでは、pip の方法を紹介します。
$ pip install jupyter
これだけ。簡単でしょ? Python は v2 系と v3 系で言語仕様がだいぶ違うのですが、気にしなくて結構です。 Jupyter はどちらでもインストールできますし、OCaml Jupyter は Python 非依存なので、どちらでも大丈夫です。
OCaml Jupyter カーネルはOPAM(OCamlのパッケージマネージャ)に登録されています。 OPAM をまだ持っていない人は、 How to install OPAM に従ってインストールしてください。 カーネル自体は
$ opam install jupyter $ jupyter kernelspec install --name ocaml-jupyter "$(opam config var share)/jupyter"
でインストールできます。以下のコマンドで、Jupyter notebook が起動します。
$ jupyter notebook
勝手にブラウザが開いて、こんな感じの画面が立ち上がります。
Docker イメージを使う
「正直、インスール面倒」とか思っている貴方、朗報です! 数値計算系のパッケージをありったけ詰め込んだ Docker イメージ akabe/ocaml-jupyter-datascience (GitHub) を公開しています。 圧縮済みの Docker イメージでも 1GB となかなかエグいデータ量ですが、Python 版データ分析用イメージ jupyter/datascience-notebook は 2GB なので、まだ軽い方でしょう。
コマンドを叩いて、しばらく待つと、コンソールに URL が表示されるので、これをブラウザに貼り付けてください。 さっきの画像のような画面が立ち上がるはずです。
$ docker run -it -p 8888:8888 akabe/ocaml-jupyter-datascience [I 15:38:04.170 NotebookApp] Writing notebook server cookie secret to /home/opam/.local/share/jupyter/runtime/notebook_cookie_secret [W 15:38:04.190 NotebookApp] WARNING: The notebook server is listening on all IP addresses and not using encryption. This is not recommended. [I 15:38:04.197 NotebookApp] Serving notebooks from local directory: /notebooks [I 15:38:04.197 NotebookApp] 0 active kernels [I 15:38:04.197 NotebookApp] The Jupyter Notebook is running at: http://[all ip addresses on your system]:8888/?token=4df0fee0719115f474c8dd9f9281abed28db140d25f933e9 [I 15:38:04.197 NotebookApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation). [W 15:38:04.198 NotebookApp] No web browser found: could not locate runnable browser. [C 15:38:04.198 NotebookApp] Copy/paste this URL into your browser when you connect for the first time, to login with a token: http://localhost:8888/?token=4df0fee0719115f474c8dd9f9281abed28db140d25f933e9
ちなみに、この Docker image は weekly で自動的にリビルドされ、DockerHub にアップロードされるようになっています。 なので、自動的にパッケージや辞書等のデータセットが更新されています。
個人的には、Docker イメージの方がオススメです。何故かというと、
- OCaml 界では、小さい数計算系ライブラリが散乱しています。手動でインストールすると、複数のマシンに同じ環境を整えるのはかなりの手間になります。
- 他人に分析結果を共有するだけなら、notebook ファイル (.ipynb) を GitHub とかにアップすれば十分です。しかし、実験の再現や条件を変えて再施行などを行う場合、環境を合わせておかないとバグったりします。ローカルで環境構築しちゃうと、「他人の環境で動かない!」なんてことになりがちです。*1
どちらの問題も、Docker なら Dockerfile を渡せば良いので楽に解決できます。 実際、仕事でもこの Docker image に社内 API サーバのクライアントライブラリ等を追加してカスタマイズしたものを使用しています。
チュートリアル
これ以降は、色々なパッケージを扱うので、Docker 環境を前提とします。ローカルで環境構築しても README.md に書いてあるパッケージをインストールすれば、動くはずです。 基本的には、OCaml の対話環境と同じなので、Jupyter 特有の部分に焦点を当てつつ、色々な処理をやらせてみようと思います。
https://github.com/akabe/docker-ocaml-jupyter-datascience/tree/master/notebooks に色々な例を上げています。Jupyter 上で見たい人は
$ git clone https://github.com/akabe/docker-ocaml-jupyter-datascience $ cd docker-ocaml-jupyter-datascience/notebooks $ docker run -it -p 8888:8888 -v $PWD:/notebooks akabe/ocaml-jupyter-datascience
とすると、色々な notebook を実行できて楽しいと思います。
この例の中では、 formant_estimation_by_AR.ipynb が一番面白く、かつ実用的なノウハウが詰め込まれています。 データのダウンロード・読み込み・前処理・分析・可視化の全工程とその解説を1ファイルで簡潔にまとめています。 ただ、数学的なところを説明するのが面倒なので、今回は、簡単な数値計算と可視化だけやってみます。 要望が多ければ、解説するので、Twitter で @ackey_65535 に声をかけてください。
1. ノートブックの作成
コンソールに表示された URL をブラウザに貼り付け、画面右上の、New
というボタンを押すと、OCaml と Python が選択できるようになっているはずです(バージョンは人によって違うかもしれません)。
ここで、OCaml をクリックします。
間違っても Python は押しちゃダメだ!そっちには静的型検査のない暗闇が待っている…!
OCaml の世界に正しく入り込めた皆さんは、下のような画面を目にすることでしょう。
Logout
ボタンの下には OCaml と表示されていますね?
2. 対話環境を使ってみる
まずは、OCaml 対話環境としての機能を試してみます。 In [ ]
の右側のテキストボックスに適当な OCaml コードを貼り付けて、Shift+Enter を押してみてください。
こんな感じで実行結果が表示されます。ちなみに、この OCaml コードを書いたテキストボックスを「セル」と呼びます。
もう少し、数値計算っぽいことをやってみましょう。 皆さん、ランダムウォークはご存知でしょうか? まず、0 からスタートして、前回の値に +1 するか -1 するかを確率 1/2 で決めながら、時系列データを作っていきます。 なんだか酔っぱらいが歩いていてるみたいですね。 OCaml で書くとこんな感じ。
ちゃんと動きましたか?
でも、これだけだと、シンタックスハイライトが効くだけの単なる対話環境って感じですよね。 Jupyter の真髄はここからです。
3. グラフの描画
データを作ると、可視化したくなりますよね?そんな時のために、jupyter-archimedes
という簡単な2次元チャートライブラリが用意されています。
このライブラリは Archimedes という OCaml 製のチャートライブラリを Jupyter 上で使えるようにしたものです。
早速、これで先程のランダムウォーク系列を可視化してみましょう。
素晴らしいことに、画像が notebook に埋め込まれて表示されます。 これは、コンソールの対話環境では真似できない機能ですね。 一気に、データ分析してる感が出てきました!画像表示しただけなのに!分析してないのに!テンション上がります!
4. Markdown で説明を書く
セルを選択(セル周囲が枠線で囲まれた状態)で、画面上部のドロップダウンからセルの種類を Code から Markdown に切り替えます。
Markdown cell では、こんな感じで Markdown のシンタックスハイライトが効くようになります(MathJax の数式も使えます)。
入力が終わって、Shift+Enter を押すと、Markdown が HTML として表示されるようになります。
Markdown cell はダブルクリックすると、再度編集可能になります。
Markdown cell も、コンソールの対話環境にはない機能です。 データ分析系の作業をやっていると、コメントに表や数式を入れたくなりますが、この Markdown cell はその要望に答えられる機能です。 個人的には、
- ノートブックを他人と共有するときは、Markdown の説明をメインにして、コードを併記する書籍スタイルにする
- 分析結果だけほしい時(ノートブックを共有しない場合)は、コードをメインに Markdown をコメント代わりにする
という感じで書いています。
5. セルの移動
選択中のセルを移動させることもできます。上のメニューの矢印ボタンを押してみてください。
他にも、削除・挿入・コピーなどができるので、ノートブック全体を見やすくいい感じに成形できます。
6. ノートブックの保存
最終的に、いい感じにまとまったら、Ctrl+S でノートブックを保存します。画面上にノートブックのファイル名が表示されています。
ここをダブルクリックすると、ファイル名を編集できます。
画面左上の OCaml ロゴをクリックすると、ファイル一覧に戻ります。
さっき作ったノートブックが「Running」と表示されています。これは、「バックグラウンドで OCaml Jupyter カーネルが起動中だよ」という意味です。 左のチェックボックスにチェックを入れて、カーネルをシャットダウンすることができますが、対話環境で保持していた変数の値などは消えてしまいます。
しかし、コードと実行結果(対話環境のレスポンスや埋め込まれた画像)や Markdown の内容は保存されているので、消えません。 カーネルや Jupyter サーバ自体を停止させても、保存済みの実行結果を閲覧したり、コードの再実行ができます。 対話環境の結果やコードを整理・保存して、後から読み返せるのはなかなか便利です。
このノートブックは .ipynb
という拡張子ですが中身は JSON、つまりただのテキストファイルなので、git などで管理できます。
ノートブックを編集・再実行すると無駄に差分が大きくなるので、merge とかはやめたほうが良いですが、他人と共有しやすいのは良いところです。
しかも、GitHub は Jupyter notebook を人間可読な形で表示してくれます。
業務で GitHub を使っているなら、「分析したで、URL これ!」って言って渡すだけで誰でも結果を見られます。超便利です。
まとめ
すごく基礎的な所だけ、Jupyter notebook の使い方を説明しました。 急いで書いたので、読みにくいところがあるかもしれません。 もっと色々い知りたいことがあったら、Twitter @ackey_65535 で声をかけてください。 バグとか見つけたら、GitHub Issues に報告していただけると嬉しいです(PR はもっと助かります)。