こんにちは。株式会社フィックスポイントのよしだです。
初回のこの記事では、Fixpoint 技術情報ブログの内容についてご紹介します!
続きを読むこの記事は New Relic Advent Calendar 2024 の 19 日目の記事です。
こんにちは。株式会社フィックスポイントの KI です。
この記事では、私が所属しているプロダクトデベロップメント部での New Relic の利用、特に分散トレースについて紹介します。
前半では分散トレースを試験環境に実際に導入し、その結果として監視体験がどのように変化したかを紹介します。
後半では、 Go 言語でのメッセージキューを経由するようなトレースの実装においての工夫や、 OpenTelemetry 導入時のおすすめの設定を紹介します。
2024 年 11 月より、弊社製品 Kompira AlertHub の一部に分散トレースを導入しました。
Kompira AlertHub はマイクロサービスアーキテクチャを採用しており、複数のサービスによって構成されています。
Kompira AlertHub は非常に簡略化すると、以下のようなサービスです。
サービスは上記の「アーキテクチャ図 A」のように実装されています。
※実際にはもう少し複雑ですが、簡略化しています
この図で障害ポイントを考えたとき、下記のように障害を起こす可能性のあるポイントが複数あります。
現在、外形監視によって「Pod A→ Message Queue → Pod B の処理が正常に終了しているか」といった監視をしています。
ですが、障害状態であることが判明した後にも、具体的にどの箇所が要因となって障害が発生しているのかの問題の切り分けが必要です。
「熟練のメンバーなら障害対応が可能でも、新規メンバーでは障害対応が難しい」などの課題があります。
前述の運用課題について、オブザーバビリティを実現することが解決になると考えました。
オブザーバビリティとは、以下のような能力です。
簡単に言うと、私たちが考えるソフトウェアシステムの「オブザーバビリティ」とは、 システムがどのような状態になったとしても、それがどんなに斬新で奇妙なものであっても、(中略)事前にデバッグの必要性を定義したり予測したりすることなく、 システムの状態データのあらゆるディメンションやそれらの組み合わせについてアドホックに調査し、よりデバッグが可能であるようにする必要があります。 もし、 新しいコードをデプロイする必要がなく 、どんな斬新で奇妙な状態でも理解できるなら、オブザーバビリティがあると言えます。
オブザーバビリティ・エンジニアリング 1.2
New Relic は公式サイトにも記載がある通り、オブザーバビリティプラットフォームです。
New Relicオブザーバビリティプラットフォーム | New Relic
New Relic をオブザーバビリティバックエンドとして利用するためには、サービスのデータを New Relic にエクスポートする必要がありますが、エクスポートする手段を New Relic APM か OpenTelemetry から選択できます。
私たちのサービスでは、以下の理由により OpenTelemetry (以下 OTel ) を選択しました。
📖 New Relic と OTel に関しての公式記事は こちら
上述の通り Kompira AlertHub を構成するサービスについて OTel の導入を決め、まずは分散トレースの計装をサービスに追加することにしました。
トレースの計装はまだ本番環境への適用が完了しておらず、試験環境での変化ですが、下記のような変化が見られました。
トレースデータがあると、複数のサービスを経由する一連の処理の中で、特定のサービスでの処理にかかった時間を簡単に求めることができます。
たとえば、あるトレースについて、サービスごとの処理時間は NRQL では以下のようなクエリで求めることができます。
SELECT (max(timestamp + duration.ms) - min(timestamp)) / 1000 as 'sec' FROM Span WHERE service.namespace = '{{ service_namespace }}' FACET trace.id, service.name LIMIT MAX
※タイムスタンプが 13 桁のエポックミリ秒で記録されていたため、 timestamp を 1000 で割って sec に変換しています
https://docs.newrelic.com/jp/docs/logs/ui-data/timestamp-support/
ヒストグラムで集計する場合は以下のようなクエリで求めることができます。
SELECT histogram(sec) FROM ( SELECT (max(timestamp + duration.ms) - min(timestamp)) / 1000 as 'sec' FROM Span WHERE service.namespace = '{{ service_namespace }}' FACET trace.id, service.name LIMIT MAX ) FACET service.name
今回、サービス内で以下のような箇所にのみトレースを計装しました。
これだけでも、以下のようなことがトレースデータを閲覧するだけで分かるようになりました。
🤔「この外部エンドポイントを叩くとどのサービスをどんな順番で処理がされてるんだろう…?」
→「トレースデータによって、どのサービスがどの順番で処理しているか分かる!」
→「Span をちゃんと計装したら、どの処理がどのファイルで実装されているかも分かるので、コードも追いやすい」
🤔「構成図ではこのサービス間は HTTP リクエストで連携しているけど、実際にどんなエンドポイントを叩いているんだろう…?」
→「http ハンドラにもトレースの計装を追加したことで、実際に叩いているエンドポイントが分かる!」
トレースの計装があることで
といった、新規メンバーがサービス理解をするのに非常に頼もしい変化がありました。
このセクションでは、実際にサービスコードに OTel の計装を追加するにあたり、工夫した点や、おすすめの実装を紹介します。
「アーキテクチャ図 A」のように、製品内でメッセージキューを利用しています。
メッセージキューを経由した場合でもトレースを維持するには、トレースコンテキストをメッセージに含める必要があります。
OTel において、トレースコンテキストの伝搬は Propagators API を利用して行いますが、現在この実装として TextMap Propagator が存在します。
The Propagators API currently defines one Propagator type:
- TextMapPropagator is a type that injects values into and extracts values from carriers as string key/value pairs. https://opentelemetry.io/docs/specs/otel/context/api-propagators/
つまり TextMap Propagator は何かというと、Map に対してトレースコンテキストを注入したり、抽出ができるものです。
TextMap Propagator を利用してキューのメッセージにトレースコンテキストを含めると、キューのメッセージでもトレースコンテキストの伝搬ができるようになります。
そこで製品に OTel トレースを導入するにあたり、以下のような Carrier を実装しました。
※ OpenTelemetry トレースを導入したサービスは Go で実装されているため、Go での実装です
メッセージキューとして Azure Service Bus Queue を利用しているため、関数名などに AZServicebus
を含んでいますが、 // type of azsb.Message.ApplicationProperties
というコメントの箇所をお使いのメッセージキューにあわせて変更いただければ他のメッセージキューでも利用いただけると思います。
package tracer import ( "strings" ) func getWithPrefix(prefix, key string, p map[string]any) string { value := p[prefix+key] str, ok := value.(string) if !ok { return "" } else { return str } } func keysWithPrefix(prefix string, p map[string]any) []string { var keys []string for k := range p { if strings.HasPrefix(k, prefix) { keys = append(keys, strings.TrimPrefix(k, prefix)) } } return keys } // NOTE: // AZServicebusMessageCarrier のメソッド経由で // map の値を操作すると強制的に prefix が付与されるので trace context propagation 以外の用途では利用しないこと type AZServicebusMessageCarrier struct { metadata map[string]any // type of azsb.Message.ApplicationProperties } func (c *AZServicebusMessageCarrier) Get(key string) string { return getWithPrefix("otel-", key, c.metadata) } func (c *AZServicebusMessageCarrier) Set(key string, value string) { c.metadata["otel-"+key] = value } func (c *AZServicebusMessageCarrier) Keys() []string { return keysWithPrefix("otel-", c.metadata) } // NOTE: // OpenTelemetry の Propagator(に含まれるCarrier) を Azure Service Bus のため独自定義する // NewAZServicebusMessageCarrier() の返り値を azsb.Message.ApplicationProperties にセットすることで // Azure Service Bus に trace context を埋め込む func NewAZServicebusMessageCarrier(p map[string]any) *AZServicebusMessageCarrier { return &AZServicebusMessageCarrier{metadata: p} }
この実装の工夫ポイントとして、この Carrier を利用した際、トレースコンテキストに otel-
prefix を付与するようにしたことが挙げられます。
Azure Service Bus Queue の ApplicationProperties には、トレースコンテキスト以外のメタデータを格納したいケースがあるかもしれません。
万が一でも他のメタデータを格納した際に衝突しないよう、prefix を付与しました。
補足:
carrier の計装については以下の記事にとても助けられました。ありがとうございます!
https://ymtdzzz.dev/post/opentelemetry-async-tracing-with-custom-propagator/
https://zenn.dev/google_cloud_jp/articles/20230626-pubsub-trace
OTel Resource の Attribute は様々なものがありますが、Service Attribute の service.namespace
を指定しておくと、New Relic での体験が良かったので紹介です。
New Relic の OTel のテレメトリを表示する画面で APM & Services 画面があります。
ここには様々な環境のサービスが全て並ぶかと思います。
この状態では複数の環境に OTel 計装をデプロイした際に、目的のサービスを探すのが大変です。
ですが、 Attribute で service.namespace
を指定しておくと、画面上部の 「+」 から service.namespace
でのフィルタができ、特定の namespace のサービスのみ表示することが出来ます。
画面上部の「 Save view」より、フィルタ状態に名前をつけて保存することも可能です。
このように活用できるので、service.namespace
を指定しておくのをオススメします。
分散トレースの導入にあたっては New Relic 社のテクニカルサポートも利用しました。
APM 画面の仕様の問い合わせや、 細かい疑問の解消など、お世話になりました。
ありがとうございました!
New Relic Advent Calendar 2024 のナイストライ賞をいただきました!
こんにちは。株式会社フィックスポイントのきむらです。
先日、社内の有志メンバーで開発合宿をしてきました!
今まで合宿イベントをしたことはなかったのですが、社内メンバーの一人が発起人となって企画を立ち上げ、実現に至りました🥳
今回はその合宿の様子をお届けします✨
そもそもなぜ今回合宿をすることになったのでしょうか?
実は業務中にこんなツールがあったら便利だなというアイデアがあっても、なかなかまとまった時間をツールの開発にとれないといった思いがありました。
具体的に今回のターゲットになったのは、弊社製品の独自言語であるジョブフローを、エディタ上で手軽に整形するツールです。
そこで、2泊3日の合宿でさながら文化祭のように一気に作っちゃおう!となりました。
その結果、普段はリモートで対面する機会のない遠方のメンバーも楽しめる合宿企画ができあがりました🥳
今回の合宿は、静岡県伊豆半島の山の中に位置するコテージ1棟を貸し切って行いました!
自然豊かな場所で、天候にも恵まれ穏やかな日差しの中3日間を過ごしました✨
弊社では毎月オンラインで社内勉強会を開催しています。 このブログでも過去の勉強会の様子を紹介していますね。
今回の合宿では現地メンバーが発表テーマを持ち寄り、よりラフな雰囲気で勉強会を行いました!
他のメンバーが発表した内容に触発されて、突発的に新たな発表が生まれたりと、対面ならではの盛り上がりを見せました。
合宿といえばおいしいご飯! 夜は外のテラスでBBQをしました。
料理が得意な我がエンジニア部門の部長がコテージのキッチンでアボカドと卵を使ったソースを作ってくれ、とても美味しかったです🍽️
今回の合宿で開発したツールは、その後社内の定例会議でエンジニアの皆さんに紹介しました。
今までコードを整形するためには少しだけ操作が必要だったのですが、今回のツールによりエディタ上で保存するだけで自動で整形されます🎉
少しの手間ですが、その手間がゼロになる自動化は、やはりエンジニアのストレスを下げてくれますね。
今回初の試みだった合宿は、無事成功に終わりました!
弊社では、社内で実際に自動化したい業務を募集し、新人に解決してもらうというユニークな新人研修があります。
困っている社員にヒアリングをし、要件定義、設計から実装まで一通り経験できるうえ、 実際に社内に導入されて皆に使ってもらえる楽しい研修です。
ただ、どうしても新人研修用には難易度や開発期間が合っていないネタは後回しにされがち……
今回の合宿はそういった要望とも非常に相性のいいものとなりました。
ぜひ今後も第2回、第3回と継続して開催していきたいですね。
こんにちは。株式会社フィックスポイントのよしだです。
今回はエンジニアの技術に関わる発表というより、一般的な社会人スキルについての発表がありました!
社会人として職場で円滑な人間関係を構築するには、適切なコミュニケーションが必要不可欠ですよね。
最近社内でもホットな話題でしたので、いつもと毛色は違いますが取り上げさせていただきます✨
続きを読むこんにちは。株式会社フィックスポイントのよしだです。
今回は、2023年度の新入社員技術研修をご紹介します!
フィックスポイントでは、新入社員のエンジニアに対して毎年部署配属の前に2ヶ月ほど研修を行っています。
本記事では、研修の終盤に行う個人開発タスクに焦点を当てて取り上げたいと思います!
全体的な研修の内容や体制については、前年度の記事に詳しく記載しておりますので、気になる方は見てみてくださいね😊
続きを読むこんにちは。株式会社フィックスポイントのよしだです。
今回は、前回に引き続き新入社員が勉強したテスト内容と実際に行ったテストについてご紹介します。
前回の記事は下の通りですので、興味のある方は前編から見てみてくださいね!
それでは早速、テストのお勉強の続きから内容に入っていきましょう♪
続きを読む