beat-generatorによるBeats開発

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

藤本です。

Elastic{ON}2016はエキサイティングでした。今回はElastic{ON}のBeatsセッションで紹介されていたBeats開発ツールのbeat-generatorを試してみましたのでご紹介します。

概要

BeatsはGo言語で開発された軽量なData Shipperです。様々なデータに対応したBeatsが開発されており、Elastic{ON}2016会期時点では4つのオフィシャルBeats、14つのコミュニティBeatsが公開されています。
Beatsに関しては以下のブログをご参照ください。

Beatsは、公式ドキュメントに開発手順が公開されており、手順に則れば、独自Beatsを開発することができました。ただ、Go言語と英語が分からない私にはハードルが高かったです。

今回はElasticのgithubで公開されているbeat-generatorを利用して、独自Beatsを開発してみましょう。

Beatsの実装の細かいところに関しては以下のブログが非常に参考になります。

Beatsのつくりかた - ヒープとGCを取得するJstatbeatsを実装してみた

上のブログと比較して、beat-generatorによって短縮される箇所は名称の自動補完、必要最小限のテンプレートから開発をスタートできる、パッケージ管理、バイナリ・設定ファイルの生成、といったところでしょうか。

やってみた

今回のサンプルでは、Elasticsearchのインデックスのドキュメント数をカウントするBeatsを開発します。

事前準備

Cookiecutterインストール

Beatsのテンプレート作成にCookiecutterを利用します。

cookiecutterはpipやbrewでインストール可能です。

# pip install cookiecutter

or

# brew install cookiecutter

glideインストール

beat-generatorはパッケージ管理にglideを利用します。

glideはbrewでインストール可能です。

# brew install glide

Beatsテンプレート作成

Beatsの雛形をbeat-generatorからcookiecutterで作成します。

projectnameにDoccountbeat、github_nameにGithub Accountを指定します。

# cookiecutter https://github.com/elastic/beat-generator.git
Cloning into 'beat-generator'...
remote: Counting objects: 157, done.
remote: Compressing objects: 100% (10/10), done.
remote: Total 157 (delta 2), reused 0 (delta 0), pack-reused 147
Receiving objects: 100% (157/157), 25.41 KiB | 0 bytes/s, done.
Resolving deltas: 100% (59/59), done.
Checking connectivity... done.
project_name [Examplebeat]: Doccountbeat
github_name [your-github-name]: s-fujimoto
beat [doccountbeat]:
beat_path [github.com/s-fujimoto]:
full_name [Firstname Lastname]: Shinji Fujimoto

以下のようなファイルが作成されます。
いくつかのファイルが作成されていますが、最低限実装すべきファイルは*を付けた3ファイルだけとなります。
(バージョン情報を埋め込む場合はmain.goにも設定が必要です)

tree doccountbeat
doccountbeat
├── CONTRIBUTING.md
├── LICENSE
├── Makefile
├── README.md
├── beater
│   └── doccountbeat.go *
├── config
│   └── config.go *
├── docs
│   └── index.asciidoc
├── etc
│   ├── beat.yml *
│   ├── doccountbeat.template.json
│   └── fields.yml
├── glide.yaml
├── main.go
├── main_test.go
└── tests
    └── system
        ├── config
        │   └── doccountbeat.yml.j2
        ├── doccountbeat.py
        ├── requirements.txt
        └── test_base.py

7 directories, 17 files

次にパッケージをインストールします。

# cd doccountbeat
# make init
glide update  --no-recursive
[INFO] Downloading dependencies. Please wait...
[INFO] Fetching updates for github.com/elastic/beats.
[INFO] Setting version for github.com/elastic/beats to ae30528774fffa5879392388f651286785118d38.
[WARN] Skipping lockfile generation because full dependency tree is not being calculated
make update
bash ./vendor/github.com/elastic/beats/libbeat/scripts/update.sh doccountbeat github.com/s-fujimoto/doccountbeat ./vendor/github.com/elastic/beats/libbeat
Beat name: doccountbeat
Beat path: ../doccountbeat
Start modifying beat
Update config file
Update fields
fields.yml file is empty. fields.asciidoc cannot be generated.
fields.yml is empty. Cannot generate template.
git init
Initialized empty Git repository in /Users/fujimoto.shinji/Techs/go/src/github.com/s-fujimoto/doccountbeat/.git/
git add .

設定ファイル

BeatsはYaml形式を設定ファイルに利用します。テンプレートのYamlファイルは実行間隔となるperiodのみが設定値に用意されています。

今回のDoccountbeatでは設定ファイルにて、ドキュメント数を取得する対象となるindex名を指定できるようにします。

Yamlファイル

Yamlファイルにフィールドを追加します。

etc/beat.yml

# vi etc/beat.yml
doccountbeat:
  period: 1s
  index: logstash-* // index名となるフィールドを追加

設定ファイルのオブジェクト実装

Yamlファイルとマッピングする構造体にフィールドを追加します。

config/config.go

# vi config/config.go
(略)
type DoccountbeatConfig struct {
    Period string `yaml:"period"`
    Index  string `yaml:"index"` // index名となるフィールドを追加
}

インデックスからドキュメント数を取得する実装を追加

メインとなる処理を実装します。既に実装が用意されていますので、``Run```関数に記録したい値を取得する処理、MapにKey/Valueを追加するだけです。簡単ですね。

beater/doccountbeat.go

# vi beater/doccountbeat.go
(略)
func (bt *Doccountbeat) Run(b *beat.Beat) error {
(略)
++      url := "http://localhost:9200/" + bt.Configuration.Doccountbeat.Index + "/_count" // ドキュメント数を取得するAPIのURLを生成
++      resp, _ := http.Get(url) // localhostのElasticsearchへリクエスト
++      body, _ := ioutil.ReadAll(resp.Body) 
++      js, _ := simplejson.NewJson(body) // go-simplejsonパッケージにてjsonをパース
++      doc_count, _ := js.Get("count").Int() // レスポンスのJsonからcountキーの値を取得

        event := common.MapStr{
            "@timestamp": common.Time(time.Now()),
            "type":       b.Name,
            "counter":    counter,
++          "doc_count":  doc_count, // 取得したドキュメント数をMapに追加
        }
        b.Events.PublishEvent(event)
(略)

コンパイル

設定ファイル、実行ファイルとなるバイナリを生成します。

# make
go build

# ls -l
total 26256
-rw-r--r--  1 fujimoto.shinji  staff         0  3  1 14:41 CONTRIBUTING.md
-rw-r--r--  1 fujimoto.shinji  staff       560  3  1 14:41 LICENSE
-rw-r--r--  1 fujimoto.shinji  staff       893  3  1 14:41 Makefile
-rw-r--r--  1 fujimoto.shinji  staff      1988  3  1 14:41 README.md
drwxr-xr-x  3 fujimoto.shinji  staff       102  3  1 14:41 beater
drwxr-xr-x  3 fujimoto.shinji  staff       102  3  1 15:13 config
-rwxr-xr-x  1 fujimoto.shinji  staff  13398140  3  1 16:38 doccountbeat // 追加
-rw-r--r--  1 fujimoto.shinji  staff      9119  3  1 15:07 doccountbeat.yml // 追加
drwxr-xr-x  4 fujimoto.shinji  staff       136  3  1 15:07 docs
drwxr-xr-x  5 fujimoto.shinji  staff       170  3  1 16:18 etc
-rw-r--r--  1 fujimoto.shinji  staff      1676  3  1 16:25 glide.lock
-rw-r--r--  1 fujimoto.shinji  staff       237  3  1 15:52 glide.yaml
-rw-r--r--  1 fujimoto.shinji  staff       220  3  1 14:41 main.go
-rw-r--r--  1 fujimoto.shinji  staff       398  3  1 14:41 main_test.go
drwxr-xr-x  3 fujimoto.shinji  staff       102  3  1 14:41 tests
drwxr-xr-x  3 fujimoto.shinji  staff       102  3  1 16:38 vendor

動作確認

実行

設定ファイルにindex名、出力先のElasticsearchホストを指定します。

# vi doccountbeat.yml
doccountbeat:
  period: 1s
  index: test
output:
  elasticsearch:
    hosts: ["localhost:9200"]

Kibanaでデータ確認

ラインチャートでデータを確認してみます。

空のインデックスを指定しているので0件のデータがプロットされています。

Visualize_-_Kibana

対象のインデックスにデータを追加します。

# curl -XPOST localhost:9200/test/type -d '{"name":""}'
{"_index":"test","_type":"type","_id":"AVMxNrAwSbtYHrUlXJfi","_version":1,"_shards":{"total":2,"successful":1,"failed":0},"created":true}

Visualize_-_Kibana

doc_countが1となりました。取得したデータが送信されています。

まとめ

いかがでしたでしょうか。
beat-generatorを利用することで、簡単に独自Beatsを実装することができました。必要な設定、必要な処理を実装するだけです。Beats/Logstash => Elasticsearch => Kibanaの組み合わせは少ない実装で本当に多くのユースケースを実現することができます。