きっかけは、所属している若手エンジニアサークル「Cpaw」代表のpallocさんにサイバーエージェントがハッカソン型インターンを実施すると教えていただいたことです。教えてもらわなければチャンスもなかったので、本当に感謝しています。実はインターンは初めてなので不安でしたが、無事選考を通過し参加することが出来ました。
今回は広告ネットワークにおいてDSPと呼ばれるものを作って、利益率や安定性を競うのがテーマです。詳細は省きますが、なかなかおもしろい仕組みになっています。
運営によって割り振られた[サーバ2人+データ分析2人] x 8チームで戦い、なんと準優勝することが出来ました。賞品の文鎮ももらい、こうしてドヤ顔で参加記を書けています。本当に良かった…
普段はピクシブにてバイトなんでもエンジニア(主にPHPでサーバサイド)をやっていますが、その経験により、「実プロダクトとして運用できる?」 「それスケールすんの?」 「可用性は?」 という発想を持てていたのも生きました。お世話になっている皆さんにも感謝です。
最初に分担を決めたのですが、Goをしっかり書けるもう一人にサーバサイドコードの7割くらいをお願いしたことで、私は全体設計とインフラと残りのサーバサイドコードに集中できました。また、データの二人が予測器の製作に特化できるようにするための支援も行いました。
1日目に全体の構成を考えた結果、以下のような構成になりました。今回のコンセプトは「高スケーラビリティ・高可用性な分散型DSP」です。
これをGCE(Google Compute Engine)で展開しました。お金はいくらでも使っていいという話だったので台数を豪勢に20台+1台使いましたが、APサーバを4コアにしたため、トータルでも18000円程度に収まりました。
当時の私は最高に冴えていたのですが、この予算をMaster walletから各APサーバのLocal walletに分散する仕組みはかなり良くて、講評でも高く評価されました。ポイントとしては
- ほぼ無限にスケールする。Masterをさらに階層構造にすることもできる
- 運用中にAPサーバを自由に増減できる
ということがあります。今回はAPサーバ20台とMaster walletサーバ1台の21台で構成しましたが、何台でも増やせます。今回は2000QPSでしたが、リソース利用率は20%程度だったので、この台数で9000QPSくらいまでは耐えられるはずです。それ以上増えても構成は変えずにGCEのコントロールパネルから台数を増やして、デプロイスクリプトを実行するだけの数分で対応できます。今回は十分な余裕を確保したのもあって、正常レスポンスを100.0%のリクエストに対して返すことが出来ました。これは他のチームに比べてもダントツの信頼性だったようで、誇らしいです。
systemdでコア数ぶん(今回は4つ)のAPデーモンとそれに対応するPythonの予測エンジンを立ち上げて、各APデーモンのポート番号を直接ロードバランサに指定することで、APサーバ内にはロードバランサが存在しません。こういったムダをなくした構成により、95パーセンタイルレスポンスタイムを10msまで縮められました。
最初は予測エンジンのレスポンスが遅すぎて困ったのですが、line_profilerというモジュールで行ごとの実行時間を計測してボトルネックを発見し、高速なレスポンスを返せるようになりました。
GCEから呼び出せるStackDriverというモニタリング画面も便利でした。実施時間は13:32 ~ 17:32です。それ以降は気にしないでください。
こちらが本番中の画面です。他チームとの違いとしては、本番中にパラメータを調整できるようにしたことがあります。便利だったコマンドは journalctl -f | grep Win
で、デーモンが標準エラー出力に吐き出したログをリアルタイム監視ができます。このように監視プログラムやデプロイプログラムを複数用意したので、本番中にすぐに異常に気が付けました。
今回学んだこととしては、細かい単位での動作チェックを繰り返すことが大事というのがあります。開発はGitHubを軸に行いましたが、これのwikiにcurlでAPIを叩くテストパターンと想定レスポンスを数パターンずつ記載しました。機能を実装する前に明示しておくことで、チームで理解の齟齬が生じにくくなります。これは後半になってから実施したのですが、役立ちました。
結局時間は全く足りず、2日目の夜にカラオケで1曲も歌わずにひたすら作業をする羽目になりました。とりあえず、徹夜は向いていないことがわかりました。脳が動かなくなって効率が20%くらいまで落ちます。もう1日あれば、少なくとも徹夜はしないで済んだかもしれないです。
デバッグでどうしようもなくなったときや、監視が本当に動いているのか心配になったときには、 tcpdump -w out.pcap
でキャプチャしたデータをscpで手元に転送してWiresharkで見て解決できました。結局本当に流れているデータはキャプチャを見るのが一番確かなので、今後もtcpdumpは積極的に使っていきたいです。
リモートでGitHubのプライベートリポジトリからpullするのには、ssh -A
でエージェント転送するとよいです。自分以外も管理者権限を持っているサーバ上においた秘密鍵をGitHubに登録はしたくないので。 ssh -A
は多段もできるので、適当なAPサーバにsshしてからさらにDBサーバにsshすることもできます。
デプロイはシェルスクリプトで前述のssh -Aを順番に打ってサーバでpullしていくシンプルな方式にしました。今思えば、最後に&をつけて並列にしても良かったかもしれません。また、CircleCIなどの今時の方法を使う手もあったかもしれません。
しかし、当時のベストを尽くせた気はするので満足しています。開催してくださったCAの皆様、素敵な機会を提供してくださってありがとうございました。楽しかったです!