YAPC::Asia前夜祭でOSS開発者としての話をします
俺の考えるISUCON
ISUCONというイベントがある。要するに技術コンテストイベントだ。領域はWebアプリケーションにかかわる全てといってよい。
これがなんなのか、そろそろ一発説明しておくか、という気分にちょっとなったので書く。実は何を隠すこともなく次の出題者なのでいかに出題内容にひっかからないように書くかがちょっと大変かもしれないが、どうせ出題内容とかまだ確定しているわけでもないので、いいや。
ISUCONとは何か
ある日の朝、Webアプリケーションが一式、適当に設定されたサーバごと渡されます。あとベンチマークツールも渡されます。
さて夕方までにこのベンチマークツールの計測するスコアを可能な限り上げてください、そのためなら渡されたサーバ上で何をやっても構いません。ただしベンチマークツールはアプリケーションの動作が変わっていないかどうかチェックするための機構を備えているので、そいつが違反を検出したらスコアは無効です。
これだけです。もちろんコンテストなので詳細は当日朝にレギュレーション文章として渡されます*1が、想定外の事態を防ぎたいだけで、内容としては本当に上のものだけです。
世の中一般におけるWebアプリケーションというやつは通常、Webサーバ、アプリケーションサーバ、アプリケーションサーバ上で動作するプログラムコード、およびデータベースから成ります。データベースは多くの場合はRDBMSですが、まれにNoSQLオンリーという強烈な構成のものも世の中にはあります。あとキャッシュサーバを追加したりもしますね。
ISUCONにおけるWebアプリケーションもだいたいはこういうやつです。学生の頃には自分もRDBMSをさわったりはほとんどしなかったけど、お金を稼ぐためにRDBMSは非常に強力なソフトウェアなので、だいたい働きはじめると使うことになります。
ISUCONにおける「何をやってもいい」というのは本当に何でもよくて、別言語でスクラッチから実装し直そうがサーバソフトウェアの構成を完全に変えようがOSから入れ替えようが、何でもいいです。Webアプリケーションの動作さえ変えなければ。これはそれらしい用語ではブラックボックステストとか言ったりします。
これは要するに「あなたに全てをぶっこわす権利をあげます」ということです。それで勝てるもんなら勝ってください。それを見てみたい。
現代のISUCON
むかしむかし、ISUCONには一人で参加できました。現代のISUCONには一人では参加できません。必ず2人か3人でチームを組む必要があります。なぜか。
極めて簡単で、これは1人で勝てるものではない、ということがやってみて明らかになったからです。1人では絶対に勝てません。本当は2人でもつらくて、これまでの優勝チームは漏れなく3人チームですし、上位入賞チームもほとんどが3人です*2。
むかしむかし、ISUCONは先着申し込み順での決勝1日だけのイベントでした。現代のISUCONは予選をリモートで行い、通過者が会場に集まって決勝を行うことになっています。なぜか。
最初はこんなコンテストにこんなに参加者が集まるとは思っていなかったからです。が、今となっては70以上のチームが参加*3するイベントになりました。他に似たようなイベントがなかったのが目新しいからでしょうか。
一方会場に集まって決勝をやるスタイルはそのままです。これは単純に、そのほうが盛り上がるからです。イベント後に飲みながらお互い何をやったか話し合うのが、もう、めちゃくちゃに面白いからです。
むかしむかし、ISUCONのWebアプリケーション提供はPerl, Ruby, Node.jsのみで行われました。現代のISUCONではPerl, Ruby, PHP, Golang, Python, Java, Node.jsなどから、増えたり減ったりしつついくつも提供されています。
これは主催者が考えるに、スコアが出せるかどうかは言語の問題ではない、と思うからです。どの言語だろうと勝つ人は勝ちます。速い言語遅い言語で勝負がつくようなつまんない問題は過去一度も出されませんでした。
だがしかし、過去4回の優勝チームの使用言語は全てPerlでした。今年はどうかな?
ISUCONの技術
ISUCONでは様々な技術が必要とされます。もちろん、ごく基本的なWebアプリケーションを書く能力は真っ先に求められます。いわゆるWeb一般の知識、HTTPやHTML/Javascript/CSSなどの基本および応用的な知識も必要でしょう。
性能を出すためには各種ミドルウェアを適切に設定する必要もあります。Webサーバ、アプリケーションサーバ、データベース、Key-Value-Storage、OSなどは少なくとも一ヶ所ずつ手を入れることになるでしょう。
ISUCON出題者はナンセンスな設定を参加者に押し付けることはありません。最初からそこそこ動く設定はおそらく行われています。しかしスコアを出すためにはそこに安住できないのはもちろんのことです。
ISUCONで勝とうと思ったらアプリケーションの改造を恐れてはいけません。高パフォーマンスなアプリケーションを作るためには、適切なキャッシュの管理、RDBMSへのクエリの最適化と適切なインデックスの作成、データの持ちかたの変更、その他ありとあらゆる計算量・処理データ量の削減の努力が求められます。
それを最適な形で実現するために、アプリケーションコード以外のあらゆる部分の変更の可能性を検討すると同時に、アプリケーションコードの変更を組み合わせることを考えてください。
現代のISUCONはチーム戦です。厳しい制限時間の中で、2人が同じことをやっている余裕はありません。コミュニケーションをとり、お互いを信頼して作業を進めましょう。
そしてその結果をお互いにチェックしあいましょう。人間はミスをするものです。ミスを相互にカバーできるのがチームです。
ISUCONの勝敗
最終的に、勝敗はつきます。勝敗は大事です。勝つために参加しているのです。
しかし、最も大事なものは勝敗ではありません。決勝なら25以上のチーム、予選なら去年はなんと185ものチームが、同じ時間に、同じ問題に、同じ目的で、最大限集中して取り組んだということが一番大事なことです。
普段我々が手にできない、全く同じ前提条件でのフラットな技術的な議論ができる場が、そこにできるのです。
その意味では、ISUCON参加前と参加後に最も多くのものを持ち帰った人こそが勝者と言えるでしょう。
まとめ
ISUCONは超面白いので、今年のもたぶん面白くなると思うので、みんな参加したらいいと思う。
とはいえ、普段Webサービスを開発している人にとっては普段の仕事領域だけど、学生の人達にはRDBMSなんか触ったこともない、という人もいると思う。まあ機会なけりゃそんなもんだよね。しかしRDBMSを使ってみたという経験がこの後に無駄になることは100%ありえないので、ぜひこの機会にやってみてほしい。
ということで機会があります。学生の方はいかがでしょうか。isucon.net
さー今年のISUCONの問題はどうしよっかなー!
Hive GenericUDFの挙動がおかしい場合
コンストラクタで初期化処理をやってしまっていないか気をつけよう。
通常 GenericUDF を継承したUDFを実装する場合、初期化処理は initialize メソッド内で行う。コンストラクタで行ってはいけない。それでも古いほうのHiveのバージョンでは動くケースがあったようだが、Hive 0.13で動かなくなっているケースが出て難儀した。
なにしろデータソースによって動いたり動かなかったりする。普通にカラムの入力データを食わせると動かないが、入力データをHiveQLの中で文字列リテラルで与えると動いたりして地獄だった。動かないというのもエラーになるとかじゃなくて、なんかありえない挙動を示す。正規表現マッチのグルーピングが本来の挙動に対してひとつズレた結果が返ってくるとか。
詳しくはそこまで調べてないのであまり書くと嘘になってしまうが、どうもUDFのインスタンスを生成するタイミングと、それを実際に各Task内でinitializeするタイミングが異なるせいではないか、そしてその間に何らかのシリアライズ・デシリアライズが挟まってるのではないか、という感じ。Serializableでない内部状態をコンストラクタ内で作ってしまうと、それが再現できずに(あるいは中途半端に再現されて)以降の処理がおかしいことになる。
ということで、GenericUDFを継承したUDFを作るとき、初期化処理は initialize() メソッド内でやりましょう。
なおUDFを継承したUDFを作るとき(ややこしいな!)は、そのコンストラクタは実際にはGenericUDFのinitialize()から呼ばれるので、使ってよい、ということでした。id:myui さんに教えてもらった。詳しい人がそのへんに座ってるの超便利……!
Hadoop MapReduceのプロファイルをとる
Hadoop MapReduceのジョブは各スレーブノード上で1タスクごとにJVMが1プロセス起動する形で実行される。
で、それらのアプリケーションのプロファイルをとりたい場合、各々のJVMごとにとることになる。これにはhprofを使う。
Apache Hadoop 2.7.0 – MapReduce Tutorial
この記述に従って以下のプロパティをセットする。ジョブ起動時に指定すれば有効にできる。プロファイラのパラメータはデフォルトがあるという話だったけど、やってみたら指定しないとエラーになった。以下の指定は自分が試したとき用にいじってある。
mapreduce.task.profile=true mapreduce.task.profile.params=-agentlib:hprof=depth=4,interval=100,cutoff=0.001,cpu=samples,heap=sites,force=n,thread=n,verbose=n,file=%s # MRv1の場合は mapred.task.profile などとする
プロファイラのオプション指定についてはOracle Java SE doucumentを見ればいいのかなと思うけど、なんかいまいち詳しくない。自分が設定する分にはIBMのサイトの日本語解説が役にたった。IBMのやつとOracleので違いがあるかもしれないけど、特に気付かなかった。
HPROF: A Heap/CPU Profiling Tool
IBM Knowledge Center HPROF プロファイラーの使用
このオプションを有効にしてジョブを実行すると、ジョブ終了時(正確にはプロファイラを有効にしたtaskの終了時)に profile.out が生成され、そこにプロファイル結果が出力される。これはJobHistoryServerもしくはJobTrackerのWeb UIから該当のattempt(もしくはtask)を選んで*1、そのログを見れば見られる。いちいちHDFSやローカルファイルシステムに見にいかなくてもいいのは便利。
YARN MRv2での問題
MRv1ならこれだけで一発で結果が得られるんだけど、YARN + MRv2で実行しているとこの結果がちゃんと出てこない。起動時のバナーだけ見られたり、あるいはプロファイル結果の出力が途中でブチ切れた状態になっていたりする。
これはhprofの結果出力がJVMがTERMシグナルを受け取って働く終了処理の中で行われるからだ。んでYARNはTERMを送ったら250msだけ待って終了していないとすぐKILLを送る。ので、プロファイラの結果出力の最中でJVMがKILLられて結果出力がちゃんと得られない、ということになる。
この問題についてはApache JIRAにissueがある。Resolvedになっているが該当バージョンは 2.8.0 だ!
[MAPREDUCE-5465] Tasks are often killed before they exit on their own - ASF JIRA
2.8.0 を待っているわけにはいかないので、どうにか設定をいじってattemptのコンテナがKILLられてしまうまで猶予を作ってみる。これは各スレーブノードで設定して NodeManager を再起動する。
<property> <name>yarn.nodemanager.process-kill-wait.ms</name> <value>30000</value> </property> <property> <name>yarn.nodemanager.sleep-delay-before-sigkill.ms</name> <value>30000</value> </property>
ジョブのConfigurationを見てもこの変更が反映されていなくてアレっと思ったが、試してみた限りでは想定通りに動いているようだ。ちゃんとプロファイル結果が最後まで出力されるようになった。
何か副作用があるかどうかというと、もしかして特定の条件で30秒間だけ長く生き残ってしまうattempt containerが出てしまうかもしれないけど……まあ、特に問題でもないでしょう。
めでたしめでたし。
まとめ
Enjoy profiling!
*1:プロファイラが有効になっているものは明らかに実行時間が長くなるからすぐにわかる
"Docker and Fluentd"の話をした
Fluentd meetup と Gophers and Docker-users beer social というのが連続であって、それぞれでほぼ同じ話をした。……んだけど参加者層が違うからまあいいかなと。あと後者は英語だった。いまだに英語でしゃべるのはだいぶ疲れる。
【レポートあり】Fluentd Meetup 2015 夏 - 2015/06/01(月) - dots.[ドッツ]
Gophers & Docker-users Beer Social - Docker Tokyo (Tokyo) - Meetup
資料はこれ。2回目にやるときにFluentd知らない人がけっこういそうだったのでそのあたりを足してあるくらい。
Dockerを使ってアプリケーションをデプロイするときにログの収集に気をつけないといけないんだけど、そもそもその構成を設計するときにどういうパターンが考えられるんだっけとか、それをFluentdでやるとしたらどのような設計を選択できるんだっけとか、そういうことを話題にしている。
そのバリエーションとして、いまpull-reqを出している Fluentd logging driver を使えるようになるといいんだけどね、という話をしていた。あとそろそろ公式の Docker image を作ったらいいのかなあと思ったので、それを出した話も書いてある。
ところで
上述 pull-req はめでたく code review status に進んだので、これと document review が済めばマージされると思う。だいぶ長いことステータスが変わらなかったのが不安でアレだったけど、今ではちょっと安心している。
あと Fluentd meetup の段階では DockerHub でユーザ名がとれてなくてね、という話をしたけど、その後にめでたくユーザ名 tagomoris も organization name fluent も(ついでに fluentd も)とれた。めでたしめでたし。ということでスライド中にURLなどはアップデートしてある。
#norikra meetup 2 をやってきた
いつもながら会場を :DeNA さんに提供していただいて開催できました。本当にありがとうございます。
tagomoris in English — Norikra meetup #2
こっちにも書いたので細かいことは省きますが、今回も100名近く? の人に集まっていただいて、今後の機能どうするかなあとか、実運用こうやってるんだなあとか、一人で開発していると得られない本当に様々なフィードバックをもらえました。ありがとうございます。
自分のスライドはこちらで、最近のアップデートで入った機能などについて話しました。NULLABLEとかは便利だと思いますし、Listener pluginについては簡単なコードで劇的にアーキテクチャが変わる可能性があるので、ぜひ試してみてもらいたいです。
またNorikraは普通に開発を継続していますので、ここが使いづらいとかこの機能が必要だとかあったら、ぜひ issue にお寄せください。みなさんの要望はたぶん一人だけのものではなく、割とみんな欲しいものである可能性が高いです。よろしくおねがいします。
まとめると、今回も楽しかったな!
ソフトウェア技術についてのスライドは英語で書こうぜの話
それだけで潜在読者が軽く10倍以上になるし、自分で英語書く練習にもなるし、1枚のスライドに大量の日本語を書いてしまって見にくくするのも避けられるし、いいことづくめだよ。
まじで。