FluentdでGoogle BigQueryにログを挿入してクエリを実行する

Googleの虎の子BigQueryをFluentdユーザーが使わない理由はなくなったとのこと。

- [Googleの虎の子「BigQuery」をFluentdユーザーが使わない理由がなくなった理由 #gcpja - Qiita](http://qiita.com/kazunori279/items/10ac0066ac9b0b5aaaf3)

よし、Google BigQueryを使って超高速ログ解析だ!!!!と思っているとそこまでの道のりは長かった。


# Google BigQueryの環境を構築する

Google BigQueryは[Google Cloud Platform](https://cloud.google.com/)のサービスの1つである。Google Cloud Platformには様々なサービスがあり、統合されているような、されていないような作りになっている。AWSのWebインターフェースも難しいけど、Google Cloud Platformもよくわからないので覚悟してかかろう。公式のドキュメントも記述が古いときもあるので疑ってかかろう。

## プロジェクトを作成する

Google BigQueryを使用するには[Google Developers Console](https://console.developers.google.com)からプロジェクトを作成する必要がある。Google Cloud Platformの他のサービスを既に使用している場合はそのプロジェクトを使い回しても問題ない。

- [Google Developers Console](https://console.developers.google.com)

プロジェクトを作成するにはプロジェクト名とプロジェクトIDが必要である。このプロジェクトIDは後から変更できず、BigQueryでもよく記入することになるので、良い感じのプロジェクトIDを指定しよう。

Google BigQueryは処理データ量には無料枠が用意されているが、ストレージ自体は有料であるため、課金を有効にしないと使用することができない。予めプロジェクトの課金を有効にしておこう。Google BigQueryを無料で試せると思っていたユーザは諦めよう。

課金を有効にするにはプロジェクトの設定画面から課金を有効にする。クレジットカードによる決済が利用できる。

## bqコマンドをインストールする

Google BigQueryはWebインターフェースから操作できるが、実際できないことも多い。空のテーブル作成とかできないので、HTTP APIか専用のCLIコマンドである`bq`を使用して操作することになる。API経由ですべての処理を行うのは大変なので`bq`コマンドをインストールしよう。

### Google Cloud SDKをインストールする

`bq`コマンドはGoole Cloud SDKに含まれている。下記ページからダウンロード、インストールしよう。

- [Google Cloud SDK](https://developers.google.com/cloud/sdk/)

zshのPATHを通してくれるシェルスクリプトは筆者の環境ではうまく動かなかった。結局`google-cloud-sdk/bin`にPATHが通っていれば問題ない。PATHを通すには`.bashrc`や`.zshrc`みたいなファイルに次のように追記すれば良い。

```bash
export PATH=/Users/harukasan/google-cloud-sdk/bin:$PATH
```

インストール先もホームディレクトリである必要はない。共用環境などでは適切な位置に配置しよう。

### アカウント認証を行う

Google Cloud SDKはOAuthによるAPI認証を行う。アカウント認証を行うには次のコマンドを実行する。ブラウザが開いた場合はそこで認証を行い、開かなかった場合は指定されたURIを開き、認証を行う。

```bash
$ gcloud auth login
```

認証を行ったアカウントは次のコマンドで確認することができる。

```bash
$ gcloud auth list
```

### コマンドの確認

bqコマンドが実行できるか確認しよう。ヘルプが表示されれば問題ない。

```bash
$ bq
Python script for interacting with BigQuery.

USAGE: bq.py [--global_flags] <command> [--command_flags] [args]

...
```

プロジェクトが利用可能かどうかは次のコマンドで確認できる。

```bash
$ bq ls
     projectId      friendlyName
 ----------------- --------------
  your-project-id   your-project
```

# BigQueryを準備する

さて、ここまででGoogle BigQueryを利用可能な環境が整ったはずだ。続いてBigQuery側の準備をしよう。FluentdからBigQueryに入力するには、今のところ、予めテーブルを作成しておく必要がある。

## データセットを作成する

BigQueryにはデータセットとテーブルという概念がある。データセットには複数のテーブルが所属し、データセットごとに権限の指定ができる。

データセットを作成するには、Webインターフェースから操作するか、次のbqコマンドを実行する。

```bash
$ bq mk your-project-id:your_dataset
```

Webインターフェースから操作する場合は、プロジェクト名右の![down.png](https://qiita-image-store.s3.amazonaws.com/0/5218/ae47ac72-8c82-39c7-5436-64f80e658460.png)メニューの`Create new dataset`からデータセットを作成できる。

## テーブルを作成する

BigQueryでは予めテーブルスキーマを決定しておく必要がある。スキーマの指定には、リスト形式で指定する方法とJSON形式で指定する方法があるが、JSON形式にしておくとFluentdの設定にもそのまま使用することが出来るので便利だ。Fluentdからログを入力する場合、ログのスキーマとテーブルスキーマが一致していなければならないので注意しよう。

リスト形式で指定する場合、ネストしたデータを扱うことが今のところ出来ない。Webインターフェースではファイルを指定してロードすることしか出来ないが、スキーマ指定はリスト形式でしか行えないため、Webインターフェースからネストしたデータを扱う方法も今のところない。

スキーマのJSONは次のような感じになる。指定できるデータ型には、`STRING`、`INTEGER`、`FLOAT`、`BOOLEAN`、`RECORD`がある。`RECORD`の場合は`fields`属性にネストするフィールドを指定する。

```json:schema.json
[
  {
    "name": "time",
    "type": "INTEGER"
  },
  {
    "name": "uri",
    "type": "STRING"
  },
  {
    "name": "request",
    "type": "RECORD",
    "fields": [
      {
        "name": "addr",
        "type": "STRING"
      },
      {
        "name": "ua",
        "type": "STRING"
      },
      {
        "name": "referrer",
        "type": "STRING"
      }
    ]
  }
]

```

スキーマを記述したら`bq`コマンドを実行しテーブルを作成する。

```bash
$ bq mk -t your-project-id:your_dataset.your_table ./schema.json

Table 'your-project-id:your_dataset.your_table' successfully created.
```

作成したテーブルは次のコマンドで確認することが出来る。また、Webインターフェースでも確認できる。

```bash
$ bq show your-project-id:your_dataset.your_table
```

存在するテーブルを確認するには`bq ls`コマンドを実行すれば良い。

```bash
$ bq ls your-project-id:your_dataset
```

# FluentdからBigQueryにログを挿入する

FluentdからBigQueryにログを挿入するには[@tagomoris](https://twitter.com/tagomoris)さんがつくったfluent-plugin-bigqueryを使う。適切な方法を用いてインストールしておこう。

- [fluent-plugin-bigquery](https://github.com/tagomoris/fluent-plugin-bigquery)

## 認証キーを取得する

FluentdをCompute Engine以外で実行する場合は鍵認証が必要になる。鍵認証を行うには、[Google Developer Console](https://console.developers.google.com)からサービスアカウントを作成する。

サービスアカウントの作成はプロジェクトのコンソールの認証情報画面で行う。作成するには、OAuthの`新しいクライアントIDを作成`をクリックし、サービスアカウントを選択する。発行が完了すると、画面上にクライアントID、メールアドレス、フィンガープリントが表示され、鍵ファイルがダウンロードされる。

![UGoogle Developer Console](https://qiita-image-store.s3.amazonaws.com/0/5218/facfdf69-7cdc-9235-8a41-0e3a76c5824d.png)

## Fluentdを設定する

サービスアカウントが発行できたらFluentdの設定を行う。出力する部分の設定は次のようになる。

```apache:fluent.conf
<match access.log>
  type bigquery

  method insert

  auth_method private_key
  email XXXXXXXXXXXX@developer.gserviceaccount.com
  private_key_path ./XXXXXXXXXXXX-privatekey.p12

  project your-project-id
  dataset your_dataset
  table your_table

  time_format %s
  time_field time
  schema_path ./your_table_schema.json
</match>
```

### 認証

認証には先ほど取得したサービスアカウントのメールアドレスと鍵ファイルを使用する。メールアカウントと鍵ファイルはそれぞれ`email`、`private_key_path`ディレクティブに指定する。

```apache
  auth_method private_key
  email XXXXXXXXXXXX@developer.gserviceaccount.com
  private_key_path ./.keys/XXXXXXXXXXXX-privatekey.p12
```

### テーブル

挿入するテーブルは次のディレクティブで指定する。`table`ディレクティブの代わりに`tables`ディレクティブを用いると、複数テーブルの指定が可能になり、レコードがシャーディングされる。インサート数がGoogle BigQueryの制限にかかってしまう場合に有効だ。

```apache
  project your-project-id
  dataset your_dataset
  table your_table
```

### スキーマ

スキーマはリスト形式でも指定できるが、先ほど使用したJSONファイルを指定するだけで良い。JSONファイルは`schema_path`ディレクティブに指定する。

```apache
  time_format %s
  time_field time
  schema_path ./your_table_schema.json
```

`time_format`では時刻のフォーマットを指定するが、BigQueryには`TIMESTAMP`型は存在しないため、unix epochを指定する。unix epoch形式で挿入しておけばクエリで後から変換が可能だ。時刻のフィールドは`time_field`で指定する。このフィールドもスキーマに含まれている必要がある。

# クエリを発行する

さて、Fluentdによるログの挿入が出来たら実際にクエリを発行してみよう。クエリの発行にはAPIを使用する方法、Webインターフェースを用いる方法、そしてbqコマンドを使用する方法がある。

## Webインターフェースからクエリを発行する

Webインターフェースからクエリを発行するには、テーブルを選択し、右上の`Query Table`ボタンか、`COMPOSE QUERY`からクエリ入力画面を開く。

![Query](https://qiita-image-store.s3.amazonaws.com/0/5218/3b676544-b59c-1cc5-c710-043b534434e0.png)

`Enable Options`から出力先のテーブルを指定したり、キャッシュの有効・無効、クエリの優先度を指定することが出来る。また、右のチェックマークをクリックすることで、クエリを発行することでどれだけのデータ量を処理するかを教えてくれるので、課金される前に確認するようにしよう。

## bqコマンドからクエリを実行する

bqコマンドからクエリを実行するには`bq query`コマンドを用いる。

```bash
$ bq query --project_id your-project-id "SELECT * FROM your_dataset.your_table LIMIT 1000"
```

`--dry_run`オプションを指定することで、実際にクエリを発行する前にクエリの確認を行うことが出来る。

```bash
$ bq query --project_id your-project-id --dry_run "SELECT * FROM your_dataset.your_table LIMIT 1000"
Query successfully validated. Assuming the tables are not modified, running this query will process 0 bytes of data.
```

実行結果は標準出力に出力されるが、`--format`オプションを指定することで、JSON、CSV形式での出力も可能である。

```bash
$ bq query --format cdv --project_id your-project-id "SELECT * FROM your_dataset.your_table LIMIT 1000"
```

フォーマットには現在のところ、次の種類が指定できるようだ。

```
--format <none|json|prettyjson|csv|sparse|pretty>
```

# まとめ

この記事ではGoogle Cloud Platformに登録し、Google BigQueryを使い始め、Fluentdからログを挿入し、実際にクエリを実行するまでを紹介した。実際つらい道のりであったが、まだクエリを実行できただけで、ログ解析の道のりはここからである。

プロダクションのログを用いて検証しているが、ログの挿入、クエリの実行は確かに高速である。TreasureDataと違い、クエリのスケジューリング等は標準で提供されておらず、基本的にはAPI経由で使用することになる。Google Apps Scriptを用いれば、Spreadsheetへの出力や、スケジューリングも行うことが出来る。日本語のドキュメントは殆どないので、Qiitaに記事が増えてくれるとうれしい。