おそらくはそれさえも平凡な日々

書籍「Mackerel サーバ監視[実践]入門」を執筆しました

Mackerel サーバ監視[実践]入門

やっと出せました。この本は、僕がはてなに入社してからずっと携わっている、Mackerelというサーバー管理・監視サービスに関する本です。Software Design誌の2015年3月号から2016年11月号まで掲載していた「Mackerelではじめるサーバ管理」という連載の内容をベースに再構成し、加筆、修正をおこなったものです。

連載時は、主に id:stanaka, id:y_uuki と僕の3人で執筆をローテーションし、書籍化にあたっては、id:sugiyama88, id:daiksy, id:a-know と僕の4人で、加筆、修正作業をおこないました。この6人が著者陣となります。連載と書籍化両方に携わっているのは僕だけで、中心的に携わらせてもらいました。

この本を通して、Mackerelの使い方を学ぶとともに、サーバー管理・監視についても学べる内容となっております。Mackerelには無料で使えるTrial/Freeプランがあるので、個人でもお試しできる内容となっています。是非お手にとってご購入いただけると幸いです。

僕にはエンジニアとして「世の中に使われる技術を作りたい」という継続的な目標があります。プログラミング言語開発とかがわかり易い例ではありますが、実際それができる人は一握りです。今回、SaaSであるとは言え、自分が開発に携わったサービスが「書籍化できるようなソフトウェア」になったことは感慨深く思っています。

今回、連載記事から書籍への再構成が一番大変な作業だったように思います。ここではかなり、技術評論社の中田瑛人様に助けていただきました。編集のプロの技を見ました。

去年は「みんなのGo言語」、今年はこの本を共著で書いたので、来年は単著がなんか出せると嬉しいなぁとかおぼろげに思っております。

サブコマンドはUNIX哲学と相反していないのか

「UNIXという考え方」に書かれているUNIX哲学に「各プログラムが一つのことを上手くやる」というのがある。それとサブコマンドは矛盾するんじゃないかと感じていた。一つのプログラムが複数のことを実行できるじゃん、という。

最近は以下のように思うようにあった。

汎用的なコマンドラインツールはグローバルな名前になるので、名前の衝突には気をつける必要がある。今や多くの開発者がコマンドラインツールを書くようになった。

また、コンテキストを同じくした複雑なツール群を提供する場合、名前空間的なものはあったほうが良いのは確かでしょう。例えば git のサブコマンドが全部バラバラのコマンド名だったら発狂してしまう。

最近、僕はGoでツールを書く事が多いが、サブコマンドを採用せずに各々のコマンドでビルドバイナリを作ってしまうと、スクリプト言語と違い、容量も大きくなってしまうという現実問題もある。

なので、サブコマンド自体は許容できるが、サブコマンドを持つツールは、サブコマンド同士の実装が疎になるように設計しないといけない、と思うようになったのが最近の心境です。

UNIXという考え方

完全に自分用に `blog` コマンドを作った

このBlogは Riji という静的ブログツールを使って構築していて、VPSのNginxから配信している。

コンテンツは、BitBucketのprivate repositoryでバージョン管理している。Markdownを書いて、repositoryにcommitしてpushすれば、VPS上で再構築が走って、サイトが更新されるというお手軽具合。

上記のように大分お手軽になっているのだが、最近はそれでも怠惰が過ぎて更新が滞ってるので、blog というコマンドを叩いたら、自動でエディタが立ち上がって、ファイルを保存すると自動でサイトが更新されるようにしてみた。

恐らくそれなりに正確なGoのApacheログパーザーを書いた

https://github.com/Songmu/axslogparser/

下記のようにすれば、ApacheログかLTSVログをよしなにパーズしてくれます。

import "github.com/Songmu/axslogparser"

log, err := axslogparser.Parse(line)

Apacheのログ形式として知られるアクセスログはいろいろな形式があり、正確なパーズが何気に困難であることが知られています。よく使われるのは以下のような形式です。axslogparserはこれらの形式をサポートしています。

フィールドの区切り文字は一般的には半角スペースが使われますが、パーズし易さのためにタブ文字が使われることもあります。現在はcombinedログに独自フィールドがいくつか追加されたものが多く使われているのではないでしょうか。

これらをパーズする正規表現は結構出回っていますが、大体の場合不正確です。特に難しいのが、ダブルコーテーションの扱いで「エスケープされていないダブルコーテーションで囲まれたフィールド」を正規表現で正確に取り出すのは実はかなり難しいです。

Goでも案の定、正確にパーズできるパーザーが見当たらなかったので、カッとなって書きました。結構正確はなず。戦略としては、タイムスタンプの部分まで正規表現でマッチさせて、それ以降は素朴にフィールドを取り出すようにしています。

既存の類似ライブラリは、ログのフォーマットを指定して、それに従ってパーズするようになっているものが多いのですが、指定無しで大体の場合をケアできるようにしたかったのでそのようになっています。そのうちフォーマット指定できるようにするかもしれない。

動機としてはアクセスログを舐めて集計するような処理を書きやすくしたくて作った。はてなのアクセスログはほとんどLTSVになってるんだけど、Mackerelのプラグインで使いたいというのがあって、頑張ってApacheログもケアしたかった。もうちょっとお茶を濁しても良かったと思うんだけど凝り性なのでついついこだわってしまった。

なので、本命としてはLTSVアクセスログなのでみなさんアクセスログをファイルに落とす場合はLTSVを使いましょう。もしくはローカルのファイルシステムにアクセスログを書き込むのはもう古い(適当

ISUCON7開催に寄せて。もしくはISUCON6予選問題作問奮闘記

ISUCON7開催決定 めでたいですね。開催されるかどうかハラハラしていたので、開催が決まって良かったです。

考えてみたら、昨年のISUCONに関して個人ブログの方に何も書いてなかったので書いてみます。書いたら「とにかく辛かった」みたいな話ばかり出てきそうなので、それが影響して今年の問題作成に名乗りを上げる人がいなかったら困るなと思って、書くのを躊躇していた部分もあります。

問題作成することになったきっかけ

2015年末当時の話になりますが、過去3回優勝させてもらっていたので、そろそろお鉢が回ってくるんじゃないかとは思っていました。過去のISUCON優勝者、もしくは上位入賞者を擁する企業の中で、はてなはまだ問題作成をしていなかったからです。

回ってきたら困るな、と思っていたのも事実です。過去の問題作成者に比べると、僕は明らかにエンジニアとしての実力が見劣りするからです。過去の優勝もチームメイトに恵まれていたからです。また、ISUCON3では僕はfujiwaraさんの下、問題作成チームにいたのですが(当時仕事がデスマっていたのでチームにいただけで結局特に作業はしなかった)、その内部事情を知っていて、問題作成がいかに大変かを身をもって知っていたと言うのもあります。

ただ、話をもらったら受けるしか無いだろうな、とも思っていました。

そんな折、忘れもしない2015/11/13(金)にmorisnite #3というISUCON5の打ち上げ的なイベントがあり、そこで案の定 941さんにISUCON6の問題作成の打診を受け、引き受けることにしました。引き受けた理由としては以下の様なものがありました。

その時点で、941さんはpixivさんとの共同作問という座組を考えており、pixivのedvakfさんも問題作成に意欲を示しており、心強く思えました。また、これはあとで決めたことですが、片方が予選、片方が本戦を作って相互にレビューする仕組みも、スケジュールを守れそうだな、と感じたのもあります。

社内で問題作成の協力をまず僕以外のチーフエンジニアに呼びかけたところ、motemenとwtatsuruが両者とも前向きで頼もしい反応を示してくれたのも大きかったです。

また、やはりISUCONに何か返したいという思いもありました。賞金もありますが、何よりISUCONのお陰で僕のことを知ってくれる人も増え、業界内で仕事がしやすくなった部分も少なからずあります。このあたりは、妻にも「ISUCONに色々なものをもらっているのだから返した方がいい」と言ってもらい、背中を押してもらいました。

そういう経緯でISUCON6の問題を作成することになったのです。

本戦と予選、どっちを担当するかは、pixivさんと話し合ったのですが、両者が予選を希望したので、じゃんけんの末はてなが予選を担当することになりました。最初は「7月くらいにはお互い問題を出し合おう」とか言っていた記憶があるのですが、結局お互いギリギリになりました。

作問にあたって

僕はISUCONが「インフラエンジニアのコンテスト」だと言われることに対しては違和感がありました。また、OSやミドルウェアのチューニングだけで勝てると思われてさえいることは明らかな誤解だな、と感じていました。

OSやミドルウェアを適切に素早くチューニングできる能力と、アプリケーションのコードを正確に書き換えられる手の速さが両立してこそISUCONに勝利できると思っていたからです。

僕は、Webアプリケーション開発の現場にいてスキルを磨いてきたので、アプリケーションエンジニアとしての挟持がありました。なので「アプリケーションエンジニアならでは」の問題を作りたいな、と考えていました。

それが結果として、競プロ勢に向いた問題になった部分があるのかもしれません。予想は全くしてませんでしたが。

コンセプト決め

本当は5月のGWくらいにはコンセプトを決めて手を動かし始めたかったんだけど「みんなのGo言語」の執筆もあり、結局スケジュールは後ろ倒し。

「無駄なマイクロサービス」というコンセプトは僕の中に当初からあったのだけど、それだけじゃ足りないな、と思っていたのだけど、7月頭にmotemenがキーワードリンクのコンセプト実装を作ってきて、これはイケそうだということで、それを盛り込むこととしたのです。

結果として以下のようなものをつくることにしました。

作問

当初は以下のような役割分担になっていました。

しかし、7月に前CTOのstanakaの退職を知らされ、motemenがCTOになることが決まり色々それどころじゃなくなり、僕が初期実装も作成することになりました。

7/13(水)-7/15(金)に夏期休暇を取得したのだけど、そこは自腹で渋谷のアパホテルに自主的に缶詰になり、そこから週末にかけて問題を作りました。isupamという雑なスパムチェックマイクロサービスを盛り込むことにも成功し、結構この時点で手応えを感じ始めていました。

8/13(土)-8/15(月)にかけても夏期休暇を取得し、そこではベンチマーカーを作っていました。ベンチーマーカー作成にあたっては、tagomorisさんがISUCON5時に作成したJavaのベンチマーカーフレームワークや、pixivさんの社内ISUCONで使われたベンチマーカーがめちゃくちゃ参考になりました。特にPuerkitoBio/goquery は便利で、これが無かったら、ベンチマーカー作れてたかどうかあやしい。

その翌の8/16(火)が、MSさんに集ってpixivさんに参考実装を解いてもらうリハーサルの日だったのだけど、なんとか雑な準備が終わったのが、本当にその日の未明でした。それがこの日

リハーサルでは「ちょっとボリューム少ないんじゃないか」という指摘も受け、ボリューム増やすかどうか迷ったんだけど、そのままで行くことにした。その時点ではPerlの参考実装しかなかったため、参考実装のコードを固めて、他の言語に早めに移植を開始したかったというのが一番の理由。最終的に、多言語実装との間での調整に苦しむことになるので、ここで色気を出さないでよかった。

他言語実装・インフラ準備・最終調整

8月末からの1ヶ月が最終調整期間。競技用ポータルサイトはmotemenが作ってくれて助かった。僕は、ベンチマーカーをスケールさせるためのワーカーシステムとそのポータルへの繋ぎこみをやっていた。この辺の仕組みは結構よくできたと思っていて、pixivさんも本番に流用してくれました。ワーカーシステムがなんとなくできたのが、9/4(日)くらい。

多言語移植は、pixivさんや社内のエンジニアがかなり協力してくれて助かりました。多言語実装、大変に思われるかもしれないし、実際大変なんだけど、複数人の僕じゃない人が移植することで、ベンチマーカーのバグに色々気づけたのは逆に良かったように感じています。具体的には想定していないレスポンスを受けるとベンチマーカーがクラッシュするといったケースを見つけられました。

最後の1週間は言語実装毎にあまりにも初期スコアがバラつくのに苦しみました。特に言語ごとに正規表現エンジンの性能に差があり、性能の悪い言語だと全然スコアが出ないという問題に苦しめられました。キーワードの初期データは、最初は20000ワードから調整段階で増やしていくことを想定していたのですが、結局7000ワードくらいまで減らすことになりました。

また、競技用データはWikipediaで公開されているデータをサンプリングして利用していたのですが、チェッカー側で誤判定する記号が含まれているワードを丁寧に除外したり、誤検知が起きそうなワードをこれも個別に除外したりと、データセットを作るのにもかなり時間をかけることになりました。抽出スクリプトや検査スクリプトを書きまくり、何度もデータの作り直しやサイズ削減をおこないました。「なんて問題を作ってしまったのだ」とこの時は自分自身を恨みました。

予選直前の9/14(水)-16(金)の期間は丁度京都出張が入っていたので、京都のメンバーと顔を突き合わせて業務時間後に作業ができて助かりました。つまり直前まで調整していた。

とは言え「ISUCON5のデスマよりまし」だと自分に言い聞かせて頑張っていました。最後の数日徹夜とかはありませんでした。もともとISUCON5の問題作成がいかに過酷だったかはkamipoさんから散々聞かされており、それもあって僕は恐怖におののいていたのです。

幸い、インフラ構築は、wtatsuruとy_uukiに任せることができたので助かりました。僕は予選で利用する運営側のサーバーにmackerel-agentを入れる設定を書いたくらい。

オンライン予選の運用

オンライン予選参加チームが317チームだと聞いた時は、覚悟はしていましたが、マジか…、とだいぶくるものがありました。初日と二日目それぞれ150チーム強ときれいに分かれていました。

過去のオンライン予選では、運営がリモートベンチマーカーシステムのスケールに少なからず苦労していたのが見受けられたので、そこの設計と運用、それに加えてそれらのモニタリングをちゃんとやらないといけないと考えていました。

ベンチマーカーノードは単純に

ベンチマーカーノードは1ノード1VM専有とし、起動したら勝手にベンチマーカー群にジョインするようにしました。これにより、Deploy to Azureボタンを押すだけでベンチマーカーのスケールアウトが実現できるようになりました。

システムの構成はざっと以下のような感じです。

[ユーザー] -enqueue-> [競技Portal] <-> [worker群] -benchmark-> [対象サーバー]

ワーカーの滞留の監視

ベンチマーカーの仕組み上、ベンチマーカーが結果の投稿に失敗することがまれに起こりえるようになっていました。それをMackerelで監視し、アラートを運営Slackに流すようにしました。それが以下の様子です。

アラートを検知をトリガーに手動でデータ修正をおこない(これはダサい)、復旧している様子です。

キューの状況のモニタリング

キューの状況を注視し、必要に応じて、ベンチマーカーノードの追加をいました。面白いのでグラフ画像を共有します。下の黄色い部分が稼働中のジョブの、紫の部分が待っているジョブの数になっています。最初は5ワーカーくらいから始まり、最後は20ワーカーくらいになっているのがわかります。

実行ジョブの数

これはモニタリングとはあまり関係ないのですが、実行が終わったジョブのグラフも面白いのでお見せします。初日は7000弱、二日目は8500程度のベンチマークが動いたことが見て取れます。

ワーカー群のCPU利用率積み上げ

これもちょっと面白いのでお見せします。初日と二日目で傾向が違うのが面白いですね。初日は競技開始後にバタバタしていますが、二日目は競技開始直後は穏やかで、その後、各チームがサーバーのセットアップが終わったくらいのタイミングで少しスパイクしているのがわかります。実際二日目のチームのほうがAzureをしっかり予習してきたチームが多かったように感じました。

予選実施

予選当日はとにかくハラハラしました。初日はだいぶ精神的にも実際もバタバタしたのですが、二日目は多少は余裕を持って運営できたように感じています。二日目は、ランチを外に買い出しに行く余裕もありました。運営自体は、wtatsuruと僕がメインでやりましたが、pixivの面々も当日運営部屋にいてくださったのはだいぶ心強かったです。

参加者の皆様も、運営の至らない点がありながらも、丁寧に質問や問い合せをしてくださり、心苦しく感じるところもありましたが、非常にありがたかったです。問題もそれなりに評価していただいて、本当に嬉しかったです。

終わってみると、ほんとうに感謝しか無い。無事に終わってよかった。

とにかくしんどかった。けど良かった

作問中は、下手な問題を出すと、もしくは出題を落とそうものなら業界の信用を失うんじゃないかと不安で仕方がなかった。一般的に問題解く人よりもスキルが上の人が問題を作るものであるはずなのに、なんで自分より圧倒的にスキルが上の人に対して問題を作るのか意味が分からないよ、とか思ったりもしてた。

自宅で作業をする時は、本当に呻き声を上げながら作業をしていた。去年は色々忙しいことが多く、それぞれ辛くなることも多かったので、妻が僕の呻き声ソムリエになった。具体的には、僕の呻き声を聞き分けて僕が何の作業をしているか分かるようになった。例えば以下のような作業。

まあでも良い体験になったのでこういう経験をさせてもらえたことは本当にありがたく思っています。終わって本当に良かった。

謝辞

本当に多くの皆様に協力していただきました。改めて感謝いたします。

ISUCON7

てことで、今年は気楽に参加させてもらおうと思っています。純はてなチームで出ようと思っているので、それで優勝するのが目標です。今年の運営の皆様よろしくおねがいします。がんばってください!

育児休業を取得しています

というエントリを書こうと思っていたら、あっという間に残り数日になってしまった。

てことで、3月頭から今月いっぱい育児休業中です。去年、id:motemenが短期で取得してたので、そういうのもアリか、と思って僕も取得してみることにした。

年子で大変なので何か手伝えればいいなってのは少しあるんですが、どうせあまり戦力にはならないだろうので、折角の機会なので育児経験をしたいって言うのが一番大きなモチベーション。

朝早起きするのは大変ですが、最近忙しさにかまけて怠っていた料理とかを心置きなくできるのが良い。僕の料理は妻とはちょっと趣向が違うので、1歳半を過ぎた娘が食べてくれるか心配だったけど、物珍しさか結構食べてくれるのは嬉しい。

戦力にはあまりなってない気がしますが、どちらかと言うと僕の我儘で育児休業を取得することを許してくれた妻には感謝しています。おかげさまで楽しんでいます。

会社側は、まあ僕がいなくて回るでしょ、って思ってたら案の定回ってるので、まあそりゃそうですよね、という感じです。育休取りやすくて良い会社です。

YAPC::Kansai 2017 OSAKA の前夜祭で話しています

「突撃!隣の開発環境!」というテーマのお話をさせてもらっています。

今は主に以下のような環境で開発をしています。

何のためにバージョンロックをするか

外部ライブラリに依存する時に、どのようにバージョンロックをすべきかどうかという話。僕個人のスタンスです。

基本的には、開発しているものがライブラリであれアプリケーションであれ、 とにかく依存先の最新についていく のが前提で、その前提に立った場合に、上のような考え方になるかな、と思っている。

特にライブラリ作者は依存ライブラリに非互換変更が入って動かなくなったら、頑張って追随するか、依存を切るかをする。これは、ライブラリを公開している上での責務であり、そこで低いバージョンにロックしてしまうのは悪手。(「責務」って書いちゃうと大変な感じしちゃうけど、まあそのへんはベストエフォートで…)

バージョンロックした場合の落とし穴として、古いライブラリに依存し続けるという問題が起きがち。そこは、依存ライブラリを最新に上げても動かせるかどうかのチェックは随時行ったほうがいい。例えばnightly buildなど。

これは理想の話をしていて、なかなか現実思い通りにはいかないものですけれどもね。現実は厳しい。

`map[string]Obj` だと、 `m["hoge"].Field = "hoge"` できない。 `map[string]*Obj` だとできる

理由とかはこの辺。

以前 map[string]Obj 使ってて嵌ったんだよなーと思ったので、書き留めておく。なんか、 m["hoge"] は常に2つ値返してくれて良いんじゃないかとも思うことはある。

2017年年始

あけましておめでとうございます。2016年年末に無事に子供が産まれて、激動の2016年はきれいに締めくくられました。

去年は概ね良い年だった。けど、さすがに公私共に流石に激しすぎた。状況に振り回されて、ずっと半ば混乱状態だったように思う。そんな中、なんとか乗り切ったと思うが、自分の思い描く最低限のパフォーマンスの1/3も出せなかったように感じている。

今年はもう少し落ち着いてペースを取り戻していきたい。立場も少し変えて、少しチームマネージメントからも離れて、プロダクトに注力していけると良いかな、と思っている。

アウトプットよりインプットの割合を増やしたい。英語も流石にどうにかしてちゃんと学ぶかって言う気持ちになっている。