weblog of key_amb

主にIT関連の技術メモ

"shove" というシェルスクリプト用のテストツールを作った

CONTENTS:

"shove" とは

こちらです。

github.com

動作イメージとしては README.md にも貼っているスクリーンショットがわかりやすいと思うので、こちらにも貼ります。

成功時:

f:id:key_amb:20160423110652p:plain

失敗時:

f:id:key_amb:20160423110705p:plain

上のようにテストスクリプトを引数に与えて shove コマンドを実行すると、テストを実行して結果を出力してくれます。

Perl Monger のみなさんは「proveっぽい」と思っていただけたでしょうか。
そうです。名前の "shove" は "shell" + "prove" を縮めたものです。

bash だけでなく、ksh, dash, zsh など POSIX を満たしているシェルに対応できるように作りました。
$SHELL 変数の値と異なるシェルでテストを実行したい場合は、 shove -s|--shell シェル という形で指定してください。

Motivation

一言で言って、きっかけは Yak Shaving です。

最初に「シェルスクリプトのテストを書きたい」と思ったのは、PATH 操作ツール sh-pathctl を作ったときで、2週間ほど前のことでした。

pathctl は元々 Bash で使うことしか考えてなかったこともあり*1、最初に見つけたのは Bats でした。作者の sstephensonrbenv の作者でもあり、rbenv も Bats でテストされているようです。

Bats のテストコードでは、@test "description" { ... } で括った ... 部分の Bash コードが1つのテストケースとなり、set -e で実行して 1行でも失敗したら FAIL というものです。

シンプルですが、まあこれで十分かな、という感じで、しばらくこれを使いました。

ところが、その後、思いつきでシェルスクリプトのパッケージ管理システムみたいなものを作りたくなり、実際に今 clenv というリポジトリで作っています。*2
…で、そのテストを Bash 以外でもやりたくなりました。

Bash 以外でも使えるテストツールを探してみたところ、思ったより色々あるようです。見つけたものを下に列挙します。

shUnit2 を少し試してみましたが、なんだか面倒であまり使い続ける気になりませんでした。
Sharness は README を見て、インタフェースにうーんとなってしまいました。

そんなに大したことをしたいわけでもないので、自作でも行けるだろう、と思って作り始め、まあそれなりに使えるものになったかな、というところです。

こんなツールにしたい

設計意図のようなものを下に並べます。

  • テストコードをシェルスクリプトの文法で書きたい
  • テストツールに余計なことをしてほしくない。書いた通りに動いてほしい
  • 関連するテストをグループ化したい

何やってるのかわからなくて学習コストの高いテストフレームワークは嫌だな、と思っています。*3

今のところ、中で少し面倒な変換をしているところもあるのですが、概ね上に挙げたことは実現できているかと思っています。

使い方について

README.md#Usage で一通り網羅できているかと思います。

1つ、具体例を書いてみましょう。
下のようなシェルスクリプトがあったとします。

# add.sh
add() {
  eval tmp=$"$1"
  eval $1=$(expr $tmp + $2)
  unset tmp
}

テストコードは、例えば下のように書けます。

# add.t
. add.sh

var=3
t_ok $var "var"

T_SUB "add A <num>" ((
  add var 3
  t_is $var 6 "3 + 3 = 6"
  add var 5
  t_is $var 11 "6 + 5 = 11"
))

t_is $var 3 "subtest doesn't affect main context"

テストを実行すると下のようになります。

% shove add.t -v
Run tests by /bin/zsh
-------------------------
add.t ...
ok 1 - var
  # add A <num>
  ok 1 - 3 + 3 = 6
  ok 2 - 6 + 5 = 11
  1..2
ok 2 - "add A <num>"
ok 3 - subtest doesn't affect main context
1..3
ok

Test Summary Report
-------------------
All tests successful.
Files=1, Tests=3, Successes=3, Failures=0
Result: PASS

中で何をやっているのか

shove foo.t bar.t を実行すると、下のようなことを実行します。

  • foo.t bar.t を元に、それぞれテストスクリプト$SHOVE_WORKDIR (デフォルトは $HOME/.shove) に生成します。
    • 先頭に以下の2行を足します。
      • . /path/to/shove/lib/t.shrc ... lib/t.shrc にテスト用の関数が定義されています。
      • t_init ... 初期化処理関数
    • 末尾に t_report $datfile という行を足します。$datfile にはテストの総数、成功数、失敗数が記録されます。
    • T_SUB "subtest name" (()) という行を発見すると、テストをグループ化するコードに変換します。
  • 生成したテストスクリプトを実行します。
  • それぞれの $datfile のデータを集計します。
  • 総テスト結果を表示します。

おわりに

自作のテストツール "shove" の紹介をしました。

機会がありましたら、どうぞご利用ください。

また、使ってみて何かトラブルがありましたら、GitHub Issuesなどでお知らせ下さい。

それでは、Have Fun!

*1:今は pathctl のテストも shove で行っています。

*2:clenv については、またその内ブログを書くと思います。

*3:RSpec は難しいですね。。頑張ってちょっとずつ覚えてます^^;

渋谷.rb で "fireap" という pull 型デプロイツールの話をした #shibuyarb

拙作の fireap について、最初のお披露目記事(↓)を書いてから1ヶ月と少しが経ちました。

それから Ruby 系の勉強会に参加する機会を伺っていたのですが、ようやく昨日 4/20 に 渋谷.rb[:20160420] で少し話すことができました。

下がその発表スライドになります。

概要の紹介 + 直近(といっても1ヶ月前)の更新内容をざっくり話した感じです。

以下、前回記事からの差分という形でまとめます。

  • v0.1 〜 v0.4 の更新内容
    • Consul の "service" や "tag", ノード名でタスクの対象を選べるようにしました。
    • config ファイルを毎コマンド実行前にバリデーションするようにしました。おかしな動作をしないように。
    • fireap task というコマンドで config に定義したタスクを一覧表示できるようにしました。
  • ドキュメントを GitHub Wiki に書きました。*1

「最低限必要そうな機能はあって、使ってみるのに足りる情報はある」状態にできているかな、と思っています。

もし試してみて動かないなどあれば、GitHub Issue なりでお知らせください。

渋谷.rb[:20160420] について

初めて Ruby の勉強会に参加しました。

昼間イベントページを見たとき 40 人中 20人弱ぐらいで、過疎らないかちょっと心配だったのですが、出席の人はだいたいいらっしゃっていた様子でした。
アットホームな感じでよかったです。

飲み会に行けなかったですが、若い人から SI 業の方まで色んな方がいらっしゃって、刺激になりました。

今後もときどき、どこかの Ruby の勉強会にお邪魔するかもしれません。

どこかで見かけた際は、よろしくお願いします。

*1:その内 bootie-docs にしようと思ってたけど、結局まだ出来てない。

Bash で pathctl という PATH 操作ツールと bb という簡易ベンチマークツールを書いた

小さな Bashスクリプトを2つ書いたという小ネタです。

使い方

1. pathctl

環境変数に対する push, pop, shift, unshift 操作を提供します。

#!bash
source pathctl.shrc
pathctl_push    /path/to/your-bin  # 末尾に追加
pathctl_unshift /path/to/your-bin  # 先頭に追加
pathctl_pop                        # 末尾から除く
pathctl_shift                      # 先頭から除く

2. bb

簡易ベンチマーク
time コマンドを付けて引数で与えられたコマンドを一定回数実行して結果を出力するだけ。

## 例
$ bb.bash true "test -f bb.bash && echo true" "cat README.md"
Exec 'true' 1000 times ...
real    0m0.037s
user    0m0.016s
sys     0m0.019s
Exec 'test -f bb.bash && echo true' 1000 times ...
real    0m0.044s
user    0m0.023s
sys     0m0.019s
Exec 'cat README.md' 1000 times ...
real    0m1.972s
user    0m0.820s
sys     0m1.111s

source bb.bash すると bb という関数が読み込まれるので、この関数を使っても同じことができます。
回数を変えたいときは loop=$N bb ... で($N は数字)。

pathctl の高速化について

pathctl を作ったきっかけは Qiita に書いたこちらの記事の状況でした。

これを最初に書いた直後に @sonots さんからコメント頂き、「RedHat 系 OS だったら pathmunge という関数があるよ」と知りました。

コードを見てみたところ、 pathctl よりもシンプルで速そうだったので、この方式に修正してみることにしました。

実際どのくらい違うのか測ってみるついでに bb を作った、という経緯です。

結果は こちらのイシュー に書いていますが、下にコピペします。

# pathmunge x 1000
Exec '. tmp/pathmunge.bash && pathmunge foo' 1000 times ...
real    0m0.138s
user    0m0.102s
sys     0m0.031s
# pathctl x 1000 高速化前
Exec '. pathctl.bash && pathctl_push foo' 1000 times ...
real    0m5.024s
user    0m2.249s
sys     0m3.061s
# pathctl x 1000 高速化後
Exec '. pathctl.bash && pathctl_push foo' 1000 times ...
real    0m0.921s
user    0m0.498s
sys     0m0.311s

元々 pathmunge の1/40ぐらいのスループットだったのが、1/7ぐらいまでにはなった、というところです。

機会がありましたら、ご自由にご利用ください。

時系列DBって結局どれがいいんだっけ #TSDB

※4/6 その後調べた情報などを記事末尾に追記

前提となるニーズ

  • サーバの負荷情報とか、アクセス状況のような KPI を取得・保存し、可視化(参照してグラフ化)したい。
  • リアルタイム性が要求される。5分以上前のデータしか見れませんみたいなのはお呼びでない。
  • 古いデータはそんなに精度は気にしないけど、ロングスパンで俯瞰して見れたら便利。

最近はビッグデータ環境の時系列データ解析もビジネスではけっこうニーズがありそうだけど、そっちはもう少し要求が多そう。
ここでは考えないことにする。

選択肢になりそうなもの

  1. 古きよき RRDtool
  2. Elasticsearch + Kibana
  3. Graphite + Grafana
  4. InfluxDB + Grafana 等
  5. Zabbix

他に、現実的には SaaS に任せるという手段もあるだろうけど、そう言うと話が終わってしまいそうなので、ここでは考えないことにする。

以下、順に所感を。

1. RRDtool

枯れてる。
一昔前のデファクトスタンダードの位置にあると思う。

Munin や Cacti などの監視ソフトウェアの多くで採用されているものでもある。

枯れているがゆえに目新しい話題はないが、自前でサーバメトリクス等を可視化する上では、まあまあ妥当な選択肢ではないだろうか。

各種開発言語のライブラリもあるし、GrowthForecast や CloudForecast のような RRDtool をラップしたツールもある。

2. Elasticsearch + Kibana

最近よく聞く組合せ。
Fluentd からデータを流して Elasticsearch + Kibana で可視化する、という例は枚挙に暇がない。
ログデータの可視化ではよく使われていそう。

やりようによってはその他のサーバメトリクスを載せることも可能だろう。

もともと検索エンジンであり、高機能なので、今回のニーズに対してはリッチ過ぎるという感もなくはない。

3. Graphite + Grafana

はてなや Y! で採用されている。

覚えている記事や発表の情報だと、「大規模だとつらそう」という印象。

4. InfluxDB + Grafana 等

一時期盛り上がったのだけど、最近あまり新しい話題を聞かない。
どうなったんだろう、という感じ。

オフィシャルページなど見たところ、開発は活発に続いている様子。
v0.9 でバックエンドの DB が LevelDB から BoltDB になったみたい。

※記事末尾に追記有り。

5. Zabbix

監視で取得したデータをかんたんにグラフ化できる機能があるようだ。
Zabbix は RRDtool ではないらしい。

全体的な補足

職場の Slack の #infra チャンネルでこの辺りのモヤモヤした疑問について、ふわっとつぶやいてみたところ、グラフツールを作ったりチューニングしていた人から、下のようなコメントを頂いた。

InfluxDB に期待したこともありましたが、なかなか難しいですね
描画部分も問題で、Kibana (or Grafana) は表示の時に頑張るので、グラフたくさん並べると遅いとかあったり、
ユースケースで選ぶ必要があると思っています。

その他

"timeseries db" や「時系列 DB」などで検索すると、他のプロダクトもちらほらヒットします。
OpenTSDB なんかは Hadoop と HBase をバックエンドにした大規模分析環境に向いてそうな時系列DBです。

雑なまとめ

決定版はいま無いのかなぁというところです。
ユースケースによる」と言ってしまえばそれまでですが。

NewRelic 等の監視 SaaSAWS の CloudWatch を使っているところもそれなりにあるでしょうし、「ツールが乱立している」とも言えるのかもしれませんね。

SaaS に頼らず自前でやるとして、正直、サーバメトリクス程度なら、そんなに大量になることは(たぶん、ふつう)ないので、どのツールでも工夫次第でなんとかできそうな気もしないでもないです。

使っている監視ソフトウェアが制約条件になるというケースもあるでしょう。
たとえば Zabbix を使っているなら、Zabbix で可視化するのが理に適っていると思います。

監視は監視でやっているけど、別途メトリクスも取りたいような場合は、どれを使うか迷う状況がしばらく続くかもしれません。

求む、決定版。

参考

4/6 追記

その後調べたりして得た情報を追記しておきます。

RRDtool 2.x

RRDtool の作者 Tobias Oetiker が、次の15年も陳腐化しないことを見据えて RRDtool のリニューアルを計画している(していた?)ようです。

こちらは3年前の記事:

イシューで要求を募って、設計をドキュメントに書いている段階のようですが、イシューの最終更新は去年の7月なので、止まっていないか心配です。

InfluxDB のニュース

クラスタリング機能は将来的に Enterprise プランでのみの提供になるとのこと。
スタンドアローン版は OSS としての開発が継続されます。

その他

Git のリポジトリを1コマンドで統合できるシェルスクリプトを書いた

気づいたら GitHubリポジトリが40個以上ありました。
古いものは作りかけだったり、実験して後に放置したものなどがいろいろありました。*1

そのままでも特に困ることはなく、これまで気にすることはなかったのですが、最近ときどき邪魔だなと思うことがあったので、少し整理することにしました。

本当にこれは要らないなと思うものはリポジトリを削除したのですが、一応取っておこうかなと思うものもあったので、リポジトリを統合する方法はないかなと検索してみたところ、下の記事を見つけました。

何度かこの通りにやって上手く行ったのですが、まだいくつか統合したいリポジトリがあったので、1コマンドで統合作業ができるようなスクリプトを書いてみました。

こんな感じで叩きます。

# 親リポジトリのトップディレクトリで
git-merge-repository <統合対象のリポジトリURL> [ディレクトリ(省略可)]

第2引数のディレクトリを省略した場合は、統合対象のリポジトリ名がそのままディレクトリ名になります。

処理の流れは次のような感じ:

  1. 統合対象のリポジトリgit clone
  2. clone したディレクトリに cd して、 git filter-branch でコミット履歴を書き換え
    ... 全てサブディレクトリ下の変更になるように書き換える
  3. filter-branch の終了コードが 0 だったら確認プロンプトを表示。Y なら次に進む。(N なら終了)
  4. git remote set-url で remote origin を親リポジトリの remote origin に変更
  5. git pull --rebase && git push origin master
    ... ここまでで remote リポジトリでは統合完了
  6. cd .. で親リポジトリディレクトリに戻り、clone したディレクトリを rm -rf して git pull する
    => 統合されたディレクトリが fetch & merge される

注意点が2つあります。

  1. Qiita の記事のコメントにもありますが、Macsed では \t がタブとして変換できませんでした。ので、 CMD_SED=gsed とシェル変数(or 環境変数)を付けて実行すれば sed の代わりに gsed を使うようにしました。
  2. Qiita の記事では全てのブランチの履歴を取得する方法も載っていますが、このスクリプトでは HEAD 固定になっています。

実行しているコマンド等、改良の余地はあるかと思いますが、とりあえず自分はこれで困ってないので、このまま紹介しておきます。

また、参考として、実際に動作させた環境情報は、MacOSX El Captain, git v2.5.4 (Apple Git), bash 4.3.x (Homebrew) です。

ユースケースはそんなにないかなと思いますが、機会がありましたらどうぞご自由にご利用ください。

余談

削除と統合の結果、GitHub 上のリポジトリは34個に減りました。

*1:そういうのは BitBucket に置くといいよ、というような話も聞いたことはあります。