AnsibleをAndroid端末で動作させる

# はじめに

電気代が高騰している昨今、今まではデスクトップ端末で行っていたようなことも、より省電力な端末によってリプレースしたいという欲求が私の中で高まっている。
そこでAndroid端末でのAnsible実行を試してみた。
[もうすでにやっている人がいる](https://twitter.com/_badshark_/status/493837039499247616)のを見て、私もやってみたかったという話でもある。

Android上でLinuxディストリビューションを動作させる方法はいくつかあるが、今回は[GNURoot](https://play.google.com/store/apps/details?id=champion.gnuroot)を使用した。
私の知る限り他の方法を使うにはrootedな端末でないとならないが、GNURootはrootedでなくても動作するのが特徴である。
なのでAndroidで動く家電辺りが将来増えてきたら、そこで比較的簡単に実行できたり、もするかもしれない。

ハードウェアはNexus 7 (2013)を使用した。
Androidのバージョンは4.4.4(KTU84P)および5.0(LRX21P)で検証し、どちらも[ファクトリーイメージからの入れ直し](https://developers.google.com/android/nexus/images)を行った「とてもきれいな状態」である。つまり比較的環境依存性が小さい状態であるという注意書きが付くが、特に問題なく今回の検証は上手くいった。
しかし後述するが、他の端末では上手くいかなかったものもあった。

GNURootが対応するCPUアーキテクチャはARMのみでx86やMIPSには対応していないと書かれている。
また、ARMはARMv6またはARMv7を指すと思われ、ARMv8の端末上で動作するかどうかは該当端末を所持していないので不明である。
対応するAndroidのバージョンは1.6以上となっているので、この条件に引っかかることはまずないだろう。

**2014-12-03追記**: Android 5.0.1(LRX22C)上でも試してみたが、特筆すべき変化なく実行できた。

# Ansibleを動かすまで

## GNURootの起動

GNURootをインストールしたら、以下の手順で起動させる。

* 「Select Rootfs to Create」で"Wheezy"を選択
* 「Create New Rootfs」をクリックするとGoogle Playの[GNURoot Wheezy](https://play.google.com/store/apps/details?id=champion.gnuroot.wheezy)に飛ばされるのでインストールする
* もう一度「Create New Rootfs」で解凍が始まる
* 「Select Rootfs to Launch」に"Wheezy"が現れたら「Launch as Fake Root」にチェックを入れて「Launch Rootfs」をクリック

これでWheezy(Debian 7)が利用できるコンソールウィンドウが開くはずである。

しかし私はGL07SというAndroidスマートフォンを持っているのだが、同じことをするとコンソールウィンドウが一瞬開くもののすぐ閉じてしまって何もできない。
すぐ閉じてしまうので理由はよくわからない。「きれいな状態」ではないせいか、性能が足りないのか、SoCがHiSilicon製であるせいか、はたまた。

## SSHサーバを立てる

（少なくとも私は）Androidのキーボードで文字入力するのは非常に苦痛なのでSSHサーバを立ててPCから操作することにする。

しかしどうもopenssh-serverは動いてくれないので、別のSSHサーバであるDropbear SSHを使用する。

外部からログインできるようにrootユーザのパスワードを設定しておく。
望むならここで公開鍵を置いて公開鍵認証にしても良い。置き場所は通常通り/root/.ssh/authorized_keysである。
なお、root以外のユーザを作成してもどうやらrootユーザと同じ権限で動いているようで、作る意味はなさそうだった。

```
# passwd
```

初期状態では/usr/binと/binにしかPATHは通っていない。このままだとapt-getさえも良く失敗するので必要そうなところはPATHに追加しておく。

```
# export PATH=$PATH:/sbin:/usr/sbin:/usr/local/sbin
```

とりあえずインストールされているパッケージを最新に。

```
# apt-get update
# apt-get upgrade
```

dropbearをインストール。

```
# apt-get install dropbear
```

ただしこれはインストールはできるもののエラーになる。SSHの標準ポートである22は利用できないにもかかわらずインストール時に起動しようとするからだ。調べていないがこれはAndroidというOSによる制限、というところだろう。
そこで設定ファイルである /etc/default/dropbear のDROPBEAR_PORTの値を変更し1024より大きくしてから起動する。なおsedで修正するのはちょっと長いコマンドを入力しないとならないから私はやだ。

```
# vi /etc/default/dropbear
(DROPBEAR_PORTの値を変更する)
# /etc/init.d/dropbear start
```

端末のIPアドレスを調べる。

```
# ip r
```

ちなみに、コンソールウィンドウを閉じてもSSHサーバは落ちるわけではないので（※）、不要になったら「/etc/init.d/dropbear stop」を実行する。外出時に端末を持ち歩いている最中、非常に簡単なパスワード認証にしてあったためにログインされて中身を覗かれた、なんてことがないように。

（※） それどころかGNURootの「Delete Rootfs」で削除してさえ端末を再起動するまで落ちていない。認証ができないのでログインは失敗するが、もう一度「Create New Rootfs」してrootパスワードを設定するなど認証できるようにすればdropbearを再度インストールしなくてもSSHログイン可能な状態になっている。

## Ansibleのインストール

SSHサーバが利用できるようになったので、ここからはPCからログインして操作する。

早速Ansibleをインストール、と行きたいところだが、Debian 7の標準リポジトリにはAnsibleのパッケージは存在しない。
これはGNURootで作成した環境に限った話ではなく、Debian 7ならばどの標準リポジトリにもない。
ならば非標準のリポジトリを、というわけで、Jessie(Debian 8)にはAnsibleのパッケージが存在し、Debian BackportsプロジェクトによってDebian 7用のパッケージが作られているのでそのリポジトリを追加してやる。2014-12-02現在、インストールされるAnsibleのバージョンは1.7.2である。
pipでインストール、などの方法もあるだろうが、多少面倒なのでここでは扱わない。ビルドが始まったりすると結構時間が掛かるとだけ書いておく。

```
# echo "deb http://ftp.jp.debian.org/debian/ wheezy-backports main contrib non-free" >> /etc/apt/sources.list
```

リポジトリを追加したのでもう一度apt-get updateしてからAnsibleをインストールする。必要になるだろうopenssh-clientもついでにインストールしておく。
これでAnsibleが普通に使えるようになる。

```
# export PATH=$PATH:/sbin:/usr/sbin:/usr/local/sbin
# apt-get update
# apt-get install ansible openssh-client
```

ただし、-kオプション付きでansibleを実行するならsshpassが必要になるなど、状況によって他にも必要なものもあるだろう。

## ここまでをスクリプト化

「GNURootの起動」の部分はスクリプト化できないので書いた通り実行するしかないが、それより後をまとめると以下のようなスクリプトファイルを/sdcardの下のどこかにコピーして実行すれば良いだろう。
ただしNexus 7 (2013)では/sdcardの下のファイルに実行権限は付けられなかったので「sh /sdcard/xxx.sh」のように実行する。

```
#!/bin/sh

PATH=/usr/bin:/bin:/sbin:/usr/sbin:/usr/local/sbin
passwd
echo "deb http://ftp.jp.debian.org/debian/ wheezy-backports main contrib non-free" >> /etc/apt/sources.list
echo "export PATH=/usr/bin:/bin:/sbin:/usr/sbin:/usr/local/sbin" >> /root/.bashrc
apt-get update
apt-get -y upgrade
apt-get install -y dropbear openssh-client ansible
ansible localhost -m lineinfile -a "dest=/etc/default/dropbear regexp='^DROPBEAR_PORT=[0-9]+$' line='DROPBEAR_PORT=2222'"
/etc/init.d/dropbear start
ip r
```

# パフォーマンスは？

他のコンピュータに対してansible -m pingを測定してみたが、実行完了まで6秒程度（何度か実行してみて、1回目の結果は無視して2回目以降の結果の平均値で判断している。以下同じ。またAndroid 4.4.4でも5.0でも結果はほぼ同じ）。
localhost（「ansible localhost -m ping」を実行しているので暗黙的にansible_connection=local。以下同じ）に対しても5秒弱だったので、ネットワーク速度などが大きな問題となっているわけではなく、まず立ち上がりに時間が掛かっているようだ。

比較のため、RaspbianがインストールされたRaspberry Pi Type Bでも同じことをやってみた。
他のコンピュータに対しては3.5秒程度、localhostに対しては4秒程度（なぜかlocalhostの方が時間が掛かる）。

普段使いのPCのVirtualBoxに1コア、1GBでHDD内に立てた仮想マシンだとlocalhostに0.2秒程度（上記「他のコンピュータ」は同じVirtualBoxの別仮想マシンなので、ここに対してのpingはイコールコンディションにならないので測定対象外としている）。

というわけでAndroid上でやると遅い。
遅いが耐えられないほどかと言うと、そうでもないとは思っているけれど。

# おまけ

逆にAndroid端末をAnsibleで操作したい場合、この手順だとdropbearの起動とansibleの依存解決でpythonがインストールされた時点で実行可能のように思えるが、dropbearではsftpが使えないのでもう一手間必要になる。
以下のどちらかで解決する。

1. 操作する側のansible.cfgの[ssh_connection]セクションにある「scp_if_ssh=True」のコメントアウトを外す
2. Android側でopenssh-serverをインストールする（/usr/lib/sftp-serverが欲しいだけでsshdとして起動するわけではない）

さらにPATHが/usr/binと/binだけなのでplaybookの中でenvironmentを使うなどする必要もあるだろう。以下のように。

```
- hosts: all
  tasks:
    - apt: name=openssh-server
      environment:
        PATH: /sbin:/usr/sbin:/usr/local/sbin:{{ ansible_env.PATH }}
```

ちなみにこれ、最初「gather_facts: no」とかやってて、動かないのにしばらく悩んだりした。
ansible_env.PATHの値はgather_factsによって取得されるからnoにはできないことに気付くのに時間が掛かった、という話。