※資料内の参照リンクを選択し閲覧する場合は、ダウンロードをお願いいたします
\積極的に技術発信を行なっております/ ▽ Twitter/COLOPL_Tech https://twitter.com/colopl_tech
▽ connpassページ http://colopl.connpass.com
▽ COLOPL Tech Blog http://blog.colopl.dev
Cloud Profiler でGo アプリケーションのパフォーマンスをお手軽に計測できた
View Slide
自己紹介● 技術基盤本部 第2バックエンドエンジニア部 prizmチーム● 2020年4月 新卒入社2
prizm とは● コロプラで開発している対人や協力などリアルタイム性のあるゲームのためのフレームワーク○ サーバー: いわゆるリアルタイムサーバーを作るためのフレームワーク○ クライアント: リアルタイムサーバーと通信するクライアントの基盤ライブラリ● サーバーが Go で書かれている○ 今回は prizm を用いたアプリケーションのパフォーマンスを計測した話をします3
Contents● Cloud Profiler を使うことになった経緯● プロファイラとは● Cloud Profiler○ 導入方法○ 画面や設定項目の説明○ 良かったところ4
Cloud Profiler を使うことになった経緯● prizm を使ったタイトルのリリースが近づいたため負荷試験を実施→prizm サーバーの CPU 使用率が高い!となった○ 試験対象のタイトルではほぼクライアント間のメッセージを中継しているだけ■ チャットメッセージの NG ワードチェックで正規表現をかけている程度○ メッセージの送受信で CPU を使っているならいいが、他の要因があるなら潰したい● 負荷試験環境で prizm サーバーのパフォーマンスを計測したい→Cloud Profiler というものがあるらしい→Go なら Cloud Profiler を使えるぞ!使おう!5
プロファイラとは● 「プログラムの実行を通して情報を収集することでプログラムの性能を解析する」[1](動的解析)ためのツール○ CPU やメモリなどの資源をプログラムのどこで多く消費しているかが分かる=ボトルネックが分かる● Go には pprof という標準のプロファイラが存在○ サーバーで pprof を扱う場合、計測結果は HTTP サーバーで公開■ クラウド環境で用いる場合はアクセス制御などが必要で、やってやれないことはないが面倒[1] Wikipedia より6HTTP や gRPC などのサービスを提供計測結果を HTTP で公開
Cloud Profiler の紹介● GCP で提供されているプロファイリングサービス○ GCP 以外でも動く(らしい)● Go、Java、Node.js、Python に対応○ (少なくとも Go では)導入も簡単● 統計的にパフォーマンスを計測しているため低オーバーヘッドパフォーマンスを計測・アップロード ブラウザでパフォーマンスを確認7
Cloud Profiler の導入方法● プロファイリングしたいコードに右の赤枠で囲ったコードを追加○ コンフィグ作ってスタートするだけ■ コンフィグの中身は後に説明○ 基本的にはこれだけで OK とドキュメントには書いてある■ しかし GKE 環境の場合これだけでは動かない● GKE 環境で動かす場合、計測対象のプログラムが動く Pod に対してWorkload-identity で Cloud Profiler の権限を付与する必要があるpackage mainimport ("cloud.google.com/go/profiler")func main() {cfg := profiler.Config{Service: "cloud-profiler-test",ServiceVersion: "1.0.1",}if err := profiler.Start(cfg); err != nil {// error handlinglog.Fatal(err)}// 以下サーバーの実装}8
Cloud Profiler がうまく動かなかったら● Cloud Profiler はうまくデータを GCP に送れなくてもプログラムが異常終了せずにそのまま動いてしまう○ プロファイリングに失敗してもサービスは提供したいから?○ 5分程度待って GCP console でデータが出てこない場合トラブルが起きてると思って良さそう● profiler.Config の DebugLogging をtrue にすると動作ログが出力される○ 権限が足りなかったときはこれで気づけましたpackage mainimport ("cloud.google.com/go/profiler")func main() {cfg := profiler.Config{Service: "cloud-profiler-test",ServiceVersion: "1.0.1",DebugLogging: true,}if err := profiler.Start(cfg); err != nil {// error handlinglog.Fatal(err)}// 以下サーバーの実装}9
Cloud Profiler の画面● GCP Console でパフォーマンスを Flame Graph の形式で確認できる○ 縦がコールスタック、横が処理の重さ( CPU 時間の長さなど)を表す○ 横軸はソート済みなので重い処理がどこかすぐに分かる○ pprof で見れる画面とほぼ同じだったりする10
Cloud Profiler の画面● GCP Console でパフォーマンスを Flame Graph の形式で確認できる○ HandlerFunc の下で正規表現のコンパイル が走ってしまっている■ =正規表現のコンパイルをリクエストごとに実行してしまっている■ 実際のサービスで Cloud Profiler を使ったときも正規表現周りで効率の悪い部分を発見した11
Cloud Profiler の画面● 「プロファイルの…」の部分でどの指標を見るかを選べる○ CPU使用率やメモリ使用量など○ 「プロファイルの…」となっているが、おそらく「プロファイルの種類」12
Cloud Profiler の画面と profiler.Configpackage mainimport ("cloud.google.com/go/profiler")func main() {cfg := profiler.Config{Service: "cloud-profiler-test",ServiceVersion: "1.0.1",}if err := profiler.Start(cfg); err != nil {// error handlinglog.Fatal(err)}// 以下サーバーの実装}● サービス(Service)○ プロファイルするプログラムの識別子○ コロプラではサービスを用いて “dev” や “prod” のような環境も分離している○ ドキュメントのサンプルが“myservice” なので○○service にしたくなるけど別にそうする必要はない13
Cloud Profiler の画面と profiler.Configpackage mainimport ("cloud.google.com/go/profiler")func main() {cfg := profiler.Config{Service: "cloud-profiler-test",ServiceVersion: "1.0.1",}if err := profiler.Start(cfg); err != nil {// error handlinglog.Fatal(err)}// 以下サーバーの実装}● バージョン(ServiceVersion)○ 文字通りバージョンを表し、同じサービスでの区別が可能○ コロプラでは同一の{アプリケーション、環境 }での機能追加時にバージョンを上げている○ バージョンアップ前後のパフォーマンス比較に使える■ 画面の「比較対象」から比較先を選べる14
Cloud Profiler の良かったところ● コードへの組み込みが楽○ Google のサポートが手厚い Go の利点■ PHP だとこうはいかずつらい● 簡単に動かせる割に強力○ 「このままでも動くけど効率が悪い」系の問題をすぐに見つけられて良い■ 正規表現のコンパイルを都度走らせる部分の見落としに気づけた● かかる負荷が小さい○ 本番環境でもかけっぱなしで大丈夫なレベル○ メトリクス採取に用いている Prometheus exporter より小さかった● 無料○ 30日間しかデータを保存できないという難点もある■ とはいえ長期的にデータを見たいケースはあんまりない15
まとめ● プログラムのパフォーマンスが気になったときはプロファイラを用いて調査するととても良い● クラウド環境でのプロファイリングは GCP が提供する Cloud Profiler が便利○ お手軽に使える■ 導入も簡単■ 結果の確認も直感的○ オーバーヘッドが小さいので本番環境でも使える○ 無料16
pprof の使い方● 計測対象のプログラムに pprof を使うための数行のコードを追加○ ファイルへ計測結果を出力して pprof に食わせる■ すぐに終了するプログラムで有用○ HTTP サーバーとして計測結果を公開■ サーバーなどの常駐プログラムで有用■ もとのプログラムが HTTP サーバーの場合はハンドラを追加すれば OK● 出力したファイルや HTTP サーバーを用いてパフォーマンスを確認ファイルに出力する方法 HTTPで公開する方法HTTP や gRPC などのサービスを提供計測結果を HTTP で公開17
● プロファイリングの結果は CLI とブラウザで確認できる○ ブラウザで見る場合は以下のコマンドを実行■ go tool pprof -http [host:port] [prof file or http path]Flame GraphCLI のような表示GraphCLI では対話的に操作できる ブラウザでは直感的に可視化pprof のプロファイリング結果を確認する18
pprof をクラウド環境で使うのは少し難しい● サーバーがたくさん動くのでまとめる必要がある● ファイルに出力する場合、ファイルをサーバーから回収する必要がある○ 例えば Kubernetes で動かしている場合 Pod が終了する前に回収する必要がある● サーバーとして公開する場合、アクセス制御が必要○ HTTP サーバーではない場合、ポートをプロファイラ用に開ける必要も○ 開発環境ではまだいいけど、本番環境だと抵抗感が大きい19