💾

マイクロサービスのシステム改善に伴うデータ基盤のログ対応奮闘記

2022/12/09に公開

こんにちは。
datatec-jpアドベントカレンダー9日目の記事です!!

自分はデータ基盤の開発保守運用を担当するデータエンジニアです。この3ヶ月間、ログ取得周りで大きな変更に追従し続けたので、そのことについてまとめます

本日話すこと

  • 今どうやってログを取り込んでるの?
    • 弊社での諸々の背景をまず書きます
    • 今のデータ基盤ってどんな感じで取ってるの?
    • ログ出力についてシステム周りどうなってるの?
  • ログ取得に関するモデリングの変移
  • 意識したこと、工夫したこと

今どうやってログを取り込んでるの

本当に概要だけざっくり書きます。
システムとデータ基盤について詳しく知りたい方は過去の登壇資料である「みんなの考えた最強のデータアーキテクチャ」登壇資料解説をご覧下さい。

システム周り概要

  • 全部GCPで動いています
  • マイクロサービス化が進んでおり、各サービスはGAEで動いてます(図では4種類)
  • 各サービスで2種類のログを出力してます
    • リクエストログ: UA、referrer、リクエストの種類など
    • アプリケーションログ: 分析観点で欲しい情報をアプリケーション側で落とす(JSONとか)

ログ取り込みまで

  • 一部のサービスが別言語で実装されている(図ではDサービスがscalaで実装)
    • サービスごとに、ログ出力の方法とカラム名が違う
  • ログはBQにエクスポートされる
    • 1つのデータセットに複数のテーブルが出力される(言語ごとに)
  • データ基盤では以下のようにログを取り込んでいる
    • traceId を使ってリクエストログとアプリケーションログを紐付けて1リクエスト1レコードを生成
    • 特定のテーブルは、アプリケーション側の特定の情報だけ取り出して実体化
    • 一部のサービスだけログ出力方法が違うので、サービスごとに抽出方法を変える

データ基盤の初期版

  • 過去の資料から引用ですが、BQからBQへスケジュールクエリ経由でデータ生成していました

【変更その1】GAEからCloudRun移行

今年の6月ぐらいに、サービスのCloudRun化を進めている噂を聞く。

何がどう変わるの

  • GAEで動いていたサービスがCloudRunに移行される
  • ログの出力内容(カラム名等)は変わるが、リクエストとアプリケーションの2種類は引き続き
  • 図ではGAE由来のログを青、CloudRun由来のログを緑で表現

  • 図ではBサービスだけCloudRun化されてる例になります
    • Bサービス由来のログだけ、取り方を変えなきゃいけない
  • 何より全サービス一気に変わるわけじゃなく、各サービスがそれぞれのタイミングでCloudRun化するとのこと
    • え!いつどのサービスが変わるのか把握せなあかんじゃん
  • スケジュールクエリがめちゃめちゃカオスになる。。。

ログ取得の設計

  • また以前の記事から図を引用しますが、ちょうどdbtに移行しているタイミングでした
  • ログ周りも層を分けて取り込むように設計しました

  • このタイミングでは以下のように設計しました
    • CloudRun移行が各サービスごとに実施されるため、全てサービスごとに別ける
    • 全般的なリクエストログテーブルもサービスごとに分離
    • CloudRun移行のタイミングで、stg層が参照先をGAE由来(青)からCloudRun由来(緑)に替える
  • メリット
    • 移行する際、stg層だけ修正すれば良いので影響範囲が限定される
    • 言語の差分(Dサービスがscalaで実装されてる等)はfct層で気にしなくていい
  • デメリット
    • どのリクエストが何サービス由来なのかを知る必要がある
    • 移行日はGAE由来とCloudRun由来が混在するので、リリース日だけ特殊な対応が必要

なんか、これなら乗り越えられそうですね!安心〜〜〜

【変更その2】APIゲートウェイ的なサービスが爆誕

ある日、特定のログがほぼ0になる事件が発生!!何が起きた!?明日から海外旅行で2週間休むんやけど/(^o^)\
どうやら、システムの内部改善で登場した「APIゲートウェイ」的な役割を持つサービスの影響であることがわかった

何がどう変わるの?

システム的な詳細は省きますが、ざっっっくり以下のようなイメージです

  • アプリケーションが直接サービスにリクエストを投げない
    • 間にAPIゲートウェイ的な役割を持つサービスが登場(一旦APIゲートウェイサービスって呼びます)
    • どのリクエストが何サービスに問い合わせをすればいいのか深い理解をしている君です
    • APIゲートウェイサービスはCloudRunで動きます
  • 今までリクエストログとして落ちていた情報が、APIゲートウェイサービス側に落ちるようになった
  • いつ、どのサービスが、APIゲートウェイを経由するようになるかは、各サービスごとに異なる
    • え!こいつも各タイミングで切り替わるの!?また把握せなあかんじゃん!!
  • 以下の図がサマリです
    • Aサービスは、APIゲートウェイ対応とCloudRun移行対応が完了
    • Bサービスは、APIゲートウェイ未対応でCloudRUn移行対応が完了
    • C,DはGAEのまま
    • APIゲートウェイサービスはCloudRunで本来は緑ですが、落ちる情報が変わるので黄色で表現

ログ取得の設計

  • このタイミングでは以下のように設計しました
    • 同じようにAPIゲートウェイ対応が済んでるかどうかで取り方を変えればええやん
    • (何がどこに落ちるのか、結構複雑になってきたので、図はめちゃめちゃ簡略化してます

サーバーサイドチームと連携すれば、漏れとか無さそう。なんか乗り越えるんじゃね〜〜?

移行期間めっちゃ大変だった

そんなことはなかった。本当に辛かったです。

反省点

  • リリース日に「参照先切り替えデプロイ」と「当日混在するから両方取る」をするだけじゃん〜では無かった
    • 当日GAE由来とCloudRun由来とAPIゲートウェイ由来をそれぞれ取る、という実装とテストを毎回ちゃんとやっていたが、これめちゃめちゃコストかかる
    • 例: 2022-12-03、10時ににCサービスのCloudRun移行の場合
      • 2022-12-02までのBサービスはGAE由来のログだけ
      • 2022-12-03の10時付近まではGAE由来のログを取る
      • 2022-12-03の10時付近移行はCloudRun由来のログを取る
      • 2022-12-04移行はCloudRun由来のログだけ
      • リリース日だけ混在するのでそういう対応をする必要あり、だるい!
    • 翌日は正しいデータから取ってるよね?の確認コストも高い
  • 将来的にかかるリソースが凄まじいのに見通しが甘かった
    • Nサービスについて「CloudRun移行」「APIゲートウェイ移行」それぞれ移行作業が発生する
    • 図だと4サービスなので、単純計算で8回作業が発生する(実際はサービスはもっと多いので対応回数もっと多い)
    • 1リリースにつき、実装とテスト、混在テスト、翌日確認、諸々合わせて○日リソース持っていかれる。あれ、ヤバない?

さらなる改善の動きが

  • Dサービスが別言語で実装されているが、一部のAPIから順番にGoLangに移行することに
    • Dサービスで落ちていたログが、既存のBサービスや新規のEサービスに移籍される
    • GoLangログの落とし方に共通化されるので嬉しい動きだが、あれ、○○リクエストってDサービスから落ちなくなるの!?え!!

把握しなければいけないこと

  • CloudRunの移行時期と対象サービス
  • APIゲートウェイサービスの移行時期と対象サービス
  • DサービスのGoLang移行の時期と対象APIと、それがデータ基盤に影響があるのか

当時はメリットとして設計のベースだった「サービスごとにテーブルを別ける」があまりメリットになってないじゃん ← この振り返りが一番重要!!

移行期間どうやって乗り切った

  • 以下考えたこと
    • システム側がリリースするたびにデータ基盤がリリースするのが辛い、よってやめたい
    • GAE→CloudRunに移行した場合、両方にログが落ちることはない
    • よって移行有無関係なく、とにかく任意のサービスについて両方から取ってこればよくね!?
    • 一部無駄なクエリが発生するかもしれないが、データ量がそこまで多くない&リリースコストを鑑みて、一通りとって存在するものからリクエストログを生成する方針に

  • 設計案
    • stg層で、アプリケーションまたはリクエストログは、GAE(青)CloudRun(緑)APIゲートウェイサービス(黄)全部から取り込み、存在するものから生成する方針へ
    • mart層では、GoLang移行や新規サービス移行の対応をしなくて済むように、全サービスを統合するテーブルを作る
    • 全ての移行が完了したら、改めてCloudRun(緑)とAPIゲートウェイサービス(黄)だけ取るように修正する。つまりこのリリースと完了のリリースの2回だけで済む!
  • メリット
    • システムのリリース日にデータ基盤がリリースしなくていい。これがでかい!
    • GoLang移行を気にしなくていいのが良い
  • デメリット
    • 一旦全部取る、みたいなSQLになっているので、データ量的に無駄なクエリを走らせている

ログ基盤設計まとめ

  • stg: サービスごとに分離。全対象から取って存在するものから生成
  • mart: サービスごとに分離せず、とにかく1つのテーブルに統合する

把握しなければならないこと

  • 以下しっかり把握しなければいけなかったが、システムリリース翌日に確認しておくだけで済むようになった
    • CloudRunの移行時期と対象サービス
    • APIゲートウェイサービスの移行時期と対象サービス
    • DサービスのGoLang移行の時期と対象APIと、それがデータ基盤に影響があるのか

まとめ

  • システムは常に変化しており、設計当時のメリットやデメリットが、現在当てはまらないケースもある
    • よって定期的に振り返ることは大事
  • きれいなやり方で完璧なスーパープラン、みたいなものに拘らない
    • 今回は無駄なSQLを投げる代わりに、将来的に1回のリリースで全て移行完了するという意思決定をした
      • (このサービスってこのタイミングでは○○に落ちていないんだけど、、という気持ちを捨てる)
    • データ量がそこまで多くないのと、自分の保守運用コストがめちゃめちゃ持っていかれたので、そこを判断材料とした
  • 偶発的に発見したシステム側の変更もあるので、エンジニアとのシンクは引き続きやる!

開発チームがtruncベース開発とマイクロサービスにより大いなる進化を遂げており、その変化にデータ基盤が追従するのにかなり苦労したが、自分がシステム側を結果的に詳しくなったり、運用できなくね!?みたいな経験も出来たので良かった。

データ基盤の保守運用で一番大事なのは「分析に影響がでないこと」なので、完璧を目指さないというのも大事かなと思いました。

以上です!

Discussion