2016 年を Software Engineer として振り返って
はじめに
何か書いておこうかなー、と先々週ぐらいから思いつつ、結局ギリギリになってしまいました(汗)
鼻高々と自慢できるような大きな成果はないかなぁと思うものの、ここで来年以降も定点観測できるように、やはり書いておくことにしました。
概要と、昨年との比較
- 作ったものは小粒なツールが多いですが、こうして振り返ってみると、数はそこそこありました。
- 物によっては、 GitHub で多少、スターがもらえることもありました。
- 昨年は主なものだと 2つ作っていました。数的には、かなり増えたと思います。バリエーションも増えました。
- OSS への貢献も少ししました。
- 昨年はこの手の活動はほぼなかったので、これも進捗したと言えます。
- 勉強会で4回発表しました。これは去年と同じぐらいだと思います。
2016年に作ったもの
ピックアップ
GitHub でスターの多い順 + 作った日時の降順です。
shove (ShellScript)
これはブログ記事でも書きましたが、下の方にもある clenv というツールを作る際に Yak Shaving によって誕生しました。
後に @b4b4r07 氏の enhancd でも使われていることを知りました。
たまにスターがもらえるので、他にも使っている人がいるのかもしれません。
fireap (Ruby)
オンプレミス環境でも使える pull 型デプロイツールを作ろうというモチベーションがあって、作りました。
Consul が必要ですが、 O(log N)
でデプロイできます。
poloxy (Ruby)
時として大量に発報されるアラートを、いい感じにまとめたくて作りました。
こちらはまだプリミティブな機能しか実装できていません。
そのうち熱が高まったら、開発を再開するかもしれません。
grifork (Ruby)
https://rubygems.org/gems/grifork
オンプレミスでも使える Yet Another なデプロイツールです。
pull 型ではありませんが、これも O(log N)
でデプロイできます。
fireap と違って、 Consul に依存していません。
perl5-App-Memcached-CLI
https://metacpan.org/release/App-Memcached-CLI
Redis の redis-cli のように、対話的にも利用できる Memcached 用の CLI ツールです。
最近、職場のどこかでも導入されて、「便利」と言ってもらえました。
関連記事
以上のツールの一部についての関連記事です。
- シェルスクリプト用テストツール "shove" v0.8 までの更新のお知らせ - weblog of key_amb
- memcached-cli の v0.9.4 までの追加機能の紹介 - weblog of key_amb
他に、こんなのも作ってました
- clenv (ShellScript)
- シェルスクリプト用のパッケージ管理ツールです。今年の余暇時間を一番費やしたツールだと思います。もうちょっと頑張ったらだいぶ完成形に近づくかなぁ、というところ。。
- "clenv" というシェルスクリプトのパッケージ管理ツールのようなものを作った - weblog of key_amb
- "clenv" というシェルスクリプトのモジュール管理ツールを引き続き作っている - weblog of key_amb
- "clenv" がそこそこ xxenv っぽく使えるようになってきた - weblog of key_amb
- perl5-App-Memcached-Tool
- https://metacpan.org/release/App-Memcached-Tool
- 上に挙げた App-Memcached-CLI とコードベースは一部共通しますが、こちらは本家 memcached-tool にある機能に絞って実装した CLI ツールです。
- App-Memcached-CLI の方が便利だと思います。
- ruby-stdlogger
- https://rubygems.org/gems/stdlogger
- STDOUT(or STDERR)とログファイルに同時に書き込む Logger をさくっと作りたくて、作った gem です。
- ruby-align
- Ruby のソースコードで
=
等の桁揃えをやりたくて作ったツール - Ruby コード中の "=" や "=>" を整列してくれる "ruby-align" というツールを作った - weblog of key_amb
- Ruby のソースコードで
- ruby-ec2it
- ruby-camel_snake
- https://rubygems.org/gems/camel_snake
- よくある camel case と snake case の変換という車輪をまた再実装してしまったやつ。
- sh-pathctl
- シェルスクリプトで PATH に要素を重複なく追加・削除するためのユーティリティ
- Bash で pathctl という PATH 操作ツールと bb という簡易ベンチマークツールを書いた - weblog of key_amb
- Bash や Zsh で PATH に同じパスが何度も追加されないようにする
- bash-install-github-go-binary
- yamlc (Go)
他に、単機能のシェルスクリプトなど書いて、 GitHub で公開したものもありました。
Contributions
OSS にパッチを送って、取り込まれたものです。
- b4b4r07/enhancd
- 使ってみて困ったことがあったので、2つほど機能追加の PR を取り込んでもらいました:
- ブログに経緯等を書いたもの:
- houseabsolute/DateTime-Format-Strptime
- v1.58 で混入したバグに気づいて、修正パッチを送りました:
- ブログに経緯等を書いたもの: DateTime::Format::Strptime にパッチを送った話 #Perl - weblog of key_amb
もっと小さいパッチを含めると、もういくつかありましたが、ここでは割愛。
勉強会で発表した
大きなカンファレンスには行ってませんが、今年は4回ほど勉強会で発表しました。
以下、発表で使用したスライドを貼っておきます。
以上の発表について、ブログに書いた記事:
- Ginza.rb で "grifork" について発表してきた - weblog of key_amb
- 西日暮里.rb で "poloxy" というアラートシステムの話をした #ninirb - weblog of key_amb
- 渋谷.rb で "fireap" という pull 型デプロイツールの話をした #shibuyarb - weblog of key_amb
- Gotanda.pm #8 で memcached-cli について喋ってきた #gotandapm #memcached - weblog of key_amb
終わりに
OSS 活動と業務時間の使い方について
ここに挙げたツールの内、ごく一部に会社で業務の時間を使って作っていたものもありますが、ほとんどは余暇の時間に作っていたものです。
別に、業務における OSS 開発を禁じられているわけではありませんが、それがメイン業務ではないのと、"業務上、必要な OSS に貢献したり、作ったものを OSS として公開すること" に多くの業務時間を割けていなかった、というところです。
本ブログは個人ブログなので、あまり明白に勤め先と紐付けてはいませんが、幸い今の勤め先はそんなに縛りの厳しい会社ではありませんし、今後はもっと業務と OSS 活動を結びつけて行きたいな、と思っています。
今後、何を作っていくのか?
- 作りかけの物がいくつかあります。サーバサイド周りのユーティリティーです。たぶん、近い内に公開できると思います。
- 作りたいモノのアイディアも少しあって、サーバ運用に関するものです。おそらく、趣味で作ることになるかな、と思っていて、スケジュールは決めていません。
今年はたくさん趣味プログラミングできてよかったです。
お世話になった方々、ブログを読んで下さった方々、ありがとうございました。
来年もよろしくお願いします。
皆様にとって、良い一年となりますように。
脚注
ちょっとしたワークフローエンジンを作るときは Rake で十分だと思う
Ruby なら特に。
Ruby 以外でも、使えるケースはありそう。
Rake には次のような機能が有る。
- タスクの依存関係定義
- 並列実行
- 別のタスクの呼び出し
また、タスクを記述する Rakefile 内では Ruby の文法が使えるので、特に外部の gem を使わなくても、任意のタスクにアドオンで次のような機能を付加できる。
- リトライ
- 終了処理
- 繰り返し
- 複数のタスクを並列実行
以下にサンプルの Rakefile を示す。
※ここでは parallel を使っているが、同じことを parallel を使わずに記述することもできる。
require 'parallel' def invoke(task) Rake::Task[task].invoke end def execute(task) Rake::Task[task].execute end def parallel(*tasks) Parallel.each(tasks) do |task| invoke(task) end end task :all do invoke :prepare begin invoke :start begin invoke :main parallel :job_foo, :job_bar 3.times { |i| p i; execute :job_to_iterate } invoke :finalize end ensure invoke :clean_up end end task default: :all task :prepare do p :prepare end task :start do p :start end task :main do p :main end task :job_foo do p :job_foo end task :job_bar do p :job_bar end task :job_to_iterate do p :job_to_iterate end task :finalize do p :finalize end task :clean_up do p :clean_up end
実行すると、次のようになる。
% rake --trace ** Invoke default (first_time) ** Invoke all (first_time) ** Execute all ** Invoke prepare (first_time) ** Execute prepare :prepare ** Invoke start (first_time) ** Execute start :start ** Invoke main (first_time) ** Execute main :main ** Invoke job_foo (first_time) ** Invoke job_bar (first_time) ** Execute job_foo ** Execute job_bar :job_foo :job_bar 0 ** Execute job_to_iterate :job_to_iterate 1 ** Execute job_to_iterate :job_to_iterate 2 ** Execute job_to_iterate :job_to_iterate ** Invoke finalize (first_time) ** Execute finalize :finalize ** Invoke clean_up (first_time) ** Execute clean_up :clean_up ** Execute default
この Rakefile を見れば、タスクのワークフロー制御がどう記述されているか、プログラマならすぐ理解できるだろう。
今回の :all
のような、ユーザが実行するワークフローを記述したタスクは、外部から直接利用されるメインのタスクと言えるだろう。
メインタスクから呼び出される個々のタスクは、サブタスクということになる。
メインタスク内では、ワークフロー制御と、サブタスクの呼び出しのみを記述すべきだ。
細かな内部ロジックはサブタスク側に記述することで、 Rakefile や実装されているワークフローを見通しよく保つことができるだろうと期待される。
参考
Ginza.rb で "grifork" について発表してきた
10/18(火)に、第40回の Ginza.rb に参加してきました。
初参加でしたが、今回は他にも初参加の方が8名ほど(?)いらしていたようです。
Ginza.rb では、毎回、別々のテーマについて会を催しているようです。
今回は「自分でつくったものを見せてみよう」というテーマでした。
約17名*1の参加者中、ほぼ全員が発表者だったため、1人あたりの発表時間が4分ほどになりました。
LT より短いレベルですね^^;
私は、前記事で紹介した "grifork" について発表してきました。
下がそのスライドです。
だいたい前の記事で紹介した内容の通りですが、v0.2 〜 v0.5 のアップデートについても触れています。
この場でも、かんたんに述べておきます:
- v0.3 gem 化しました。 https://rubygems.org/gems/grifork
- v0.4 Parallel, ssh, rsync のオプションを DSL でカスタマイズできるようにしました。
- v0.5
prepare
,finish
という構文でジョブの最初と最後に実行するタスクを記述できるようにしました。
発表後、Twitter のハッシュタグを見ていたところ、質問らしいツイートがありました。
ツリー構造の場合、途中のノードでデプロイエラーで死ぬと以降のノードがみんな死ぬけどそこどうするんだろう…(´・_・`)? #ginzarb
— おおた@技術書典5 か13 (@ota42y) 2016年10月18日
ごもっともな質問です。
今はある意味、「何もしてない」のですが、エラーがあったらそこでジョブ全体が異常終了するようになってます。
経験上、あるノードでデプロイが止まる場合、そのノードが異常な状態であることが多いと思います。
ので、その場合、そのノードを取り除いて再デプロイする、というのがよくある運用手順になるかな、と思います。*2
grifork の仕組みとして、リトライを入れたり、異常ノードを正常ノードと入れ替えてなるべく多くのノードに対してタスクが完了するように仕向ける、といった実装も可能とは思いますが、その辺りは要望があったら考えようかなぁ、というところです。
何かご意見などありましたら、 GitHub Issue でも Twitter でもお気軽にお寄せ下さい。
自分以外の発表について
一部 Ruby じゃなかったりもあった気がしますが、発表された成果物がバリエーションに富んでいて、刺激を受けました。
特に @joker1007 さん作のものには、ふだんお世話になっているものもありそうです。
rukawa というワークフローエンジンがなぜ rukawa なのか気になりましたが、ちょっと風邪気味だったので懇親会は遠慮しました。
Ginza.rb について
自分がこれまで参加してきた技術系の勉強会は、メインパートが「発表( + LT + 質疑)」で、その後、懇親会という流れのものが多かったです。
が、Ginza.rb は前の回の振り返りや次回内容の検討があって、新鮮でした。
またの機会がありましたら、よろしくお願いします。
ありがとうございました!
ssh と rsync だけで Tree Deploy を実現する "grifork" を作った
- はじめに〜fireap to grifork
- Tree Deploy とは
- grifork: standalone モード
- grifork: grifork モード
- 使い方
- 動作例と実行ログ
- 今後の展望
- 余談〜デプロイの未来について
- おまけ〜grifork の語源
はじめに〜fireap to grifork
約半年前に fireap というデプロイツール(タスクランナー)を作りました。
オンプレミスでも使えて、ノード数 N に対して O(log N)
で動作する、というものです。
が、前提条件として、システム内の全ホストに fireap をデプロイし、また、全ホストで Consul の agent を動かす必要があります。
その辺りが導入障壁になる環境もあるかもしれないな、と思いました。
…で、少し工夫すれば、「デプロイサーバにだけプログラムがあればツリー状にデプロイできる」ものも作れるだろう、と思って作ってみたのが、今回の grifork になります。
Ruby で書いてます。
Tree Deploy とは
なんとなく言葉の雰囲気で意味が伝わる気もしますが、grifork のメカニズムの説明がてらに Cacoo で絵を描いてみました。
対象のホストリストをツリー状のグラフにマッピングして、デプロイをキックするサーバ(以下、デプロイサーバ)から樹状にデプロイを伝播していくと、そういうデプロイのやり方を意図しています。*1
fireap 同様、実行時間は O(log N)
になります。
grifork: standalone モード
grifork には2つ動作モードがありますが、"standaloneモード" が、デプロイサーバでだけ grifork が動けばいいというモードです。
上図のようなツリーに対して、このモードでデプロイを行うと、下の3段階でデプロイが進行します。
(1) デプロイサーバから子ノードに rsync
(2) デプロイサーバから第1世代の子ノードに ssh して、第2世代の子ノードへの rsync をキック
(3) 以下、グラフの葉に達するまで繰り返し
各世代のジョブは全て Parallel *2 で並行に走らせることにしました。
ので、最大並列数が「最大のノードを持つ世代のノード数」になります。
※まだ最大14ノードでしか試してないのですが、ひょっとしたら並列数がすごく大きくなったら何か問題出てくるかも。
grifork: grifork モード
もう1つの動作モードは "griforkモード" と呼んでいます。
こちらは、全ノードで grifork プログラムが動作する前提になっています。
このモードでは、子ノードで grifork を実行することで、再帰的に葉に達するまでタスクを実行します。
こちらの仕組みについても、先ほどのツリーを使って図解します。
(1) デプロイサーバから子ノードに rsync
※ standalone モードと同じなので、図は割愛
(2) 子ノードに ssh して各サブツリーへの grifork をキック
(3) 以下、再帰的にグラフの葉に達するまで繰り返し
standalone モードで grifork や ruby を配布して、grifork モードでアプリケーションをデプロイする、というやり方もできます。
grifork モードの実験をやるときは、実際に standalone モードで全ホストに grifork を配布していました。
使い方
まだ gem にしてないので、今のところ github から clone して使う形になります。
git clone https://github.com/key-amb/grifork.git cd grifork bundle install ./bin/grifork [--[f]ile path/to/Griforkfile]
デプロイツールとして紹介してきましたが、デプロイに限らずリモートでタスクを実行させる用途でも使えます。
Griforkfile という DSL ファイルにタスクの設定を書きます。
デフォルトでカレントディレクトリの Griforkfile
を参照します。
リポジトリの example ディレクトリに、各モードの Griforkfile のサンプルを置いておきました。
動作例と実行ログ
ちょっとしたサンプルとそのログを Gist に貼っておきました。もし、参考にしたい方はどうぞ。
https://gist.github.com/key-amb/94b8113e315ed1ea0e0d98dd6071a9a9
今後の展望
とりあえず動くものはできた、というレベルなので、まだまだ色々使いづらいところや、エラーケースの対処など漏れているところもありそうです。
gem 化した方が使いやすそうだなと思いつつ、まだやっていません。
また、Go で書き直したら、grifork モードのために grifork 自身を配布するのも楽だろうな、と思っています。やる気次第だけど、できれば、やりたい。
以上、 ssh と rsync だけでクラスタに対してツリー状にタスクを伝播するタスクランナー "grifork" を紹介しました。
ご興味ありましたら、お試し下さい。
もし何か要望やバグなどありましたら、GitHub Issue か Pull Request か、Twitter などでお知らせ下さい。
余談〜デプロイの未来について
そもそもの話になりますが、PaaS やコンテナの普及によって、サーバアプリケーションをデプロイする仕組みは変わりつつあるように思います。
単純にファイルツリーを配布するようなデプロイツールのニーズは、相対的に下がっていきそうな気もしています。
おまけ〜grifork の語源
graph + fork (+ griffin)
*1:TwitterがBitTorrentで高速にデプロイしている仕組みについて - Publickey にある "Treedist" ですね
"clenv" がそこそこ xxenv っぽく使えるようになってきた
3日前に上の記事を書いたばかりですが、また少し工事をしたので、自分の中での整理も兼ねて、お知らせ。
"clenv" は私が趣味で作っているもので、シェルスクリプトのパッケージ管理ツールのようなものです。
ソースコードは https://github.com/key-amb/clenv で公開しています。
追加・変更したコマンドについて
clenv help
の実行結果から抜粋します:
clenv init [-] # Bootstrap clenv create [ENVIRONMENT] # Initialize ENVIRONMENT clenv environ # Show current environment clenv environs # List environments clenv global [ENVIRONMENT] # Get/Set global environment clenv local [ENVIRONMENT] # Get/Set local environment clenv exec EXECUTABLE # Execute EXECUTABLE in clenv clenv which EXECUTABLE # Show path of EXECUTABLE in clenv
exec
, which
は v0.3.0 で、後は v0.5.0 以降で追加しました。
clenv create
は v0.4 以前の clenv init-env
相当、 clenv environs
は clenv-list-env
相当です。
clenv environ[s]
は rbenv 等の version[s]
サブコマンド相当で、現在の clenv 的な意味の environment を表示します。
コマンド動作イメージ
上のコマンド群を動かした場合の挙動の例を、軽く以下に示します:
% pwd /home/key-amb/tmp % clenv environ default (set by /home/key-amb/.clenv/environment) % clenv environs * default % clenv create foo Initialized env 'foo' % clenv create bar Initialized env 'bar' % clenv environs bar * default foo % clenv local foo % clenv environ foo (set by /home/key-amb/tmp/.clenv-environment) % CLENV_ENVIRONMENT=bar clenv environ bar (set by CLENV_ENVIRONMENT) % clenv global default (set by /home/key-amb/.clenv/environment)
create 〜 install, environ[s] 〜 version[s] と考えれば、 rbenv 等に慣れた人であれば、そこそこ違和感なく使えるのではないでしょうか。
初期セットアップの改善
clenv のセットアップ方法も、より xxenv に近づき、以前よりシンプルになりました。
README から抜粋します。
# .bash_profile 等に追記する export CLENV_ROOT=$HOME/.clenv export PATH="$CLENV_ROOT/bin:$PATH" eval "$(clenv init -)"
rbenv や plenv の system
に相当するものがないので、 clenv init -
の中で、初回は default
という環境を create
して global
にセットすることにしました。
終わりに
clenv の主に v0.4 〜 v0.6 の変更について、ダイジェストでお伝えしました。
よろしければ、手元に入れて遊んでみて下さい。
Enjoy!