fluentdとDockerとBigQueryで1時間でビッグデータ解析基盤を作る


最近話題のDockerやGoogle Cloud Platformを用いて大規模データのための解析基盤を作ります。今回はデータソースとしてTwitter Streaming APIを利用しますが、アクセスログなどに応用することももちろん可能です。コードは一行も書きません。解析基盤をつくためにマシンを用意する必要はもちろんありません。

BigQueryについては、
[Googleの虎の子「BigQuery」をFluentdユーザーが使わない理由がなくなった理由 #gcpja](http://qiita.com/kazunori279/items/10ac0066ac9b0b5aaaf3)
が参考になります。

# 利用するプロダクト/サービス
* Google Cloud Platform
  * Google Compute Engine
  * BigQuery
* [Docker](https://www.docker.io/)
* [fluentd](http://fluentd.org/)
  * [fluent-plugin-twitter](https://github.com/y-ken/fluent-plugin-twitter)
  * [fluent-plugin-bigquery](https://github.com/tagomoris/fluent-plugin-bigquery)
* Twitter Streamping API

# 想定する環境
* mac os x 10.9
* docker / boot2docker インストール済み
* python 2.* インストール済み (`gcloud`コマンドは3系では動きません)

# 方針
* fluentdを使ってTwitter Streaming APIから取得したツイートをBigQueryに保存する
* Googleが提供しているDockerに最適化されたGoogle Compute EngineのOSイメージを利用 [(参考)](http://www.publickey1.jp/blog/14/dockerosgoogle_compute_enginecoreos.html)
* ローカル上でfluentdのコンテナを作成し、それをGoogle Compute Engine上で動かす
  * つまり fluentd on Docker on Google Computed Engine
* [container-agent](https://github.com/GoogleCloudPlatform/container-agent)を利用してインスタンス起動時にdocker上でfluentdのコマンドが自動的に実行されるようにする

## Step 1 - fluentdコンテナの構築

### Dockerの動作確認

```
$ boot2docker up # Dockerの起動
[2014-06-07 13:06:44] Starting boot2docker-vm...
[2014-06-07 13:07:16] Started.

$ export DOCKER_HOST=tcp://localhost:4243 # Dockerコマンドをlocalhost:4243に向ける

$ docker version # バージョンの確認
Client version: 0.11.1
Client API version: 1.11
Go version (client): go1.2.1
Git commit (client): fb99f99
Server version: 0.11.1
Server API version: 1.11
Git commit (server): fb99f99
Go version (server): go1.2.1
```

### コンテナの構築

(私が作成したコンテナ[`tacke/fluentd`](https://registry.hub.docker.com/u/tacke/fluentd/)を利用すれば、この作業はスキップできます。)

```
$ mkdir oreore-fluentd

$ cd !$

$ cat Dockerfile
FROM ubuntu:12.04

ENV DEBIAN_FRONTEND noninteractive
RUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list

RUN apt-get update && apt-get -y upgrade
RUN apt-get -y install curl 

RUN curl -O http://packages.treasure-data.com/debian/RPM-GPG-KEY-td-agent && apt-key add RPM-GPG-KEY-td-agent && rm RPM-GPG-KEY-td-agent
RUN curl -L http://toolbelt.treasuredata.com/sh/install-ubuntu-precise.sh | sh

RUN apt-get -y install libssl-dev 
RUN apt-get -y install build-essential
RUN /usr/lib/fluent/ruby/bin/fluent-gem install fluent-plugin-twitter
RUN /usr/lib/fluent/ruby/bin/fluent-gem install fluent-plugin-bigquery
RUN echo "" >  /etc/td-agent/td-agent.conf

$ docker build -t <YOUR_TAG_NAME> . # コンテナのビルド
```

`/etc/td-agent/td-agent.config`は意図的に空にしています。
Dockerコンテナはimmutableなので、設定内容をコンテナに含めてしまうと複数のfluentdサーバーを異なる設定で起動したい場合に、それぞれ別のコンテナを作らなければいけません。
また、twitterの認証トークンが書き込まれたコンテナをDockerのリポジトリにアップロードするのはセキュリティ上問題があります。
そのため今回は、後に出てくるように、td-agentコマンドのオプションに直接設定を渡す方法を取ることにします。(別の方法でも可能だと思います。)

```
$ docker push <YOUR_TAG_NAME> # コンテナを docker index にアップロード
The push refers to a repository [<YOUR_TAG_NAME>] (len: 1)
Sending image list
Pushing repository <YOUR_TAG_NAME> (1 tags)

...

Pushing tag for rev [] on {https://registry-1.docker.io/v1/repositories/<YOUR_TAG_NAME>/tags/latest}
```

これでGoogle Compute Engine上のDockerにコンテナをインストールする準備が整いました。

## Step 2 - GCE上でDockerコンテナ動かす

### Google Cloud SDKのインストール

```
$ curl https://sdk.cloud.google.com | bash # Google Cloud SDKのインストール

/Users/yuki.takeichi/google-cloud-sdk/install.sh
Welcome to the Google Cloud SDK!

The Google Cloud SDK is currently in developer preview. To help improve the
quality of this product, we collect anonymized data on how the SDK is used.
You may choose to opt out of this collection now (by choosing 'N' at the below
prompt), or at any time in the future by running the following command:
    gcloud config --global-only set disable_usage_reporting true

(略)

For more information on how to get started, please visit:
  https://developers.google.com/cloud/sdk/gettingstarted

```
SDKはデフォルトだと`$HOME/google-cloud-sdk`にインストールされます。
`bash_profile`やコマンド補完や`$PATH`もよきに設定してくれます。

```
$ gcloud auth login # Google Cloud Platformにログイン

Your browser has been opened to visit:

    https://accounts.google.com/o/oauth2/auth?redirect_uri...
You are now logged in as [_____@gmail.com].
Your current project is [None].  You can change this setting by running:
  $ gcloud config set project <project>
```

ブラウザが立ち上がるのでログインをしてOAuthの認証を許可しましょう。

### GCPプロジェクトの作成

プロジェクトを設定しろと言われるので、まずはConsoleでプロジェクトを作成します。

project idは適当な英単語と数字を組み合わせたものを自動生成してくれますが、わかりにくいのでプロジェクト名と同一にしておくほうがよいです。

GCPのDeveloper Consoleの`Cloud Develop -> リリース`でリポジトリの設定を行います。

設定が完了すると、GCP上にデプロイ用のリモートリポジトリが作成されます。
その後、ターミナルに戻りprojectの設定を行います。

```
$ mkdir ~/gcp

$ cd !$

$ gcloud init <PROJECT_ID> # プロジェクトリポジトリを取得
Initialized gcloud directory in [/Users/<USER_NAME>/gcp/<PROJECT_ID>/.gcloud].
Cloning [https://source.developers.google.com/p/<PROJECT_ID>/r/default] into [default].
Initialized empty Git repository in /Users/<USER_NAME>/gcp/<PROJECT_ID>/default/.git/
Project [<PROJECT_ID>] was initialized in [/Users/<USER_NAME>/gcp/<PROJECT_ID>].

$ cd <PROJECT_ID>/

$ gcloud config set project <PROJECT_ID> # コマンドにプロジェクトを設定
```

### DockerのOSイメージを利用してGCEインスタンスを作成
https://developers.google.com/compute/docs/containers を参考に進めます。

Googleが提供しているDockerに最適化されたOSイメージを利用します。
ゾーンは`asia-east1-a`(もしくは`asia-east1-b`)にします。
マシンタイプはとりあえず一番ロースペックの`f1-micro`(CPU1コア RAM0.6GB)を選択します。

```
$ gcloud compute instances create <VM_NAME> \
--image projects/google-containers/global/images/container-vm-v20140522 \
--zone asia-east1-a \
--machine-type f1-micro

ERROR: (gcloud.compute.instances.create) some requests did not succeed:
 - Project projects/<PROJECT_ID> cannot accept requests to insert while in an inactive billing state.  Billing state may take several minutes to update.
```

課金情報を登録しろと言われました。Consoleに戻って登録します。
完了したら再び上記コマンドを実行します。

```
$ gcloud compute instances create <VM_NAME> \
--image projects/google-containers/global/images/container-vm-v20140522 \
--zone asia-east1-a \
--machine-type f1-micro 

---
canIpForward: false
creationTimestamp: '2014-06-06T19:28:23.364-07:00'
disks:
- autoDelete: true
  boot: true
  deviceName: persistent-disk-0
  index: 0
  kind: compute#attachedDisk
  mode: READ_WRITE
  source: <VM_NAME>
  type: PERSISTENT
id: '000000000000'
kind: compute#instance
machineType: f1-micro
metadata:
  fingerprint: aaaaaaaaa=
  kind: compute#metadata
name: <VM_NAME>
networkInterfaces:
- accessConfigs:
  - kind: compute#accessConfig
    name: external-nat
    natIP: YYY.YYY.YYY.YYY
    type: ONE_TO_ONE_NAT
  name: nic0
  network: default
  networkIP: XXX.XXX.XXX.XXX
scheduling:
  automaticRestart: false
  onHostMaintenance: MIGRATE
selfLink: https://www.googleapis.com/compute/v1/projects/<PROJECT_ID>/zones/asia-east1-a/instances/<VM_NAME>
serviceAccounts:
- email: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX@developer.gserviceaccount.com
  scopes:
  - https://www.googleapis.com/auth/devstorage.read_only
status: RUNNING
tags:
  fingerprint: bbbbbbb=
zone: asia-east1-a

```

正常にインスタンスが作成されたようです。ちゃんと動いているか確認するためSSHでログインしてみます。

```
$ gcloud compute ssh --zone asia-east1-a <VM_NAME>
WARNING: You don't have an ssh key for Google Compute Engine. Creating one now...
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
<USER_NAME>@XXX.XXX.XXX.XXX's password: # 空
INFO: Running command line: ssh -o UserKnownHostsFile=/dev/null -o CheckHostIP=no -o StrictHostKeyChecking=no -i /Users/<USER_NAME>/.ssh/google_compute_engine -A -p 22 <USER_NAME>@XXX.XXX.XXX.XXX --
Warning: Permanently added 'XXX.XXX.XXX.XXX' (RSA) to the list of known hosts.
Linux oreore-vm 3.14-0.bpo.1-amd64 #1 SMP Debian 3.14.4-1~bpo70+1 (2014-05-14) x86_64

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.
<YOUR_USER>@<VM_NAME>:~$ 
```

ログインできましたね。初回時のみ鍵を作成する必要があります。
VM上で以下を実行します。パスワードは必要ありません。

```
$ sudo docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
```

Dockerが起動していることが確認できました。
それではローカルのコンソールに戻ってインスタンスを削除しておきましょう。

```
$ gcloud compute instances delete <VM_NAME> --zone asia-east1-a # インスタンス削除
```

## Step 3 - fluentd on Docker on GCE

それでは、Step1でpushしたコンテナを利用してfluentdをGCE上で動かします。
その前に、BigQueryのテーブルを作成します。

```
$ bq mk my_dataset # データセットの作成
Dataset '<PROJECT_ID>:my_dataset' successfully created.

$ bq mk -t my_dataset.twitter id:integer,id_str:string,text:string,lang:string # テーブルスキーマの作成
Table '<PROJECT_ID>:my_dataset.twitter' successfully created.
```

正常に作成されました。
それでは次はいよいよGCE上でfluentdを動かします。

Googleが提供しているOSイメージにはcontainer-agentがインストールされています。
container-agentはインスタンスが起動したタイミングでインスタンスのメタ情報を読み取って、Dockerコンテナをダウンロードし、コンテナ上で予め設定したコマンドを実行します。
containter-agentのための設定を`containers.yaml`に記述します。

```
$ cat containers.yaml
version: v1beta1
containers:
  - name: twitter-public-stream
    image: tacke/fluentd
 command: ['td-agent', '-i', '<source> \n type twitter \n consumer_key XXX \n consumer_secret XXX \n oauth_token XXX \n oauth_token_secret XXX \n tag input.twitter.test \n timeline sampling \n output_format nest \n </source> \n \n <match input.twitter.**> \n type bigquery \n \n auth_method compute_engine \n \n project oreore-project \n dataset oreore_dataset \n table twitter \n field_integer id \n field_string id_str,text,lang \n </match>']

```

コンテナ上で実行するtd-agentコマンドにはインラインコマンドオプション(`-i`)を利用して直接設定を渡しています。
`XXX`の部分はTwitterの各認証トークンで置き換えてください。
ちなみに元々の`fluentd.conf`は、

```
# fluentd.conf
<source> 
 type twitter 
 consumer_key        XXX 
 consumer_secret     XXX 
 oauth_token         XXX
 oauth_token_secret  XXX 
 tag                 input.twitter.test 
 timeline            sampling 
 output_format       nest 
</source> 
  
<match input.twitter.**> 
 type bigquery 
  
 auth_method compute_engine 
  
 project oreore-project 
 dataset oreore_dataset 
 table   twitter 
  
 field_integer id 
 field_string  id_str,text,lang 
</match>

```

このような姿でした。

そして、インスタンスを立ち上げます。

```
$ gcloud compute instances create <VM_NAME> \
--image projects/google-containers/global/images/container-vm-v20140522 \
--zone asia-east1-a \
--machine-type f1-micro \
--scopes bigquery \
--restart-on-failure \
--metadata-from-file google-container-manifest=containers.yaml
```

`--metadata-from-file`は`key=value`の形式でインスタンスにメタ情報を与えます。
`--scopes bigquery`でインスタンスに対してBigQueryへのアクセス許可を与えています。`--restart-on-failure`を付けておくと問題が発生してVMが終了した際に自動的に再起動されます。

インスタンスが立ち上がりしばらくするとDocker上でfluentdが起動し、
取得されたツイートのBigQueryへの保存が開始します。

### BigQuery上でクエリを叩く

取得されたツイートがちゃんとBigQueryに保存されているか確認してみましょう。

```
$ bq query "select count(*) from my_dataset.twitter"
Waiting on bqjob_r231b79b42efff050_00000146763c4855_1 ... (0s) Current status: DONE    
+-------+
|  f0_  |
+-------+
| 78450 |
+-------+
```

ちゃんと保存されているのが確認できましたね！
お疲れ様でした！

# 最後に

ログ収集プラットフォームであるfluentdを使って簡単にストリームデータの収集が行えるようになりました。Dockerを使えば他人が作ったインストール済みコンテナを利用して、煩わしいセットアップを抜きにミドルウェアを試すことができるようになりました。BigQueryを利用すればTB級のビッグデータへのクエリを、フレンドリーなSQLで記述することができ、ものの数秒で結果を得ることができるようになりました。

このような技術の進歩によって、特別なマシンを持っていない個人やベンチャーでも、簡単にデータ解析基盤を手に入れることができるようになりました。素晴らしいことだと思います。
特別な技術は必要ありません。解析したい大規模データがあるのに、リソースや技術が無いために今まで行えなかった人にとって、この記事が参考になれば幸いです。