openstfの環境をdockerを使ってLinux上にゼロから構築する方法

# はじめに
Androidのリモートデバッグ環境を構築できる[openstf](https://github.com/openstf/stf)というものがある。Mac上で構築する方法は書いてあるが、docker環境も提供されてるらしいぞと思って手をつけたらあまりにも情報が少なく相当辛かったので０から環境構築するまでのノウハウを記載する。
なお、私自身がdockerを使い始めて１週間とかなので至らない部分は多々あるかと思いますがご容赦を。

2017/2/27追記
社内サーバーとして公開する方法を記載しました。
「[簡単にopenstfの環境をMac上にゼロから構築する方法](http://qiita.com/k_keisuke/items/77144fc47951a6adf697)」

# 構築環境
OS: Ubuntu 16.0.4(実際に構築したのはMac上に立てたVirtualBox上です)

# docker, docker-composeを入れる
## dockerをインストールする
これについてはたくさん紹介サイトがあるのでそちらを参考にしていただきたい。
[docker公式](https://docs.docker.com/engine/installation/linux/ubuntu/)
[[Docker] ubuntu 14.04/16.04にDockerをインストール](http://qiita.com/koara-local/items/ee887bab8c7186d00a88)

## docker-composeを入れる
これは公式のページに記載されているコマンドをぽちぽち入力すればOK。
[docker-compose公式](https://docs.docker.com/compose/install/)
参考までに2017/2/23現在のコマンドを書いておく。

```text:cmd
$ curl -L "https://github.com/docker/compose/releases/download/1.11.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
$ chmod +x /usr/local/bin/docker-compose
```
バージョン部分が更新される場合があるので公式ページのコマンドをコピペすること推奨。また、有志のページで幾つか紹介されてるが、自分の環境ではうまく動かなかったので、何も考えず公式に行ったほうが無難。
正しくインストールできていれば「--version」オプションでバージョンが見える

```text:cmd
$ docker-compose --version
docker-compose version: 1.11.1
```

# docker-compose.yamlファイルを用意する
そもそもopenstf環境を構築する場合、**openstfをdocker hubからpullするだけでは動かない**。
環境構築に必要なdocker imageはopenstf以外に「**rethinkdb:2**」と「**sorccu/adb**」の二つ。
前者は名前の通りDB関係のimage、後者はadbコマンドをdockerに流すようにするためのimage。
これらをうまくopenstfに繋げる必要があるが、単品でコマンドをぽちぽち叩くと相当面倒くさい上にハマる可能性がかなり上がる。

そこで、docker-compose.yamlを記載し、一気に設定してしまうのが良さげってことで用意した。最終系は最後に記載。
ただ、注意してほしいのは**用意したyamlファイルを叩くだけでは動かない**。
そのため必ず以下の手順を読み進めながら環境構築をすること。

## docker-compose.yamlファイルを作成
任意のフォルダに「docker-compose.yaml」ファイルを作る。ファイル名はこれで固定っぽい？
その後、ファイルを開きファイル先頭に以下を記載

```yaml:docker-compose.yaml
version: '2'
services:
```

version2で動かすよー、という宣言と、サービスのタグを追加。以下の諸々の設定は全てserviceタグ以下に記載する
## rethinkdbの設定
yamlの設定の記載は以下の通り

```yaml:docker-compose.yaml
version: '2'
services:
  stfdb:
    image: rethinkdb:2
    ports:
      - 8082:8080
      - 28015:28015
    command: rethinkdb --bind all
```

サービス名の「stfdb」は任意に決めてOK。わかりやすいもので。ただし他のサービスからこの名前を参照するので変更漏れには注意。
imageは「rethinkdb:2」で固定。
portsは二つあける。１つ目の「8082:8080」はブラウザからアクセスするためのポート、２つ目の「28015:28015」はopenstfのサービスからアクセスするためのポートになっている。
前者の外部向けのポート(上の例なら8082ポート)は任意だが、後者はこのポートで固定しておいたほうが無難。
コマンドは上記のもので固定。特に新規のDBを最初に作ったりはしないようにしてる

### adminのパスワードを設定する
これをやり忘れるとopenstfから接続できないため必ずやること。
やり方は以下の通り。
一度yamlファイルを保存し、以下のコマンドを叩いてrethinkdbのサービスを立ち上げる

```text:cmd
$ docker-compose up -d stfdb
```
コマンドを叩くときは、必ずyamlファイルと同じ場所にいること。
その後、ブラウザでhttp://localhost:8082 にアクセスするとRethinkDBのページが開くはず。
その後、画面上部の「Data Explorer」を選択し、入力欄に以下を入力

```rb:cmd
r.db('rethinkdb').table('users').get('admin').update({password:'admin'})
```
Runボタンを押して反映されれればOK。
パスワードは任意で良いが、ここで入力したものをyamlに記載するので後でわからなくならないように注意(今回は「admin」にしてる)

<img width="1220" alt="スクリーンショット 2017-02-23 15.24.18.png" src="https://qiita-image-store.s3.amazonaws.com/0/76726/07ae9e53-5633-0c98-e3b6-4f02631630ed.png">

ここでやってるのは外部からDBにアクセスする際のパスワード設定。

## adbdの設定
yamlファイルの記載は以下の通り

```yaml:docker-compose.yaml
  adbd:
    image: sorccu/adb
    ports:
      - 5037:5037
    volumes:
      - /dev/bus/usb:/dev/bus/usb
    privileged: true
```
ここでの設定は[公式に書かれているPattern4の設定](https://github.com/sorccu/docker-adb#pattern-4---remote-client)をそのままyamlファイルに起こしているだけになる
公式のコマンドは以下の通り

```text:cmd
docker run -d --privileged -v /dev/bus/usb:/dev/bus/usb --name adbd -p 5037:5037 sorccu/adb
```

注意すべきはportsとvolumesの設定。
portは特に変更していなければadbコマンドは5037を使うので、外部、内部ともに5037となり、必ず「5037:5037」でないといけない。
volumesは外部の設定部分(コロンの左側)がマシンのOSに依存する。Linux系は「/dev/bus/usb」以下に端末がマウントされる。

### adb接続確認用の設定
この設定は必須ではないが、端末が認識されているかどうかを確認する上で便利なので記載。

```yaml:docker-compose.yaml
  adb-devices:
    image: sorccu/adb
    tty: true
    links:
      - adbd:adbd
    command: adb -H 172.18.0.1 -P 5037 devices
```
公式のコマンドは以下の通り

```text:cmd
$ docker run --rm -ti sorccu/adb adb -H x.x.x.x -P 5037 devices
```

-H後のIPアドレスは、**コンテナから見たGatewayのIPアドレスになる**。確認の仕方は「docker inspect」コマンドで「adbd」サービスの設定を覗き見るのが良い。
コマンドは以下の通り

```text:cmd
$ docker-compose up -d adbd ★adbdサービスを立ち上げる
$ docker ps ★サービス一覧を表示し、adbdのコンテナ名を確認する
$ docker inspect <adbdのコンテナ名> | grep Gateway ★GatewayのIPを確認
```

<img width="659" alt="スクリーンショット 2017-02-23 15.45.57.png" src="https://qiita-image-store.s3.amazonaws.com/0/76726/0ac8e445-0d3d-c0ce-5ca3-d85e2978f069.png">

GatewayのIPアドレスを指定したら、以下のコマンドを叩くと、デバイスが繋がっていれば「adb devices」を叩いたときの結果が出てくる。「run --rm」で叩くことで、サービスを残さないようにする

```text:cmd
$ docker-compose run --rm adb-devices」
```
<img width="661" alt="スクリーンショット 2017-02-23 15.50.59.png" src="https://qiita-image-store.s3.amazonaws.com/0/76726/fe092741-4cf6-1b77-a4fd-26ae93aebc1e.png">

## openstfの設定
yamlファイルの設定は以下の通り

```yaml:docker-compose.yaml
  stf:
    image: openstf/stf
    ports:
      - 8083:7100
    links:
      - stfdb:stfdb
      - adbd:adbd
    environment:
      - RETHINKDB_PORT_28015_TCP=tcp://stfdb:28015
      - RETHINKDB_ENV_DATABASE=stf
      - RETHINKDB_ENV_AUTHKEY=admin
    command: stf local -R --public-ip 172.18.0.4 --adb-host adbd 
```

imageは「openstf/stf」で固定
portsは内部向けの方(7100)は固定。外部の方(8083)は任意でOK。
linksはstfdbとadbdのサービスを指定。特にこだわりがなければこのままで。
environmentの設定については、
RETHINKDB_PORT_28015_TCPはRethinkDBのIPとポートを指定する必要がある。linksを設定していればサービス名で引けるので「tcp://stfdb:28015」となる
RETHINKDB_ENV_DATABASEはRethinkDB上に作るDBの名前。任意でOK。
RETHINKDB_ENV_AUTHKEYは「adminのパスワードを設定する」で設定したパスワードを書く。変更し忘れると繋がらなくなる。
commandの「--public-ip」には「stf」サービス自身のIPアドレスを指定する。確認方法は後述。
これは正しくはIPを固定させるべきだが、方法がわからないので確認中（わかったら変更します）。

「--adb-host」には「adbd」サービスのIPアドレスを指定する。linksを設定しているので「adbd」でOK

### stfサービスのIPアドレスを確認する
正直これが正しいかどうかわからないけど一応これでわかる
まず「--public-ip」に適当なIPアドレスを書いておく（localhostとかでもOK)
次に以下のコマンドを叩く

```text:cmd
$ docker-compose up -d stf
```

これで一旦stfサービスが立ち上がるので、inspectコマンドでIPアドレスを確認する

```text:cmd
$ docker ps ★stfサービスの名前を確認
$ docker inspect <stfサービス名を入力> | grep IPAddress
```
ここで取得できたIPを改めてyamlファイルに記載する

## 起動させる
全ての設定が終わったらいよいよ起動。コマンドは以下の通り

```text:cmd
$ docker-compose up -d stfdb
$ docker-compose up -d adbd
$ docker-compose up -d stf
```

一応stfサービスのlinkでstfdbとadbdを設定しているため、stfdbとadbdのサービスを個別に起動しなくても問題はない、はず。
ただ、まれにstfdbやadbdより先にstfサービスが立ち上がることがある、らしいので、念のため個別に起動させる。
「-d」オプションをつけるとサービスがバックグラウンドで動くようになり、ターミナルに制御が帰ってくる。もし動作がおかしく、ログを確認したい場合は「-d」オプションを外せばログが確認出来る。

## stfサービスにアクセスする
正しく動作していれば http://＜docker自身のIPアドレス＞:8083 でアクセスできる。
dockerのIPアドレスの確認の仕方は、
(デフォルトのままであれば)ifconfigコマンド→「docker0」の項目に書かれている。

```text:cmd
$ ifconfig
```

<img width="736" alt="スクリーンショット 2017-02-23 16.33.05.png" src="https://qiita-image-store.s3.amazonaws.com/0/76726/1896456c-0a1d-3836-43f2-4593c810d006.png">

上記の例ならば、http://172.17.0.1:8083 でアクセスすればstfのページが開かれるはず(なお、URLはリダイレクトが行われ、http://<stfサービスのIP>:7100 とかになってる、はず)

## 最終yamlファイル
**これ単体では動きません！！**必ず上記の手順を読み、こまかな設定や記載変更を行ってください

```yaml:docker-compose.yaml
version: '2'
services:
  stfdb:
    image: rethinkdb:2
    ports:
      - 8082:8080
      - 28015:28015
    command: rethinkdb --bind all
  adbd:
    image: sorccu/adb
    ports:
      - 5037:5037
    volumes:
      - /dev/bus/usb:/dev/bus/usb
    privileged: true
  adb-devices:
    image: sorccu/adb
    tty: true
    links:
      - adbd:adbd
    command: adb -H 172.18.0.1 -P 5037 devices
  stf:
    image: openstf/stf
    ports:
      - 8083:7100
    links:
      - stfdb:stfdb
      - adbd:adbd
    environment:
      - RETHINKDB_PORT_28015_TCP=tcp://stfdb:28015
      - RETHINKDB_ENV_DATABASE=stf
      - RETHINKDB_ENV_AUTHKEY=admin
    command: stf local -R --public-ip 172.18.0.4 --adb-host adbd
```

[[Gist]openstf環境をUbuntu上に構築するためのdocker-compose.yamlファイル](https://gist.github.com/kiuchikeisuke/5163b7f176e7e03e8e9055a8bb75961f)

# トラブルシューティング

## RethinkDBと繋がらない(No hosts left to try)
以下のようなエラーが出るケース

```text:log
openstf_1         | INF/util:procutil 1 [*] Forking "/app/lib/cli migrate"
openstf_1         | INF/db 10 [*] Connecting to 172.18.0.2:28015
openstf_1         | INF/db 10 [*] Unable to connect to 172.18.0.2:28015
openstf_1         | FTL/db 10 [*] No hosts left to try
openstf_1         | FTL/util:lifecycle 10 [*] Shutting down
...
```

stfサービスからrethinkDBに接続できないために発生しているエラー。原因は幾つかある

### RethinkDBが立ち上がっていない
確認方法-> 「docker ps」コマンドを叩き、rethinkdbのサービスが立ち上がっているか、またブラウザからアクセスできるかを確認する

### stfサービスのenvironmentの記載が間違っている
確認方法-> ログの「INF/db 10 [*] Connecting to 172.18.0.2:28015」で書かれているIPとポートがrethinkDBを指しているかを確認する。同様に、RETHINKDB_PORT_28015_TCPの設定が正しくなっているかを確認。

### パスワードを設定し忘れている
上記の２つでないならばこれの可能性が大きい。特にコンテナを削除した後、再生成するとパスワードが消えているので要注意。
対処方法は「adminのパスワードを設定する」の手順を参考に、パスワードを設定する。合わせてRETHINKDB_ENV_AUTHKEYにパスワードを記載しているかを確認

## Connecting db to stfdb:28015 で止まる(Unhandled rejection Error: spawn adb ENOENT)
以下のようなエラーがログ中にあるケース(でない場合もあるので必要条件ではない)

```text:log
openstf_1         | Unhandled rejection Error: spawn adb ENOENT
openstf_1         |     at exports._errnoException (util.js:1026:11)
openstf_1         |     at Process.ChildProcess._handle.onexit (internal/child_process.js:193:32)
openstf_1         |     at onErrorNT (internal/child_process.js:359:16)
openstf_1         |     at _combinedTickCallback (internal/process/next_tick.js:74:11)
openstf_1         |     at process._tickCallback (internal/process/next_tick.js:98:9)
```

これは**adbの接続がうまくいってない場合に発生する**。そのためまずは**「adb-devices」サービスを使って端末が認識されるかどうかを確認したほうが良い**。

### volumesのマッピングが間違っている
Dockerを動かしているOSがLinux以外だと発生する。
adbdサービスのvolumesの外部へのマッピング(コロンの左側）を変更することで治るはず。
なお、Macのマッピング先は調べたがわからなかったので保留している。

### stfコマンドでadb-portの指定を忘れている
stfのコマンドで「--adb-port」を指定し忘れていると起きる。記載がないと「localhost」を見に行ってしまうため。
「openstfの設定」の記載を参考に「--adb-port」を設定する

## トップページは開いたのにログインしたらページが開かない
IPのリダイレクトが失敗しているパターン。

### stfコマンドでpublic-ipの指定を忘れている/間違っている
「--public−ip」で指定したIPにリダイレクトが行われるようになる（多分）。
指定し忘れると「localhost:7100」にアクセスしに行くので繋がらなくなる。なので、「stfサービスのIPアドレスを確認する」を参考に、stfサービス自身のIPアドレスを正しく入力しているかを確認する

# 今後の課題
今回の方法ではLinuxマシン上に環境を構築することはできるが、これを社内サーバーとして公開し、社員全員に使ってもらう、という使い方はできない。
これをするには、Nginxやらを使ってサービスとして公開する必要がある。
その具体的な方法についてはこちらの「[簡単にopenstfの環境をMac上にゼロから構築する方法](http://qiita.com/k_keisuke/items/77144fc47951a6adf697)」を参照。