Slide 1

Slide 1 text

Cloud Profiler で Go アプリケーションの パフォーマンスを お手軽に計測できた

Slide 2

Slide 2 text

自己紹介 ● 技術基盤本部 第2バックエンドエンジニア部 prizmチーム ● 2020年4月 新卒入社 2

Slide 3

Slide 3 text

prizm とは ● コロプラで開発している対人や協力などリアルタイム性のある ゲームのためのフレームワーク ○ サーバー: いわゆるリアルタイムサーバーを作るためのフレームワーク ○ クライアント: リアルタイムサーバーと通信するクライアントの基盤ライブラリ ● サーバーが Go で書かれている ○ 今回は prizm を用いたアプリケーションのパフォーマンスを計測した話をします 3

Slide 4

Slide 4 text

Contents ● Cloud Profiler を使うことになった経緯 ● プロファイラとは ● Cloud Profiler ○ 導入方法 ○ 画面や設定項目の説明 ○ 良かったところ 4

Slide 5

Slide 5 text

Cloud Profiler を使うことになった経緯 ● prizm を使ったタイトルのリリースが近づいたため負荷試験を実施 →prizm サーバーの CPU 使用率が高い!となった ○ 試験対象のタイトルではほぼクライアント間のメッセージを中継しているだけ ■ チャットメッセージの NG ワードチェックで正規表現をかけている程度 ○ メッセージの送受信で CPU を使っているならいいが、他の要因があるなら潰したい ● 負荷試験環境で prizm サーバーのパフォーマンスを計測したい →Cloud Profiler というものがあるらしい →Go なら Cloud Profiler を使えるぞ!使おう! 5

Slide 6

Slide 6 text

プロファイラとは ● 「プログラムの実行を通して情報を収集することで プログラムの性能を解析する」[1](動的解析)ためのツール ○ CPU やメモリなどの資源をプログラムのどこで多く消費しているかが分かる =ボトルネックが分かる ● Go には pprof という標準のプロファイラが存在 ○ サーバーで pprof を扱う場合、計測結果は HTTP サーバーで公開 ■ クラウド環境で用いる場合はアクセス制御などが必要で、 やってやれないことはないが面倒 [1] Wikipedia より 6 HTTP や gRPC などの サービスを提供 計測結果を HTTP で公開

Slide 7

Slide 7 text

Cloud Profiler の紹介 ● GCP で提供されているプロファイリングサービス ○ GCP 以外でも動く(らしい) ● Go、Java、Node.js、Python に対応 ○ (少なくとも Go では)導入も簡単 ● 統計的にパフォーマンスを計測しているため低オーバーヘッド パフォーマンスを 計測・アップロード ブラウザでパフォーマンスを確認 7

Slide 8

Slide 8 text

Cloud Profiler の導入方法 ● プロファイリングしたいコードに 右の赤枠で囲ったコードを追加 ○ コンフィグ作ってスタートするだけ ■ コンフィグの中身は後に説明 ○ 基本的にはこれだけで OK と ドキュメントには書いてある ■ しかし GKE 環境の場合これだけでは動かない ● GKE 環境で動かす場合、計測対象の プログラムが動く Pod に対して Workload-identity で Cloud Profiler の 権限を付与する必要がある package main import ( "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 handling log.Fatal(err) } // 以下サーバーの実装 } 8

Slide 9

Slide 9 text

Cloud Profiler がうまく動かなかったら ● Cloud Profiler はうまくデータを GCP に 送れなくてもプログラムが異常終了せずに そのまま動いてしまう ○ プロファイリングに失敗しても サービスは提供したいから? ○ 5分程度待って GCP console でデータが 出てこない場合トラブルが起きてると 思って良さそう ● profiler.Config の DebugLogging を true にすると動作ログが出力される ○ 権限が足りなかったときはこれで気づけました package main import ( "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 handling log.Fatal(err) } // 以下サーバーの実装 } 9

Slide 10

Slide 10 text

Cloud Profiler の画面 ● GCP Console でパフォーマンスを Flame Graph の形式で確認できる ○ 縦がコールスタック、横が処理の重さ( CPU 時間の長さなど)を表す ○ 横軸はソート済みなので重い処理がどこかすぐに分かる ○ pprof で見れる画面とほぼ同じだったりする 10

Slide 11

Slide 11 text

Cloud Profiler の画面 ● GCP Console でパフォーマンスを Flame Graph の形式で確認できる ○ HandlerFunc の下で正規表現のコンパイル が走ってしまっている ■ =正規表現のコンパイルをリクエストごとに実行してしまっている ■ 実際のサービスで Cloud Profiler を使ったときも 正規表現周りで効率の悪い部分を発見した 11

Slide 12

Slide 12 text

Cloud Profiler の画面 ● 「プロファイルの…」の部分でどの指標を見るかを選べる ○ CPU使用率やメモリ使用量など ○ 「プロファイルの…」となっているが、おそらく「プロファイルの種類」 12

Slide 13

Slide 13 text

Cloud Profiler の画面と profiler.Config package main import ( "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 handling log.Fatal(err) } // 以下サーバーの実装 } ● サービス(Service) ○ プロファイルするプログラムの 識別子 ○ コロプラではサービスを 用いて “dev” や “prod” の ような環境も分離している ○ ドキュメントのサンプルが “myservice” なので ○○service にしたくなるけど 別にそうする必要はない 13

Slide 14

Slide 14 text

Cloud Profiler の画面と profiler.Config package main import ( "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 handling log.Fatal(err) } // 以下サーバーの実装 } ● バージョン (ServiceVersion) ○ 文字通りバージョンを表し、 同じサービスでの区別が可能 ○ コロプラでは同一の {アプリケーション、環境 } での機能追加時に バージョンを上げている ○ バージョンアップ前後の パフォーマンス比較に使える ■ 画面の「比較対象」 から比較先を選べる 14

Slide 15

Slide 15 text

Cloud Profiler の良かったところ ● コードへの組み込みが楽 ○ Google のサポートが手厚い Go の利点 ■ PHP だとこうはいかずつらい ● 簡単に動かせる割に強力 ○ 「このままでも動くけど効率が悪い」系の問題をすぐに見つけられて良い ■ 正規表現のコンパイルを都度走らせる部分の見落としに気づけた ● かかる負荷が小さい ○ 本番環境でもかけっぱなしで大丈夫なレベル ○ メトリクス採取に用いている Prometheus exporter より小さかった ● 無料 ○ 30日間しかデータを保存できないという難点もある ■ とはいえ長期的にデータを見たいケースはあんまりない 15

Slide 16

Slide 16 text

まとめ ● プログラムのパフォーマンスが気になったときはプロファイラを用いて 調査するととても良い ● クラウド環境でのプロファイリングは GCP が提供する Cloud Profiler が便利 ○ お手軽に使える ■ 導入も簡単 ■ 結果の確認も直感的 ○ オーバーヘッドが小さいので本番環境でも使える ○ 無料 16

Slide 17

Slide 17 text

pprof の使い方 ● 計測対象のプログラムに pprof を使うための数行のコードを追加 ○ ファイルへ計測結果を出力して pprof に食わせる ■ すぐに終了するプログラムで有用 ○ HTTP サーバーとして計測結果を公開 ■ サーバーなどの常駐プログラムで有用 ■ もとのプログラムが HTTP サーバーの場合はハンドラを追加すれば OK ● 出力したファイルや HTTP サーバーを用いてパフォーマンスを確認 ファイルに出力する方法 HTTPで公開する方法 HTTP や gRPC などの サービスを提供 計測結果を HTTP で公開 17

Slide 18

Slide 18 text

● プロファイリングの結果は CLI とブラウザで確認できる ○ ブラウザで見る場合は以下のコマンドを実行 ■ go tool pprof -http [host:port] [prof file or http path] Flame Graph CLI のような表示 Graph CLI では対話的に操作できる ブラウザでは直感的に可視化 pprof のプロファイリング結果を確認する 18

Slide 19

Slide 19 text

pprof をクラウド環境で使うのは少し難しい ● サーバーがたくさん動くのでまとめる必要がある ● ファイルに出力する場合、ファイルをサーバーから回収する必要がある ○ 例えば Kubernetes で動かしている場合 Pod が終了する前に回収する必要がある ● サーバーとして公開する場合、アクセス制御が必要 ○ HTTP サーバーではない場合、ポートをプロファイラ用に開ける必要も ○ 開発環境ではまだいいけど、本番環境だと抵抗感が大きい 19