Slide 1

Slide 1 text

datadog-pgoを使用して CPUパフォーマンスを最大化する

Slide 2

Slide 2 text

自己紹介 @moko_poi SRE SRE Kaigi 2025 / OpsJAWS 2

Slide 3

Slide 3 text

PGO(Profile-Guided Optimization)とは 実行時のプロファイル情報を活用して、プログラムのパフォーマンスを最適化す る手法 コンパイル時にインライン化(Inlining)などで最適化 CPU使用率の削減 3

Slide 4

Slide 4 text

GolangでのPGOサポート Go 1.21以降、PGOが公式にサポート 1. pprof などを使用して、アプリケーションの実行時のプロファイルを収集 2. pgo フラグを使用して、プロファイルを活用したビルドを実行 go build -pgo=./default.pgo -o optimized_binary ./main.go default.pgo ファイル(プロファイル情報)を使用して最適化されたバイナリを生成 4

Slide 5

Slide 5 text

プロファイルとは アプリケーション実行時の動作情報 例:CPUプロファイル、メモリプロファイルなど runtime/pprof や net/http/pprof を活用 5

Slide 6

Slide 6 text

net/http/pprof でプロファイルを取得 セットアップ import _ "net/http/pprof" /debug/pprof/ アクセスしてプロファイル取得 CPUプロファイルを取得(60秒間) curl -o profile.pprof http://localhost:8080/debug/pprof/profile?seconds=60 pprofで可視化(ブラウザ表示) go tool pprof -http=:8081 profile.pprof Frame Graphについてはこちらがおすすめ->https://deeeet.com/writing/2016/05/29/go-flame-graph/ 6

Slide 7

Slide 7 text

プロファイル取得の難しさ 開発・テスト環境では本番の負荷や利用パターンを再現しづらい 正確なプロファイル取得には本番データが必要 ユーザーの使い方やアクセス集中により、CPUやメモリの負荷が変動し、ホット スポットが変化する 7

Slide 8

Slide 8 text

手動でプロファイルを取得するには、 環境設定やタイミングの調整が必要で、現実的に難しい場合がある... → Datadog Continuous Profiler 8

Slide 9

Slide 9 text

Continuous Profilerとは 本番環境でアプリケーションの CPU、メモリ、I/O の使用状況をリアルタイムに分 析できるプロファイリングツール 手動でのプロファイル取得の手間を削減し、本番環境の負荷を正確に把握できる 9

Slide 10

Slide 10 text

PGOとDatadog Continuous Profilerが組み合わせられれば... 10

Slide 11

Slide 11 text

datadog-pgo Datadog Continuous Profilerを活用して PGO を Go アプリケーションに統合するツール CPU使用率を最大14%削減できる 流れ 1. DatadogからCPU プロファイルを取得 2. default.pgo ファイルとして Go のビルドプロセ スに組み込む 3. アプリケーションの実行パフォーマンスを最適 化 https://github.com/DataDog/datadog-pgo 11

Slide 12

Slide 12 text

実際に使ってみる 12

Slide 13

Slide 13 text

1. 事前準備 Datadog APMとContinuous Profilerの効果を検証するために、以下のリポジトリを参考にして Goアプリケーションとインフラを構築しました。 https://github.com/DataDog/apm-tutorial-golang 13

Slide 14

Slide 14 text

Notes API の動作 メモの一覧取得 curl localhost:8080/notes メモの作成 curl -X POST 'localhost:8080/notes?desc=hello' インフラ 環境: AWS ECS Fargate タスクサイズ:1 vCPU, 2GB メモリ 14

Slide 15

Slide 15 text

2. APMの導入とContinuous Profilerの有効化 GolangでDatadog APMを導入する方法は 2通り あります。 1. 手動インスツルメンテーション コードに直接トレースを埋め込む方法 2. 自動インスツルメンテーション(Orchestrion) 設定のみで自動的にトレースが有効化される方法 今回は、チュートリアルに従い手動で導入しました。 15

Slide 16

Slide 16 text

APMの導入 Datadog APMを手動で導入するには、 dd-trace-go を使用します。 import ( ... "github.com/DataDog/dd-trace-go/ddtrace/tracer" ) func main() { // Datadog APM の初期化 tracer.Start() defer tracer.Stop() ... } 16

Slide 17

Slide 17 text

contribを利用したトレースの自動適用 Datadogの contrib ライブラリを利用すると、対応しているライブラリやフレームワークで は カスタムスパンを挟まなくても、自動で計測 できます。 contrib ライブラリ 対応フレームワーク/ライブラリ net/http Go 標準の HTTP サーバー gorm.io/gorm GORM(GoのORM) gin-gonic/gin Gin(GoのWebフレームワーク) jmoiron/sqlx sqlx(拡張されたSQLライブラリ) 17

Slide 18

Slide 18 text

Continuous Profilerの有効化 導入方法は、APMと同時に profiler.Start() を呼び出すだけです。 import ( "log" "gopkg.in/DataDog/dd-trace-go.v1/profiler" ) func main() { // Continuous Profiler の開始 err := profiler.Start( // Datadog のサービス名を指定 profiler.WithService("notes"), // 収集するプロファイルの種類を指定(デフォルトはCPU & Heap ) profiler.WithProfileTypes( profiler.CPUProfile, // CPU 使用率 profiler.HeapProfile, // メモリヒープの使用量 // 以下はデフォルトでは無効になっているが、必要に応じて有効化可能 profiler.BlockProfile, // Goroutine のブロック時間 profiler.MutexProfile, // Mutex の競合状況 profiler.GoroutineProfile, // Goroutine の実行状態 ), ) if err != nil { log.Fatal(err) } defer profiler.Stop() // メイン処理 18

Slide 19

Slide 19 text

3. 負荷シナリオの作成 本シナリオは ramping-vus(段階的な仮想ユーザー増加) を採用し、以下のような負荷をシ ミュレートします。 時間 仮想ユーザー数 (VUs) 説明 0分 → 1分 0 → 5 徐々に負荷を増加(5 VUs まで) 1分 → 4分 5 → 10 負荷を継続的に増加(最大10 VUs) 4分 → 5分 10 → 0 負荷を減少(クリーンアップ) 19

Slide 20

Slide 20 text

k6 スクリプト import http from 'k6/http'; import { check } from 'k6'; export const options = { discardResponseBodies: true, // 応答ボディを破棄してメモリ消費を抑える scenarios: { contacts: { executor: 'ramping-vus', // 仮想ユーザー数を段階的に増加 startVUs: 0, // 初期VUs stages: [ { duration: '1m', target: 5 }, // 1 分かけて 5 VUs に増加 { duration: '3m', target: 10 }, // さらに 3 分間かけて 10 VUs に増加 { duration: '1m', target: 0 }, // 1 分で負荷をゼロに戻す ], gracefulRampDown: '0s', // VUs を即座に終了 }, }, }; const BASE_URL = ''; export default function () { // 新しいノートを作成 let res = http.post(`${BASE_URL}/notes?desc=MyNote`); check(res, { 'POST /notes status is 201': (r) => r.status === 201, }); // ノート一覧を取得 res = http.get(`${BASE_URL}/notes`); check(res, { 'GET /notes status is 200': (r) => r.status === 200, }); } 20

Slide 21

Slide 21 text

4. datadog-pgo の導入 ステップ 1: APIキーとアプリケーションキーを作成 ステップ 2: 環境変数を設定 export DD_API_KEY="" export DD_APP_KEY="" export DD_SITE="" ステップ 3: default.pgo の生成 go run github.com/DataDog/datadog-pgo@latest "service:notes" ./cmd/notes/default.pgo ステップ 4: default.pgo の自動検出 21

Slide 22

Slide 22 text

5. 検証結果 22

Slide 23

Slide 23 text

before 23

Slide 24

Slide 24 text

after 24

Slide 25

Slide 25 text

改善効果 指標 Before (実行前) After (最適化後) 改善幅 CPU 使用率 22.8% (228mcores) 17.1% (171mcores) 3.7% 削減 最適化後、CPU 使用率が 22.8% → 17.1% へ低減(約 3.7% 削減) 25

Slide 26

Slide 26 text

まとめ 大規模サービスでは、PGOの導入によりタスク数の削減やインスタンスタイプの ダウンサイジングが可能で、リソースの有効活用が期待できる すでに Datadog Continuous Profiler を導入している場合は、追加コストなく簡単に 適用できるため、積極的に活用すべき 26

Slide 27

Slide 27 text

Reference https://go.dev/doc/pgo https://www.datadoghq.com/ja/blog/datadog-pgo-go/ https://docs.datadoghq.com/profiler/guide/save-cpu-in-production-with-go-pgo/ https://github.com/DataDog/datadog-pgo?tab=readme-ov-file https://docs.datadoghq.com/tracing/guide/tutorial-enable-go-aws-ecs-fargate/ https://docs.datadoghq.com/ja/profiler/enabling/go/ 27