Slide 1

Slide 1 text

1 2023/05/16 運⽤本部 Necoチーム 池添 明宏 サイボウズ開運研修2023 オブザーバビリティ⼊⾨

Slide 2

Slide 2 text

この講義のコンセプト ▶ 誰に n サイボウズの開発・運⽤・品質保証に関わるメンバー ▶ 何を学んでもらうか n オブザーバビリティの重要性や⽤途 n ログ・メトリクス・トレース n 出⼒する際の決まり事や注意点 n 収集したデータの活⽤⽅法 2

Slide 3

Slide 3 text

オブザーバビリティとは 3

Slide 4

Slide 4 text

4 - オブザーバビリティ・エンジニアリングより オブザーバビリティとは、システムがどのよう な状態になったとしても、どんなに斬新で奇妙 な状態であっても、どれだけ理解し説明できる かを⽰す尺度です。 “ オブザーバビリティ(可観測性)とは

Slide 5

Slide 5 text

オブザーバビリティの実現に必要な物 ▶ データ(テレメトリー)の収集 n 3つの柱:ログ、メトリクス、トレース n 第4の柱:プロファイル ▶ 収集したデータを活⽤するツール n 検索、分析、可視化ツール n アラート通知ツール 5

Slide 6

Slide 6 text

なぜオブザーバビリティが重要なのか ▶ クラウドネイティブ技術の導⼊やマイクロサービス化により システムが複雑化している ▶ デリバリーが⾼速化しシステムの稼働状況の監視やユーザー の利⽤状況の分析がより重要になっている ▶ ユーザー数の増加に伴いパフォーマンスや可⽤性への要件が ⾼まっている ▶ セキュリティ攻撃⼿段が多様化している 6

Slide 7

Slide 7 text

オブザーバビリティは誰のためのもの? ▶ 従来は運⽤チームが主体となって活⽤していた。 ▶ 近年では開発やビジネスでの利⽤が広がっている。 ▶ 将来的にはフロントエンドや品質保証⾯での活⽤も期待。 7

Slide 8

Slide 8 text

8 - ⼊⾨監視より 監視とは役割ではなくスキルであり、チーム内の全員があ る程度のレベルに⾄っておくべきです。 ソフトウェアエンジニアはアプリケーションについて誰よ りも詳しいので、素晴らしいアプリケーション監視の仕組 みをデザインするには最⾼の場所にいるのです。 “ 監視とは役割ではなくスキルである

Slide 9

Slide 9 text

9 ビジネス ü ユーザーの利⽤状況を 分析し、ビジネス上の 意志決定に利⽤ ü 従量課⾦ 運⽤ ü 障害時のアラート ü 問題箇所の発⾒ ü オートスケーリング ü プログレッシブデリバ リーの実現 開発・テスト ü 性能測定 ü デバッグ ü ユーザーの利⽤状況に 応じた機能の改善 ü Flaky Testの発⾒ ü テスト時間の分析 オブザーバビリティの⽤途

Slide 10

Slide 10 text

オブザーバビリティを⽀える3本柱 10

Slide 11

Slide 11 text

オブザーバビリティを⽀える3本柱 ▶ ログ n 発⽣したイベントの詳細情報をタイムス タンプとともに記録 ▶ メトリクス n 統計情報を時系列で記録 ▶ トレース n 呼び出された処理やかかった時間を記録 11 logs metrics traces

Slide 12

Slide 12 text

ログ 12

Slide 13

Slide 13 text

ログ ▶ ログとは n プログラムが何かを実⾏したり、何かが発⽣したときのイベントを記 録するもの ▶ ログの種類 n アクセスログ n アプリケーションログ・システムログ n 監査ログ 13

Slide 14

Slide 14 text

ログの集め⽅ 14 App Container Runtime App Agent Agent ファイルに ログを出⼒ ファイルに ログを出⼒ 標準出⼒/ 標準エラー出⼒ ストレージ に保存 ファイルからログを読む。 必要に応じてローテーション

Slide 15

Slide 15 text

ログのフォーマット ▶ テキスト形式でもよいが、構造化(JSONやlogfmt)しておく と分析しやすくなる。 ▶ メッセージだけでなく、コンテキスト情報を含める n ログレベル、タイムスタンプ、サービス名、ユーザーID n リクエストID, トレースID, 呼び出した関数名やファイルの⾏数 15

Slide 16

Slide 16 text

構造化ログの出⼒例 ▶ JSON形式 16 {"severity":"INFO", "logged_at":"2023-05-15T04:47:09.882Z", "caller":"zap/options.go:212", "message":"finished unary call with code OK", "service":"sample", "utsname":"sample-57cfb5bf75-hbgjr", "grpc.start_time":"2023-05-15T04:47:09Z", "system":"grpc", "span.kind":"server", "grpc.service":"sample.v2beta2.Clusters", "grpc.method":"Join", "grpc.code":"OK", "grpc.time_ms":4.534} time="2023-05-15T04:50:27Z" level=info msg="Reconciliation completed" application=argocd/worker dedup_ms=0 dest-name= dest-namespace=worker dest-server="https://kubernetes.default.svc" diff_ms=49 fields.level=2 git_ms=16 health_ms=4 live_ms=9 settings_ms=0 sync_ms=0 time_ms=125 ▶ logfmt形式

Slide 17

Slide 17 text

ログ出⼒のライブラリ ▶ ファイルや標準出⼒・標準エラー出⼒に出⼒ ▶ 各⾔語で⽤意されているライブラリを使う n Java: logback n Go: zapなど ▶ ログ出⼒はパフォーマンスやファイル書き込みなどで注意す べき点が多いので、⾃作は避けよう。 17

Slide 18

Slide 18 text

ログのレベル ログレベル ⽤途 CRITICAL 即座に終了するような致命的なエラーが発⽣した。 ERROR データベースやファイルに書き込めないなど、リクエストの処理またはプロセス⾃体が続 ⾏不可能になる問題が発⽣した。 スタックトレースとか、ソフトウェアが実⾏したアクションの結果など、問題の診断情報 を⼗分に出⼒する。 WARN 処理を打ち切る必要がなく、今のところ正常に続⾏できるが、将来的に問題につながり得 る事象が発⽣した。将来何か問題があったとき、真っ先に⾒返してほしいログ。 潜在的な問題。リソースがキャパシティに近いとか。何かアクションが必要な物。 INFO 正常な動作の軌跡。サーバが起動したとかリクエストが来たとか。 外界に影響を与える処理は info ログを出す。繰り返し処理の開始・終了時に info ログを 出す。 nice-to-haveなインフォメーション。”Service started”とか”Listening on port 5050”とか。 DEBUG 関数の出⼊りの記録や⽂字列解析の途中結果など、デバッグ⽤の情報。 productionでは通常無効化しておき、問題が起きたときに有効化する。 18

Slide 19

Slide 19 text

いつログを出すか ▶ エラー発⽣時、問題が起きそうなとき、設定の読み込み時、 繰り返しや時間のかかる処理の開始と終了時、なんらかの意 思決定をしたとき、外部サービスの呼び出し時など。 ▶ メトリクスやトレースやプロファイルで出⼒すべき情報を ログに出⼒しない。適切な技術を選択する。 ▶ 不必要なログを出さない。⼤量のログはディスク容量を圧迫 し、検索性能の劣化を引き起こす。 19

Slide 20

Slide 20 text

何をログに出すか ▶ エラーを解決するために必要な詳細情報を出⼒する n ログを読んだエンジニアが何をすればいいのか分かるようにする。 n データを識別するためのID、関数のパラメータ、処理にかかった時間、 リトライ回数、タイムアウト時間、スタックトレースなどを含める。 ▶ 暗号めいたログメッセージは避ける。 n メッセージを読めば発⽣している事象が分かるようにする。 20

Slide 21

Slide 21 text

秘密情報の扱いに注意 ▶ パスワードやトークン、顧客情報、顧客⼊⼒データはログに 出⼒しないこと。 ▶ アクセス元IPアドレス、ユーザーID、ドメインIDなどは⼗分 に注意して扱うこと。GDPRへの考慮も必要となる。 21

Slide 22

Slide 22 text

ログの分析 ▶ Neco n Grafanaで閲覧。LogQLで絞り込みや加⼯が可能。 22

Slide 23

Slide 23 text

LogQLの例 23 {job=~"argocd/.*"} |= "Reconciliation completed" | logfmt | time_ms > 1000 | line_format "{{.application}}: {{.time_ms}}" n {label="value"} でラベルによる絞り込み。=~ で正規表現指定 n |= で grep による絞り込み n logfmt や json で構造化ログをパースして絞り込み n line_format で指定した形式でログを表⽰

Slide 24

Slide 24 text

デモ ▶ ラベルで絞り込んでみよう ▶ grep的にログを検索してみよう ▶ JSON形式とlogfmt形式のログを パースして絞り込んでみよう 24

Slide 25

Slide 25 text

メトリクス 25

Slide 26

Slide 26 text

メトリクス ▶ メトリクスとは n アプリケーションやシステムの挙動に関する様々な統計情報 n ⼀定周期で統計情報を収集し、時系列データとして記録する ▶ メトリクスによって分かること n 現在のアプリケーションの状態 n 過去のある時点でアプリケーションがどのような状態だったか n ある期間内に状態がどのように変化したか 26

Slide 27

Slide 27 text

メトリクス収集の仕組み 27 App Push Gateway Agent OS Exporter App Service Agent Agent ジョブの完了時など にメトリクスをpush サーバーやOSに関する 情報などを収集 ストレージ に保存 周期的(Necoでは30秒)にAPI を実⾏してメトリクスを収集

Slide 28

Slide 28 text

メトリクスのフォーマット ▶ アプリケーションに対してHTTPで"/metrics"のAPIを実⾏す るとテキスト形式のメトリクスを返す ▶ Prometheus形式がデファクトスタンダード n 数多くのOSSがこのフォーマットでメトリクスを出している n DatadogやNew RelicなどのベンダーツールもPrometheus⽅式をサ ポートしている n 各⾔語向けのライブラリが⽤意されている 28

Slide 29

Slide 29 text

Prometheus形式のメトリクス例 29 # HELP http_server_api_requests_total A counter for requests to the wrapped handler. # TYPE http_server_api_requests_total counter http_server_api_requests_total{code="200",method="get"} 157 http_server_api_requests_total{code="500",method="get"} 16 http_server_api_requests_total{code="200",method="post"} 21 http_server_api_requests_total{code="500",method="post"} 3 # HELP http_server_request_duration_seconds A histogram of latencies for requests. # TYPE http_server_request_duration_seconds histogram http_server_request_duration_seconds_bucket{handler="/api/v1/todo",method="get",le="0.25"} 99 http_server_request_duration_seconds_bucket{handler="/api/v1/todo",method="get",le="0.5"} 190 http_server_request_duration_seconds_bucket{handler="/api/v1/todo",method="get",le="1"} 343 http_server_request_duration_seconds_bucket{handler="/api/v1/todo",method="get",le="2.5"} 343 http_server_request_duration_seconds_bucket{handler="/api/v1/todo",method="get",le="5"} 343 http_server_request_duration_seconds_bucket{handler="/api/v1/todo",method="get",le="10"} 343 http_server_request_duration_seconds_bucket{handler="/api/v1/todo",method="get",le="+Inf"} 343 http_server_request_duration_seconds_sum{handler="/api/v1/todo",method="get"} 157.30460066099994 http_server_request_duration_seconds_count{handler="/api/v1/todo",method="get"} 343 ラベル 値 メトリクス名

Slide 30

Slide 30 text

メトリクスとして出⼒する情報の例 ▶ インフラ n コンテナごとのCPUやメモリ、ディスクの使⽤量 n Kubernetesのリソースに関する情報 ▶ アプリケーション n リクエスト数、エラーの数、キャッシュヒット率、CPUやI/Oを利⽤ する処理にかかる時間、リモート呼び出しの処理の回数や時間など n そのほか有⽤な物はなんでも出しておくのがおすすめ 30

Slide 31

Slide 31 text

メトリクス名の付け⽅ ▶ 名前の付け⽅ n https://prometheus.io/docs/practices/naming/ 31 http_server_request_duration_seconds_bucket チーム名や製品名などメトリクスを分類するための プレフィックスを付ける(例: kintone_ap_, mysql_) 必要に応じてサフィックスを付ける 総カウント数→total ヒストグラム→bucketなど 必要に応じて単位を付ける seconds, bytesなど メトリクスの内容を表す 名前を付ける

Slide 32

Slide 32 text

メトリクスに含めるラベル ▶ ラベルはメトリクスを分析する際の切り⼝となる。適切なラ ベルの決定が重要となる。 ▶ 例 n サーバーごとのCPU負荷を分析したい→サーバー名をラベルにする n APIごとの処理時間を分析したい→API名をラベルにする 32

Slide 33

Slide 33 text

ラベル設計の注意点 ▶ ⾼いカーディナリティのラベルを避ける。 n 分析時の性能劣化やデータ容量の肥⼤化を引き起こす可能性がある。 ▶ メトリクスにセンシティブな情報を含めない。 33 http_server_requests_total{method="GET", userid="0000830"} HTTPのメソッドは、GET, POST, DELETEなど決まった ものしかないのでカーディナ リティが低い。 ユーザーIDは⾮常に種類が多く カーディナリティが⾼い。 またセンシティブなデータ。

Slide 34

Slide 34 text

メトリクスの値 ▶ メトリクスの値は単⼀の浮動⼩数点で表現する n 例:リクエスト数、エラー数、処理時間、データサイズ n 静的な情報を表現するときは値に1を指定する。 n ステータスを表現する場合は、1/0でtrue/falseを指定する。 34 kintone_info{version="23.2.0"} 1 kintone_status{status="healthy"} 1 kintone_status{status="unhealthy"} 0

Slide 35

Slide 35 text

35 Histogram 観測値の集計データ 例: HTTPリクエストサ イズや、リクエストの レイテンシー Gauge 現在の値を⽰す 例: ディスクの使⽤量、 CPUの使⽤率 Counter 増加する値を⽰す 例: APIのリクエスト数 の累計、パケットの合 計受信サイズ メトリクスのタイプ

Slide 36

Slide 36 text

メトリクス設計の⽅法論 ▶ RED(Request Rate, Request Error Rate, Request Duration) n リクエストにおけるエラーの割合とかかった時間。 n サービスの利⽤ユーザーへの影響が分かりやすい指標。 ▶ USE(Utilization, Errors, Saturation) n リソースの利⽤率、エラー数、飽和状態の割合。 n データベースやメッセージキューなどに適した指標。 ▶ The Four Golden Signals(Latency, Traffic, Errors, Saturation) n ユーザー向け、⾮ユーザー向けのどちらにも使える指標。 36

Slide 37

Slide 37 text

メトリクスの分析 ▶ NecoではGrafana (VictoriaMetrics)を利⽤してメトリクスの 分析をおこなう。 ▶ 活⽤例 n 特定の条件にマッチしたときにアラートを発⾏ n メトリクスを集計してパフォーマンスの分析 n SLI/SLOの計算に利⽤ 37

Slide 38

Slide 38 text

PromQLの例 38 # APIのリクエストにかかった時間の99パーセンタイルをverbごとに集計 histogram_quantile(0.99, sum by (verb, le) ( rate(apiserver_request_duration_seconds_bucket{}[5m]) ) ) n histogram_quantile で分位数(パーセンタイル)を計算 n sum で合計値を計算 n rate で秒間あたりの変化数を計算

Slide 39

Slide 39 text

デモ ▶ ダッシュボードを⾒てみよう ▶ どんなメトリクスがあるのか⾒てみよう ▶ PromQLでメトリクスを集計してみよう 39

Slide 40

Slide 40 text

トレース 40

Slide 41

Slide 41 text

トレースとは ▶ 呼び出しの記録 n いつ、どの処理が呼ばれ、どれくらいの時間がかかったかの記録 ▶ 分散トレースとは n 複数のコンポーネントにまたがったトレース 41 Nginx Kintone Slash MySQL

Slide 42

Slide 42 text

トレースとスパン 42 Span Nginx Kintone Slash MySQL Span /api Span /auth Span /items Span /auth Span SELECT Span SELECT Span INSERT time Trace スパン 作業や操作の単位 トレース 複数のスパンで構成 されるリクエストの 開始から終了までの単位

Slide 43

Slide 43 text

トレースデータの集め⽅ 43 App Collector App App ストレージ に保存 APIを実⾏してスパン 情報を書き込み トレースIDが同⼀の複数のスパン を1つのトレースとして扱う

Slide 44

Slide 44 text

トレースデータのフォーマット ▶ OpenTelemetry Protocol(OTLP) n https://opentelemetry.io/docs/specs/otel/protocol/otlp/ ▶ 通信にはHTTPまたはgRPCを利⽤。 ▶ 各⾔語向けのOpenTelemetryクライアントライブラリが⽤意 されている。 n https://opentelemetry.io/docs/instrumentation/ 44

Slide 45

Slide 45 text

スパンデータの例 45 { "resource_spans": [ { "resource": { "attributes": [ { "key": "service.name", "value": { "stringValue": "my.service" } } ] }, "scope_spans": [ { "spans": [ { "traceId": "5B8EFFF798038103D269B633813FC60C", "spanId": "EEE19B7EC3C1B174", "parentSpanId": "EEE19B7EC3C1B173", "name": "I'm a server span", "startTimeUnixNano": 1544712660000000000, "endTimeUnixNano": 1544712661000000000, "kind": 2, "attributes": [ { "key": "my.span.attr", "value": { "stringValue": "some value" } サービス名などコンポーネント に固有の情報 トレースやスパンを識別するた めのID、開始時間と終了時間、 その他スパンに関する様々な情 報を含める

Slide 46

Slide 46 text

Attributesに何を含めるべきか ▶ ResourceのAttributes n サービス名やnamespace, バージョン情報など n Conventions n https://opentelemetry.io/docs/specs/otel/resource/semantic_conventions/ ▶ SpanのAttributes n HTTPのステータスコード、メソッド、URL、クライアントIPなど n Conventions n https://opentelemetry.io/docs/specs/otel/trace/semantic_conventions/ 46

Slide 47

Slide 47 text

コンテキストの伝搬 ▶ 分散トレーシングを可能にするためには、コンポーネント間 でコンテキスト(トレースIDなど)を伝搬する必要がある。 ▶ HTTP通信の場合はHTTPヘッダーに、gRPC通信の場合はメ タデータに、コンテキストを埋め込む。 47 context context Nginx Kintone Slash MySQL

Slide 48

Slide 48 text

コンテキストに含める情報 ▶ コンテキストはチームをまたがってやりとりするため、仕様 を定めておくことが⼤事。 ▶ W3C Trace Context n https://www.w3.org/TR/trace-context/ 48 # {version}-{trace_id}-{span_id}-{trace_flags} traceparent: 00-80e1afed08e019fc1110464cfa66635c-7a085853722dc6d2-01

Slide 49

Slide 49 text

サンプリング ▶ すべてのトレース情報を記 録すると量が膨⼤になる。 ▶ ⾼レイテンシーやエラーが 発⽣したものと、正常なト レースからランダムにサン プリングしたものだけを記 録する。 49 https://opentelemetry.io/docs/concepts/sampling/

Slide 50

Slide 50 text

トレースデータの分析 ▶ ボトルネックの検出 ▶ エラーの発⽣箇所の発⾒ ▶ コンポーネント間の依存関係の理解 ▶ ログやメトリクスにトレースIDを埋 め込むことで、データを連係させる こともできる。 50

Slide 51

Slide 51 text

デモ ▶ トレースデータを⾒てみよう ▶ トレースIDを埋め込んだログから トレースデータにジャンプしてみよ う 51

Slide 52

Slide 52 text

まとめ 52

Slide 53

Slide 53 text

まとめ ▶ オブザーバビリティは、運⽤だけでなく開発やビジネスなど 広い分野で重要度が⾼まっている。 ▶ ログ、メトリクス、トレースはオブザーバビリティを向上さ せるための重要な要素である。必要なデータを適切に出⼒し よう。 ▶ 収集したデータを活⽤するためのツールを使いこなそう。 53