年中アイス

いろいろつらつら

Goでファイルの特定位置から読む

bashで組んでたログの検知スクリプトを、メンテナンス性と拡張性考えてGoで書き直すことにしました。 ファイルを定期的にtailして一定行数読み込んで処理する方式を取っていましたが、最後に読んだとこから後にしたいと調べていたら、Mackerelが似たようなことをやっていたので参考にしました。

go-check-plugins/check-log.go at master · mackerelio/go-check-plugins · GitHub

File.Seek()を使って、ファイルの特定バイトから読むようにできます。 前回読み込んだバイト数を記録しておけば、次はその後から追加されたデータのみを読むことができます。

whence*1が、ぱっと見何かわからなかったのですが、この値によってoffsetの使われ方が変わります。

whence 意味
0 ファイルの先頭からのoffset(先頭からスキップするバイト数)
1 今のSeek位置からのoffset(前回Seekした位置からスキップするバイト数)
2 ファイルの末尾からのoffset(末尾から読む。この場合は負数にしないと読めません)

サンプル

Readだけ動作試してみました。

一部抜粋

data

File seek in Go.

コード

fp, err := os.Open("./data")
if err != nil {
    panic(err)
}
defer fp.Close()

fp.Seek(5, 0)
b, err := ioutil.ReadAll(fp)
if err != nil {
    panic(err)
}
fmt.Printf("offset 5, whence 0: %s\n", string(b))

出力

offset 5, whence 0: seek in Go.

whence=2の時にoffsetをマイナスにしてなくて、読めないなーと勘違いしてました。今回試したのはReadですがWriteも同様に位置指定して書き込めるようです。その際は、os.Open()はReadOnlyなので、os.OpenFile(), os.Create()あたりで書き込める状態で作る必要があります。

*1:whenceあんまり聞かない単語でGoogleに投げると「そこから」と訳されました。どこから?の区分値と捉えると納得

elasticsearch bulkAPIで地味にはまる

elasticsearch 6.3が出たので、データ突っ込んで試してみるかと、index作ってデータを投入することに。 curl叩きながら、確認を進めてましたが、BulkAPIを使って、データを入れるときに、以下のようなエラーが出ました。

{"error":"ActionRequestValidationException[Validation Failed: 1: no requests added;]","status":400}

400でvalidationなので、投げてる側の問題と判断して、BulkAPI用のJSONを眺めてみても、特にJSONとしてもelasticsearchとしても問題なさそう。 判明した原因は、curl-dを使っていたことでした。ドキュメントにも

If you’re providing text file input to curl, you must use the --data-binary flag instead of plain -d. The latter doesn’t preserve newlines.

curlなら--data-binaryを使ってくださいと書いてあり、-dがこの省略形かと勘違いしていました。 create indexや検索など他のエンドポイントだと-dでも通るのでなんでだろうと。

elasticsearchのドキュメントちゃんと書いてあってすごいですね。

参考

hakoに機能追加

ECSを本格利用し始め、デプロイの管理にhakoを使うことにしました。最近ECSに機能追加があり、hako未対応のものを使いたくなったこともあり、hakoに追加してみることにしました。

作った機能

いくつか機能を追加して、プルリク投げてマージされたもの、プルリク中のもの、まだ確認中のものと4つ作っています。必要に応じて順次追加していて、投げれそうなものはPRにしています。主に、internalなmicro serviceを動かすユースケースでECSを使っているので、それに応じたものがあります。

health check grace periodサポート

ECS導入する時に使いたかったので、追加してプルリクしました。さすがにこれは単純な設定値の追加なのですぐマージされました。

github.com

この変更で初めてhakoいじったんですが、Rubocopのエラーに悩まされて、自分で変更した部分がルールに合っていなかったものと、Rubocop自体のアップデートのせいなのかエラーになっていたものとありました。

NLBサポート

gRPCをコンテナにして、ECSで動かす予定で、NLB(Network Load Balancer)をサポートしました。PR反応がないので、とりあえず置いたままです。

github.com

一つのALBに複数のECSサービスを紐付ける

hakoはデフォルトで、設定を書いたymlファイル名のALBを作ります。ただ、開発環境などで、hakoファイルごと(サービスごとに)ALBできるとお金かかるので、ポートを変えて、ALBを指定して同一のALBを使うようにしたものです。ポートが変わるので、internal ALB前提です。

GitHub - reiki4040/hako at feature/add-service-to-exists-elbv2

NLBサポートを先にPR出してるのは、これをNLBにも適用したいためですが、これは方針が本家の人が考えるのと合うのかという点があります。一応自分の環境でもしばらく使ってみてからにしようかなという状態です。

ECSのcontainer health checkのサポート

2018/03にECSに追加されたばかりの機能で、とりあえず使ってみようとのことで実装しました。設定値は反映されることを確認しました。AWS SDKのバージョン上がってます。

GitHub - reiki4040/hako at feature/container_healthcheck

機能追加したhakoを使うためのブランチ

上記をまとめて動作確認する用に、featuresと言うブランチを作って、そこに上記未マージ分をまとめて、バージョン番号変えてgemを作って試しています。自分の変更を入れたgemを作る方法は後述しています。

hakoをいじるにあたってのメモ

Rubyは普段使っていない言語なので、とりあえず自分でhakoのコードいじったときのメモです。

まず、変更したコードの実行について。exe/hakoがあったので、lib/hako/version.rbを変えてとりあえずexe/hako --versionしてみましたが、変更したバージョンは出てきませんでした。

調べてみるとbundlerを使って関連ライブラリを入れて、bundle exec hakoといった形でやる必要があったようで、それをやったらローカルの変更が反映されて実行することができました。

  • bundlerのインストール
gem install bundler
  • 関連ライブラリをローカルの環境(プロジェクト?以下に)インストール
bundle install --path=vendor/bundle --binstubs=bundle_bin

—binstubs=bundle_binはrbenv用っぽいので使わなければ不要かも。

  • bundle execで実行するとローカルの変更分が実行されます。
bundle exec hako —version

これでとりあえず自分でhakoのコードを変更したものをhakoコマンドとして実行できます。 また、hakoはrspecでのテストとrubocopでのコードスタイルチェックが実行できます。

bundle exec rake

最初これやってなくて、テスト未実行状態でした。

自分でgemを作って使う

hakoは、bundle exec rake installでpkg/以下にgemファイルが作られます。 それを対象サーバでgem install --local <gem>とすれば、インストールでき、使用することができます。

これで、自分の環境で変更を入れたhakoを使うことができます。

RubyAWS SDKのリファレンス

Ruby SDKのdocumentがよくわからなくても、aws cliで同様の機能(API)の調べて動かすと、項目とか、実際の値も見れてわかりやすかったです。Rubyに限らず、分からなかったらwas cliで動かしてみるのわかりやすいです。

所感など

普段はGoを書くことが多く、Rubyはツール使ってるぐらいで、作法がよくわからない感じでコード書きましたが、とりあえず欲しい機能は実現はできています。rakeで足回りのことができるようになっていたので、実現したいことのコードを書くぐらいで済んだのかなと思います。

一応本家に取り込んでもらいたいなーとは考えつつ*1自分の環境で使って、使い勝手を確認中です。個人的には、Goでワンバイナリで配布できるようなものを作りたくはありますが、まずhakoを使って運用してみて、どうあるのが自分としては好みなのか(使いやすいのか)考えていこうかなと思ってます。

参考など

*1:あまり環境固有の機能実装はしない