サイバーエージェントが開催するアドテクコンペに参加してきました!
開催中に過去に参加した人のブログなども参考にしながら作業をすすめていったので、次の参加者のために僕も何かしら残そうということで今回これを書いています 笑 どうせQiitaに書くなら優勝したかったのですが...
競技結果
全8チーム中悔しさの残る2位でした。競技スコアとしては普通にトップだったようなので、プレゼンの準備不足などで越された感じですかね。プレゼンの順番で一番最初を選んだのは失敗でした。まさかDSPの運営とプレゼンの準備を同時にやらされるとは...
インターンの詳細
今回自分が参加した、アドテクコンペというインターンは広告配信のシステムの中でDSP(Demand-Side Platform)という箇所を開発していくインターンでした。広告を主軸の一つとするCAらしいインターンですね。サーバーサイド・MLサイドで二人ずつ、計4名のチームで参加するインターンとなっており、3日間で作ったDSPを最終日に他のチームのDSPと競わせます。その結果得られる競技スコアとプレゼンの点数で勝敗が決まります。
https://developers.cyberagent.co.jp/blog/archives/18636/
詳しい詳細はCAのサイトの方に載っているので、興味がある学生の方などは是非!
事前準備
実は今回のインターンは事前に昨年の参加者からある程度お話を伺うことができました。そこでせっかく参加するのだからと、テンプレートとなるようなコードは書いてから参加することにしました。自分が聞いていたのは以下の3点です。
- Server: 2, ML: 2のチーム構成
- ある程度の速度がサーバーに要求される
- インフラがGCP
これを踏まえてPythonとGolangのサーバーを別で立ててgRPCで結合するというテンプレートをあらかじめ書いてから臨みました。まあ、普通に早いPythonサーバーとかを立てた方が手っ取り早いんですが、GolangとgRPCでやりたかったんです... 学生やりがちじゃないですか... 許してください。
一応コードは載せておきますが、おそらくPythonでSanicとか使った方が早いです。
https://github.com/K-jun1221/gopy-grpc
インフラはGPCとのことなので、普通にKubernetesが使えればいけるやろってことで適当に勉強しておきました。
事前に参加者のブログなどをチェックしていればもっと詳細な情報(要求されている速度)も仕入れることができたのは反省点です。
インターン初日
10:00 ~ 12:00
午前中は主にチームでのアイスブレイクと、DSPとインターンの説明に時間が使われました。SSR, CTR, CPCなど三文字略語のオンパレードでした。三文字略語は数が多すぎるので勘弁してほしいと思うのは僕だけですかね? サーバーサイドの仕様も同時に公開されましたが、ろくに確認もしなかった僕は最終日にツケを払わせられることになります。
12:00 ~ 14:00
仕様を確認しながら相方のサーバーサイドエンジニアと役割分担を決めました。作ってきたテンプレートが使えそうだったので、自分はGolangサーバーとインフラ周りを。PythonサーバーとRedisはやってもらいました。またこの時間でテンプレートの扱い方を軽く説明しました。
- 役割分担
- インフラ構成の決定
14:00 ~ 20:00
この時点で動くことを目標にしていたのですが、Kubernetes周りで少し時間を取られ動かず...
- KubernetesでのDeployFlowを確認
- ML用インスタンスの環境構築
- Redisの接続周り
- Golangサーバーの作り込み
インターン二日目
2日目はサーバーのチューニングに時間を使いたかったので、さっさとDeployしてしまうことに。余談ですが、前日の夜にホテルでPythonのImageをPushしようとしたところ、Wifiが遅すぎたので諦めました 笑
10:00 ~ 12:00
GCPへのDeployを済ませました。前日までの段階でほぼできていたので仕上げて、運営の方に疎通確認をお願い。この時点で一番早く完成したサーバーでした。
- Deploy!!
13:00 ~ 16:00
サーバーのチューニングを行いながら、各種バグや細かいところを調整していきました。Redisのデータが突然吹っ飛ぶのは今でも謎です。計測ツールや監視ツールなどもメンターの方に教えて頂きながら進めていきました。
- 負荷計測ツールの設定
- Redisへのデータ投入スクリプト作成
- ログ監視ツールの設定
16:00 ~ 21:00
負荷計測を行ったところ要求されている水準を満たさなかったので、ログを見ながらひたすら改善を加えていきました。
要求水準が2000pqs/100msですが、この時点でのスコアが500qps/200ms程度と全く及ばず。思いついたものを片っ端から試していきました。
- クライエントサイドロードバランシング (500qps/200ms -> 800qps/200ms)
gRPCの接続部分をロードバランシングするように変更しました。L4バランサーだと適切にバランシングされないので、HeadlessServiceなどを使っていい感じに変更。
これを行ったことにより、改善はしたものの800qps/200nsと要求されている2000qps/100nsには程遠い数値。インフラの制限は特になかったのでPodを無限に増やしても良かったのですが、それでは面白くないので(というよりただただチューニングがしたかった)さらに施策を検討することに。ちなみにPodの数は10で固定しました。
- SideCar (800qps/200ms -> 1600qps/120ms)
PythonサーバーとGolangサーバーの物理的な距離を近くするためにSideCar構成に変更しました。ログを見るとやはりgRPCの通信部分で時間がかかっているようなのでLocalhostでアクセスすることに。これによりスコアも目に見えて改善し、スコアのばらつきもなくなりました。要求されている性能まであと少し!
- 標準入力 (1600qps/120ms -> 3000qps/70ms)
最後のひと押しは本当に時間がかかりました。なにぶんKubernetesが初心者でわからんものなので、メモリやGPUがどの程度使われているかの確認方法もわからないし、割り当て方法もわからないとKubernetes何もわからん状態に突入。Redisの問題かと思って、MemoryStoreを利用するように変更しても速度は改善せず。2日目深夜4:00に悩みに悩んでいたところ、そういえばメンターの人が標準入力がかなり遅いとおっしゃっていたのを思い出して、標準出力を切ってみたところ、速度が3000qps/70msと嘘みたいに改善... おかげで最終日を前に睡眠を取ることができました 笑
インターン最終日
目標としていたサーバーのチューニングまで何とか終わらせることができたので、運営や安定性を念頭に考えてサーバーを開発していきます。
10:00 ~ 12:00
DPSの以下のような式の時間ブーストなども考えていたのですが、相方が数学的に検証したところ逆効果になりそうとのことで却下に。その分サーバーの監視方法や、Green Blue Deployのダウンタイムの検証などに時間を回していました。
CTR * CPC * 1000 * ( 経過時間% * ( 1 - 残り予算% ) )
- Deployでのダウンタイムの検証
- Redisの監視スクリプトの作成
13:00 ~ 17:00
開発したDSPを用いて運用を開始しました。しかし、開始30分後に僕がエンドポイントを一つ間違えていたという初歩的かつ致命的なミスが発覚します。このため予算残高が全くわからなくなるという状況に見舞われました... 修正のDeployやプレゼンの作成と一番てんやわんやした時間になりました。
結果
結果としては優勝を逃す形になってしまいました。心配していた予算のところはどうやらそこまで問題でなかったらしく、勘で調整した予算が奇跡的にうまくいっていたようでした 笑 競技スコアは一番高かったようなので、やはり準備不足のプレゼンが問題だったのかなと。優勝したチームは順番が一番最後であったこともあってか、プレゼンの質が素晴らしかったです。またこのような機会があった際には絶対に優勝したいです。
追記
ヒダッカソンのほうは個人参加して優勝してきました。