Classi開発者ブログ

教育プラットフォーム「Classi」を開発・運営するClassi株式会社の開発者ブログです。

SRE留学体験記(第5期生)

こんにちは、学習PMF部でエンジニアをしている辻です。 私は2023年12月から2024年5月までSRE留学という社内制度を利用して、SREチームに期間限定で所属していました。
SRE留学とは?や、第1期生、2期生、3期生の体験記はこちらをご覧ください。 tech.classi.jp tech.classi.jp tech.classi.jp

留学を志望した理由

SRE留学をする以前は主にCALEというシステムの開発・運用を担当しており、その中でパフォーマンス改善やSLI/SLOの設定、Toil削減などに取り組んだことで、SREに興味を持ちました。
SREの活動に必要なAWSの知識やTerraformの知識、Classi全体のシステムの知識などを獲得して、学習トレーニングやCALEといったシステムでの開発や運用に活かせることを期待しました。また、SREチームに参加することで、単一のチームやシステムに関わるだけでは得られない視点を獲得できるとも考えました。

留学中にできたこと

基本的に、やりたいことをやらせてもらえる環境なので、留学中にしかできないことや学びになることを実施しました。日常的なタスクとして、SRE宛に来るTerraformのレビューや依頼の対応は行っていましたが、私が実施した代表的なタスクを2つ紹介します。

学習トレーニングのSLI/SLOの設定

CALEというシステムのSLI/SLOの設定を行った経験から、学習トレーニングでもSLI/SLOを設定したいと思いました。SRE留学前からSREチームでは学習トレーニングにSLI/SLOを設定することが計画されており、参加させてもらいました。
SREチームのid:ut61zさんと一緒に取り組むことで安心感があり、またSLO本読書会もこのタイミングで開催され、非常に有意義でした。学習トレーニングチームの方々と話し合いながら設定を決め、id:ut61zさんにも多く相談しました。設定後、このSLOの値を基に改善の議論が発生しており、今後もSREチームと連携しながら改善を続けていきたいと思います。

Opsサーバリプレイス

このタスクはチームで行いましたが、私は主にAnsibleの記述、CI/CDの構築、起動方法の設計・実装を担当しました。Opsサーバとはいわゆる踏み台サーバのことで、検証環境、本番環境でなんらかのオペレーションを実行するために利用しています。既存のOpsサーバはitamaeでプロビジョニングされているのですが、Classiではそのリポジトリのメンテナンスが不十分だったり、EC2関連のシステムの関心が入り混じったリポジトリからOpsサーバの関心の分離をしたいという意図があったため、AnsibleとPackerを用いて作り直しました。
既存のOpsサーバでは、起動方法にAuto Scalingグループを使っていましたが、デタッチ忘れやタグの付け忘れ、Opsサーバの消し忘れや立ち上げが手間などの課題がありました。
そのため、新しいOpsサーバの起動方法はChatOpsを採用し、AWS Chatbotを使ってより簡単に立ち上げることができ、管理しやすいようにしました。

ChatOpsの構成図

起動と削除をSlack上で行えるようにしたことで、管理が容易になり、履歴も残るため、既存のOpsサーバでの起動方法の課題も解決しました。
このリプレイスのタスクで学習した許可セットなど、SRE留学中に、普段の業務ではあまり触らないところも触ることができ、勉強になりました。SRE留学から戻った後もこれらの知識は活きてくると思います。

これらのタスク以外にも、構成図の作成、Sandboxリソース作成の通知、GithubActionsの導入、証明書の更新など、SRE留学をしていなければ経験できなかったタスクを多く経験し、多くの学びがありました。

良かったこと

普段の業務では、あまり触れることのない部分に触れた

具体的には以下の機能などです。

  • SCPを含んだAWS Organization
  • IAM Identity Center
  • IR購入に関するあれこれ

SRE留学では、SREチームと同等の権限を持ってSREとして業務を行うので、普段では触れない部分に触れることができるのは、SRE留学の良いところだと思いました。

コードレビューを通してClassiのシステムへの理解とAWS・Terraformの理解が深まった

SREチームの朝会では、SREチーム宛に来たTerraformのプルリクエストをレビューするのですが、その中で出てきたわからないことはその場でSREの方に聞くことができるので、すごく勉強になり、レビューを行う過程で、Classiの全体のサービスやそのシステム構成をある程度把握することができました。
また、留学前は自身のチームの範囲のAWSサービスやそのTerraformコードの部分しか理解していませんでしたが、Classi全体で使われているAWSやTerraformへの理解も深まりました。

難しかったこと

横断的な動きができなかった

留学生の振り返りでよくこの点が挙げられていますが、他のチームから寄せられる質問への対応やプラットフォームに関することに積極的に関わっていくことができませんでした。「こういった場合どうしたらいいですか?」という質問がSRE宛のメンションで来ても、知識が必要ということと、SRE留学生としての立場では意思決定が難しく感じる面があり、あまり関与していくことができませんでした。

今後

ClassiのSREチームはプラットフォーム部のチームなので、直接的なSREingをプロダクトに対して行うというよりはプロダクトチームへのSREingの支援という側面が強いです。留学期間中に特定のプロダクトに対する直接的なSREingはあまりできませんでしたが、プラットフォーム部のSREチームに留学したことで、今まであまり触ってこなかった技術などに触れられてとても充実したSRE留学でした。SRE留学からプロダクトチームへ戻ったときに、その経験を活かし、よりアプリケーションの近くでSREingを実践していき、SREチームと連携しながら取り組んでいきたいと考えています。

詳解Terraform読書会を実施しました

こんにちは。プロダクト本部プラットフォーム部SREチームの id:ut61z です。 SREチームが主体となって書籍『詳解 Terraform 第3版』(以後、詳解Terraform)の読書会を社内で実施しました。

www.oreilly.co.jp

どう進めたか、実施してみた感想や反響、学んだことをご紹介しようと思います。

なお、過去にもSREチームが開催した読書会を実施しているので、よければこちらもご覧ください。

tech.classi.jp

詳解 Terraform 第3版について

詳解TerraformはTerraformについて体系的に学べる一冊で、サンプルコードとともにハンズオン形式でAWSのリソースを構築しながら学ぶことができます。

Terraformの実装方法、操作方法、注意点、特徴など全体像を把握するにはとてもよい書籍だと思います。

全体の構成については以下のようになっています。

  • 1章: 従来のプロビジョニングツールとの違いと、インフラストラクチャを取り巻く環境の遷移とともにツールがどのように移り変わってきたかの紹介から始まり、IaCの利点やなぜTerraformを使うかについて解説がなされます
  • 2章: AWSを用いたハンズオン形式でのTerraformの操作方法・定義の仕方についての紹介パートが続きます。EC2 1台で稼働する素朴なWebサーバ構築から始まり、徐々に周辺リソースを追加していくことで可用性の高いスケーラブルなWebシステムを構築することを目指します。Application Load Balancer、Listener、Listener Rule、Target Group、AutoScaling Group、EC2を用いた基本的な構成のWebシステムを理解することができます
  • 3~7章: TerraformのState、Module、ループや条件分岐など動的なTerraformリソースの記述の仕方など、Terraformに搭載されている様々な機能について紹介され、やや応用的な使い方・記述の仕方について知ることができます
  • 8~9章: 本番環境の構築にあたっての注意点、テストについて紹介されています
  • 10章: チームでTerraformを運用するにあたっての勘所やデプロイワークフローの構築について触れ、組織へTerraformを導入・定着させていくための戦略についても紹介されています

なぜ詳解Terraform読書会を実施したか

ClassiではほとんどすべてのインフラリソースをTerraformで管理していて、SREチームだけでなくプロダクトチームのエンジニアもインフラリソースの構築・運用を行っています。

しかし、エンジニア全員がTerraformを用いたインフラリソース構築経験があるわけではなく、スキルやレベルにばらつきがあるのも事実です。

それを踏まえ、主に下記2点を期待して読書会を実施しました。

  • インフラストラクチャ、Terraformに対してとっつきにくさを感じているエンジニアの心理的ハードルを少しでも下げ、興味を持ってもらうこと
  • エンジニア組織全体のTerraformスキルのボトムアップを図り、より開発効率を上げること

どのように進めたか

週1回のペースで1章ずつ読み進めました。 担当者は1章分の要約と感想を前半30分で発表し、後半30分でその章について参加者全員でディスカッションを行うという形式で読書会を実施しました。

今回の読書会において工夫した点は、SREチームのメンバーが各章のまとめにコメントを加えて、実際のClassiでの運用はどうしているかをコラムとして追加した点が挙げられます。

座学的な読書会にとどめず、実践的にClassiでTerraformを書くならどうすればよいかをイメージしやすいようにし、普段の業務との接続を試みました。

記述したコラムの例

実際にやってみて

あくまで読書会として書籍を読み進めることをスコープとしていたので、主催者としてはAWSにリソースを構築することまでは参加者に強要はしなかったのですが、結果的には参加者の多くが実際に手を動かしてリソースの構築を行い学習を進めていました。

Terraformを触っている参加者であっても、ゼロからインフラリソースを構築する体験は普段の業務ではなかなか味わえないので、ハンズオンを進めながらシンプルなシステム構築を行い、改めてAWSやClassiのシステム構成への理解が深まったという感想もありました。

特に理解が深まった点として挙げられていたのは、Terraformのロックの仕組みや、Stateファイルの位置づけや特徴などについてでした。 「Stateファイルには秘匿すべき情報が平文で書き込まれてしまうので注意すべき」など、これだけは抑えておこうという知識が参加者にインプットされたのも、実務に直結する知識として今後活きてきそうだという感触を得ました。

他にも、Terraformを組織にどのように導入するかについて言及されている章では、新しい技術を組織に提案・導入・定着させていくための戦略が記されていて、Terraformに限らず応用が効きそうだという感想もあり、思わぬ収穫があったメンバーもいたようでした。

一方で、どんな読書会でも起きがちな問題ではありますが、回を重ねるごとに中だるみをして参加者のモチベーションが低下し、参加を見送る機会が増えたという意見もありました。 中間ふりかえりの回を挟んだり、途中で改めて目次を眺めてこの章は実務でどう役立つかなど、現在地の確認をしてモチベーションを維持するなどのアイディアが出ました。

ふりかえりの様子

中だるみを解消するためのアクションとして、今後の読書会に取り入れていきたいです。

SREチームによるコラムも好評で、Classiではこうしているという現状を共有することで、ハンズオン内容が普段触っているリソースやTerraformのソースコードと直結し、読書会の満足度を高めることに貢献できました。

以下に読書会後のアンケート結果を添付します。

参加者にとって有益だったことがうかがえますね。

少し余談になりますが、Classiでは読書会が常に開催されているので、各読書会の知見が集まり、読書会が洗練されていくのを感じました。

今回の読書会も読了後全体を通してのふりかえりを行うことで、どんな学びを得られたかを言語化し、各々の実務にどう接続するかを考える機会を設けることで、もう一歩踏み込んだ学びの機会になったことを実感しています。

フルサイクルエンジニアを目指して

Classiでは、開発から運用まで一環して行えるフルサイクルエンジニア*1であろう、ということをエンジニアの共通認識として持っています。

今回の読書会はその一助になったのではないでしょうか。 改めて、開発から運用までなんでもやりたいという方にとってはとてもよい環境だと感じました。

ピンと来た方がいらっしゃれば、ぜひカジュアル面談等気軽に問い合わせてみていただけるとうれしいです。

hrmos.co

SREチームもメンバー募集中です。こちらもご興味ある方はぜひお問い合わせください。

hrmos.co

最後まで読んでいただきありがとうございました。

なれる!データエンジニア

はじめに

こんにちは。データプラットフォームチームのマイン(id:manhnguyen1998)です。

2024年1月からデータエンジニア留学(第1期)という制度を利用して、データプラットフォームチームに配属されています。第1期生として、このデータエンジニア留学制度について、自分の経験をもとに紹介したいと思います。

データエンジニア制度とは

データエンジニア留学制度は基本的にSRE留学と同じですが、留学先はデータプラットフォームチームです。SRE留学に関する記事はいくつかありますのでご覧ください。

tech.classi.jp tech.classi.jp tech.classi.jp

なぜ留学したのか?

もともとバックエンドエンジニアとしてRailsのアプリケーション開発を行っていましたが、ある時社内のBigQueryでデータを見る機会がありました。どうやってRDSからBigQueryまでデータが反映されるのかに興味を持ち、データエンジニアの仕事に興味を持つようになりました。

その時、データエンジニア留学制度の告知があり、不安もありましたが、チャンスを逃すまいと留学を希望しました。

自分と同じようにデータエンジニアに興味を持っているけれど、不安でチャレンジしない方もいると思います。この記事で「誰でもできるよ!」ということを伝えたいです。

データエンジニアの仕事とは?

データエンジニアの仕事は、データ活用の基盤を作ることです。具体的にはデータの収集、処理、保存、提供などがあります。

Classiでは主に以下のようなことを行います。

  • 他チームのデータ活用の支援、相談、作業依頼

  • 改善活動

また、データエンジニアが使用するツールや技術はBigQuery、dbt、Cloud Composerなどがあります。

他チームのデータ活用の支援、相談、作業依頼

データ基盤は社内の多くのメンバーが利用しており、さまざまな意思決定を支えるシステムとなっています。そのため、データを活用する上で、データ基盤に対してもさまざまな要望があります

  • マーケティングチームは顧客の利用状況を把握するためにダッシュボードを作成したい
  • プロダクトチームがリリースした機能の効果測定をしたいが、方法がわからないので相談したい
  • データサイエンティストがデータ分析のためにデータセットを準備したい
  • データ活用に関するアドバイスやベストプラクティスを共有して欲しい

などの要望です。このような相談に対してエンジニアとして意見を出し、他チームの開発をサポートしていました。

改善活動

データ基盤の改善もデータエンジニアの重要な業務の一つです。データ基盤は膨大なデータを扱うシステムであり、うまく改善しないとデータの提供もできないことがあったり、コストも高くなることがあります。

改善活動として、私が対応したものは以下のようなものがありました。

  • データパイプラインをCloud Composer (Airflow) からdbtに移行する
  • データの品質を監視する環境の整備
  • コスト削減
  • 不要なリソースの削除

留学中にやったこと

BigQueryに抽出されたデータの欠損検知

データ基盤の毎日のデータ抽出には欠損リスクがありますが、欠損がある場合、これまで気付くことができませんでした。そこで、欠損を発見しやすくするための改善に取り組みました。

BigQueryに保存されたテーブルのレコード数の推移の異常を検知し、欠損など急な変化を検知できるようにしました。例えば、特にアクションがないにもかかわらず、レコード数が急激に増減する場合、データの欠損や何らかのミスが発生した可能性があります。逆に、繁忙期におけるデータの推移を監視することで、利用状況を把握することもできます。

Design

開発したSlack通知

通知の例

Tableau

TableのView

ダッシュボード機能のETLのdbtへの移行と改善

ダッシュボード機能のETLは元々Cloud Composerで直接クエリを実行してデータを管理していましたが、管理が難しかったため、dbtに移行しました。dbtに移行することで、データの処理や管理が容易になり、データに関する説明も整理することができました。

この機能は管理職が利用し、学校全体に関する統計情報を閲覧するため、その重要性が非常に高く、データに誤りがあると重大な影響を及ぼす可能性があります。そのため、dbtに切り替える前に、移行前後の差分確認をしっかり行い、慎重に対応しました。しかし、確認不足の部分があり、学校IDをクラスタキーとして使用することを忘れてしまったため、リリース直後に課金が増加してしまいました。この経験から、コストに対する理解を深めることができました。

このコストの問題を認識した上で、ダッシュボード機能のETLを見直す機会があり、毎日のスキャン量を約85%削減することができました。 ダッシュボード機能のクエリでは学校IDをクラスタキーとして使用していました。しかし、クラスタはクエリごとにスキャン量が異なり、効率的な測定が難しいという課題がありました。そこで、調査の結果、学校IDの空間でパーティションを使用することにより、スキャン量を削減することができました

GCPコストの削減

メタデータシステムPlatoをクローズ、dbtに移行

Platoは以前ブログでも紹介したメタデータ管理システムです。

tech.classi.jp

当時、メタデータの普及に重要な功績を残してくれたシステムでしたが、近年では運用コストが上がり、手間も増えたため、別の仕組みに移行するモチベーションがありました。

この対応により運用作業が減少し、毎月のGCPコストも削減することができました。既存のメタデータもdbt docsに移行でき、社内でのdbt docsの存在を改めて認識させることができて良かったです。

dbtに関しては下記の記事をご覧ください

tech.classi.jp

dbt docsの例

dbt docs画面

RedashのCloudSQLのPostgreSQLを9から15にバージョンアップ

データベースのメジャーバージョンアップデートは初めての経験だったので、最初から調査し、ダウンタイムを短くするための計画を立てました。作業自体は非常に勉強になりました。今回は20分以内に作業を終え、ダウンタイムを短く抑えることができてよかったです。

Slackでやり取りました

勉強になったこと

留学中に学んだことは技術に限らず、以下の点が特に勉強になりました。

分析力

データから仮説を立て、それをもとに結論や次のアクションを提案することができました。少しずつですが、分析について学ぶことができました。

チーム間コミュニケーション

留学の目標の一つはチーム間で協力することでした。自分のタスクに関わるだけでなく、SRE、マーケ、開発チームなど他のチームとも関わり、情報共有やコミュニケーションが必要でした。

「how」より「why」の考え方

以前はタスクに直接飛び込むことが多く、その理由がわからないまま適当な解決方法を出すことがありました。今回の留学で、「なぜ」そのタスクが必要なのか、誰がその成果を求めているのかを十分に理解した上で、適切な提案をすることが大切だと学びました。

難しかったこと

知識の広さ

データエンジニアは多くのチームと関わるため、求められるスキルは多岐にわたります。インフラ管理の知識、データ管理の知識、開発スキル、マーケ側とのコミュニケーション能力など、広い範囲で深く理解する必要があります。

横断的な振る舞い、考え方の難しさ

データプラットフォームチームは他のチームをサポートすることが多く、横断的な振る舞いや考え方が求められます。しかし、留学中はチーム課題に集中しすぎて、データエンジニアとして他チームをアドバイスして支援することが難しかったです。

あなたもできる!

データエンジニアは魅力的な仕事ですが、チャレンジも多いです。しかし、難しいからといってできないわけではありません。もともと何もわからなかった自分ができたので、誰でもできると思います。日々の仕事でスキルを磨き、必ず達成できます。

まずは小さなプロジェクトから始めましょう。このアプローチは、少ないリスクで新しいスキルを試し、成長するための確かなステップです。例えば、データエンジニアとして初めてのプロジェクトとして、日々のログデータから特定の指標を抽出するタスクを考えてみましょう。このプロジェクトでは、SQLクエリを使用してデータを加工し、分析結果をダッシュボードで可視化することが目標です。小規模なデータセットと少ないリソースで取り組むことで、自分のアイデアを具体的な成果物に変えるプロセスを学ぶことができます。

さらに、タスクを進めながら、メンターやチームに積極的に質問し、理解を深めましょう。この過程で、ソフトスキルも成長し、データエンジニアとしてのスキルだけでなく、チーム全体での協力や連携能力も養われます。チーム内だけでなく、インフラチームなど他の部門の意見も積極的に取り入れて、より良い成果を目指しましょう。

最後に

データエンジニアというキャリアは挑戦と学びの機会に満ちています。最初は不安でしたが、一歩踏み出すことで多くのスキルと知識を得ることができました。他チームとの協力や支援を通じて自分の成長を実感しています。

データエンジニアを目指す皆さんにも、その一歩を踏み出してほしいと思います。初めは難しく感じるかもしれませんが、挑戦し続けることで必ず成長できます。その過程で得た知識やスキルは、今後のキャリアに大いに役立ちます。

Classiのデータエンジニアに興味を持った方は下記をご覧ください

hrmos.co

Classiのエンジニア2名が RubyKaigi 2024 に参加しました

はじめに

こんにちは、エンジニアの id:kiryuanzu です! 今回の記事は 5月15日(水)から 5月17日(金)の3日間を通して開催された RubyKaigi 2024 の参加レポートです。

rubykaigi.org

弊社からは 2名のエンジニアが参加しました。本記事では各メンバーによる参加レポートをお送りします。

id:kiryuanzu の参加レポート

改めて今回の RubyKaigi 2024 に参加してきた id:kiryuanzu です。
自分自身は学生の頃(2017年)からほぼ毎年参加していますが、弊社のメンバーと参加するのは RubyKaigi 2022 以来で 2年振り 2回目となりました。

tech.classi.jp

前回と同じく初参加のメンバーにもできる限り楽しんでもらえるように意識しつつ、自分もたくさんインプットできるように心がけて過ごすようにしました。

発表の感想

Community-driven RBS repository (pockeさん)

Rubyの型情報を収集するOSSリポジトリである gem_rbs_collection の運用についての発表でした。
GitHub Actions のワークフローの仕組みを使いつつメンテナのレビューに依存しない運用に切り替えていった話について紹介されており、筆者もここ最近の業務で GitHub Actions を使って運用フローの効率化について考えていたため、とても親近感が沸く内容でした。
OSSリポジトリの場合、様々な人がコントリビューターとして関わる際に開発の効率性とセキュリティのリスクの双方を気にしつつ運用フローを考える必要があるのが難しいポイントだなと思いました。

発表が終わった後、早速ワークフローのコードを見に行ったのですが、ワークフロー内で呼び出すスクリプトを全部 Ruby で書いている点に強い Ruby愛を感じました。

github.com

スライド内でも「It uses Ruby scripts Because I love Ruby」と言及されていますね。

Adding Security to Microcontroller Ruby (sylph01さん)

PicoRuby に暗号機能を実装する話についての発表でした。
現地では英語発表で分からない部分も多かったのですが、とても楽しそうに話されている姿が印象に残っていました。その後、るびま(Rubyist Magazine)で日本語版が公開されていて改めて発表の内容について知ることができました。

magazine.rubyist.net

記事中の「Ruby での真のIoT」という表現がかなり印象的で、今後もまだまだやれることがありそうな雰囲気を感じました。

実際の IoT 環境ではこれらの違ったスペックを持つハードウェアは協調して動作させるので、PicoRuby が WiFi に繋がるようになったことで、「Ruby での真の IoT」は現実に近づいたといってよいでしょう。IoT に関するプロトコルは数多く存在しており、これらを PicoRuby で扱えるようにすることで、Ruby での IoT の可能性は広がっていくものと考えられます。このことから、Ruby での IoT は「ブルーオーシャン」であるといえます。コミュニティは皆さんのコントリビュートをお待ちしています。

自分は PicoRuby 自体書いたことがなかったのですが、他の発表でも多く言及されていたトピックの一つで「なんだかみんな楽しそうにやってていいな……」と思い会期終了後にRassberry Pi Pico W を購入しました。
まずは以下の記事を見ながらLチカチャレンジしてみようと思います。(この記事の執筆者の hasumikin さんは PicoRuby/R2P2 の開発者かつ今年の RubyKaigi のスピーカーの方です)

shimane.monstar-lab.com

発表以外で印象に残ったこと

会期中の社外の方々との交流についてはいもりさんのパートでたくさん取り上げているので、id:kiryuanzu のパートでは RubyKaigi に絡めて行われた社内での活動についてピックアップします。

今回の現地参加組はいもりさんと私の2人だけでしたが、社内の Slack で #spot-rubykaigi-2024 チャンネルを作って、会社のメンバーに向けて発表の感想や現地での出来事をその都度発信するようにしました。
今回は現地参加されなかった方々も、発表の解説ツイートや私たちの発言に対して反応してくださり一緒に楽しむことができました。

社内slack での様子

また、今年の4月から株式会社万葉から参画し、tetoru チームで関わってくださっている櫻井さんも RubyKaigi に参加されていました。
イベント直前に社内で企画した予習会では、初参加の人向けの RubyKaigi の楽しみ方についてお話ししていただき、会期中にも交流の機会をたくさん作っていただきました。
会期中は Classi Tシャツも着てくださっていたのが大変嬉しかったです。なんとよく見ると名札の方にも tetoru のロゴを手書きで描いてくださっています。

RubyKaigiが終わってすぐのタイミングで、会期中に印象に残ったことについて語る会も実施しました。
筆者が「パーサーについてみんなどうやってインプットしてるんだろう……」とぼやいた際に、同僚の方から「まずはインタプリタの作り方の第二部まで読むのがおすすめです」とアドバイスをもらい読むことにしました。紙の本を購入したところですが、中々の分厚さにびっくりしています。

(当時の会話をメモした社内esaを一部抜粋)

そのようにして、現地参加組とリモートで見守ってくれたメンバーの間で交流を深めつつ一緒に RubyKaigi の空気感を楽しむことができました。
来年はもっと多くのメンバーに現地参加してもらえるよう社内での発信を続けていきたいです。

いもりの参加レポート

2024年4月に新卒入社したいもりです!

今回、ありがたいことに新卒研修期間中にRubyKaigiへ参加する機会をいただきました。 ところが当時私のRuby学習歴はたったの4日…

そんな人間のレポートとなっています。生暖かい目でご覧ください。

参加しての感想・交流について

第一印象として、とにかく新参者に優しいコミュニティでした!

交流の場として、日中はRubyKaigiで知り合った人と一緒に昼食を食べたり、夜には多くの交流の場が設けられていたのでそちらに参加したりしました。
その場では初学者でも非常に温かく出迎えてくださり「今日の発表は何が印象に残った?」「普段はどんな研修を受けているの?」など、私の目線で会話に加えていただいたことが本当にありがたかったです。

登壇者の方ともお話しする機会に恵まれ、RubyKaigiの沿革や、登壇者ならではの苦労などを話していただきました。
特に「自分が発表している時間の発表を聞きに行きたいんだ!」という一言は忘れられません。

また、私は現在フィヨルドブートキャンプにて研修しているのですが、同じフィヨルド生の方とも交流できました。
研修で使っている『ゼロからわかる Ruby 超入門』の著者であり、Classiの新卒研修の設計に携わられていたigaigaさんとも快くお話ししていただき、サインもいただきました。大事にします!

発表

全体として、登壇者の方々が「この技術はこの方の登壇を聞けばより詳しいことがわかる 」と、他の方の発表を引用し合っているのが非常に印象的でした。

これは本当に素晴らしいことだと思います。Rubyコミッターの方々が互いを知り、また信用していないとできない引用だと思います。 改めてRubyコミュニティの活発さを感じ取った瞬間でした。

ここでは特に印象に残った発表3つについて、簡単ではありますが感想を書かせていただきます。

Writing Weird Code

初日トップバッターのキーノートの登壇です。 正直なところ、この発表がなければ私はRubyKaigiを楽しめていなかったかもしれません。それくらい衝撃的で感銘を受けた発表でした。

内容としては、irbで奇妙なコードを書くというものです。その奇妙さが本当に強烈でした。

これは紹介された一例ですが、irb上でコードのクラゲが泳ぎ、マウスでクリックするとそれに反応して変形するという内容です。

この発表では、この他にも視覚的に楽しむことができる奇妙なコードが合計で6つ用意されており、そのどれもが本当に衝撃的でした。

肝心の実装についての説明はあまりに高度な内容で、会場含め「…?なるほど…ね?」という反応にならざるを得ませんでしたが、irbすごい!これから使っていこう!と思えた発表でした。

Cross-platform mruby on Sega Dreamcast and Nintendo Wii

こちらは1日目午後の登壇でした。内容としては、mrubyを用いてNintendo WiiとDreamcastのクロスプラットフォームを作成するというものでした。

この方の発表はとてもゲーム製品への愛が伝わってくる内容で、ゲームコントローラーがいわゆるスライドポインターの働きをしており、登壇中は軽快にWiiコントローラーを振っていました。また、Tシャツが非常に個性的でした。

この発表で驚いたのは、最初はまた個性的だと思っていた発表スライドが、実は起動したゲーム内でテキストを表示させていたことでした。

発表の後半ではDreamcastを起動していたのですが、先ほどまで見ていた全く同じテキストが、Dreamcastのフォントで表示されていることに気づいた時は鳥肌が立ちました。

Ruby Committers and the World

こちらは3日目最初のRubyコミッター同士の対話形式登壇でした。 内容としては、2日目深夜にRuby3.4.0-preview1がリリースされたこと、そしてこれからのRubyについて議論するという内容で、活発に意見が交わされていました。

この対談で印象的だったのは、コミッターの方が現在検討しているRBSの文法について、参加者がどちらを好むか多数決で世論調査を行ったことでした。
この多数決が将来のRBS文法を決めるものになるかもしれないと思うと、Rubyコミュニティに少し関わることができて嬉しかったです。

また、対談は日本語と英語の二か国語で実施されたのですが、途中からMatzさんによる同時翻訳が始まったのが面白くもあり、非常にありがたかったです。

まとめ

今回、先輩の id:kiryuanzuさんを通して多くのRubyistの方とお話しさせていただきました。貴重な交流の時間、本当にありがとうございました。

また、株式会社万葉から参画しClassiに携わっていただいている櫻井さん、社内から後押ししてくれたClassiメンバーの皆さん、RubyKaigiを運営していただいたスタッフの皆さん、その他ランチや飲み会の場で交流をしていただいた方々に深く御礼申し上げます。

次回は愛媛県松山市開催ということで、関西在住の私にとっては比較的好立地なこともあり、ぜひ参加したいです。

これからもRubyコミュニティに関わっていき、一人前のRubyistを目指します!

おわりに

以上、Classi メンバーの感想レポートをお届けしました! 今回の RubyKaigi での経験を経て普段の業務や趣味の開発を頑張っていこうという気持ちを改めて持てました。

RubyKaigiの翌週には Classiオフィスで開催された shinjuku.rb #92 ではいもりさんが「RubyKaigi 2024 行ってきたレポ」というタイトルでLT発表をされていました。

tech.classi.jp

このように早速コミュニティ活動へと飛び込んでいく姿を見せており、RubyKaigi での経験が後押しになったとしたら嬉しい限りです。

来年の4月には愛媛県松山市で RubyKaigi 2025 が開催されます。その時に向けて日々の生活の中で学びを重ねつつ、今回よりも更に RubyKaigi という非日常の場を楽しめるようになりたいです。

来年もまた RubyKaigi でお会いしましょう!

Strict CSP を Content Security Policy Level 3 に対応したブラウザに絞って適用する

こんにちは、プロダクト本部エンジニアの中村 (kozy4324) です。

現在 Classi が提供している Web サービスでは Content Security Policy を導入しています。その導入時の話は以下の記事で紹介させてもらいました。

今回の記事では、運用を続けていく中でわかったことや出てきた課題、またそれらを踏まえて現在どういった CSP のポリシーで運用を行っているのか紹介します。

オリジンの許可リストをベースにしたポリシー

導入時の記事でも紹介している通り、運用開始時のポリシーは以下のようなものでした。

Content-Security-Policy:
  default-src 'self';
  script-src  example.com 'sha256-xxx' 'nonce-hogehoge111';
  style-src   example2.com 'sha256-yyy';

ベースは ’self’ (文書が提供されたオリジンと同一オリジンのリソースを許可する)とホスト名による許可リスト方式で、特定のインラインコードやインラインスタイルを追加で許可したい場合にハッシュ値や nonce を追加しています。

最初はできる限り厳格な規格に沿ってポリシーを設定、違反レポートを確認しながら必要に応じてポリシーを追加、もしくは無害と判断できるものは無視するという運用ルールとしていました。

出てきた課題1: オリジンの許可リストベースでは攻撃とは見なせない違反レポートが雑多に出てくる

上記ポリシーで運用を始めたところ、すぐに多くの違反レポートが日々あがってくる状況になりました。ところがそのほとんどがブラウザ拡張機能や 3rd party 製の何かによる img や style 、script の挿入に見受けられ、総合的にみて「攻撃とは見なせない」という判断になるものばかりでした。

Sentryに収集された実際の違反レポートの一部

違反レポート自体にはそれほど多くの情報は含まれておらず、頻度やブラウザの分布なども確認しながら一つ一つ丁寧に調査していく必要があります。新しいレポートが発生するたびに調査対応していくと運用負荷は大きなものになっていきました。

Strict CSP をベースにしたポリシーへの変更

オリジンの許可リスト方式で厳密に制限して運用していくのは運用負荷が大きいということが分かりました。また許可リスト方式では CSP バイパスの問題も指摘されています。なので方針を変更し、nonce + strict-dynamic による Strict CSP をベースにしたポリシーとすることにしました。Strict CSP では XSS に対する防御に重点を置き、基本的にはスクリプトに対してのみ制御を行います。XSS に対して効果の薄いスクリプト以外のリソースには制御を行わないため、スクリプト以外で発生する雑多な違反レポートが抑止されることを期待しました。

Strict CSP をベースにしたポリシーは以下のようになります。

Content-Security-Policy:
  object-src 'none';
  script-src 'nonce-{random}' 'unsafe-inline' 'unsafe-eval' 'strict-dynamic' https: http:;
  base-uri   'none';
  report-uri https://your-report-collector.example.com/

この設定内容は、例えば strict-dynamic をサポートしていないブラウザでのフォールバックが含まれています。 Strict CSP のページでも説明がなされていますが、より詳しく整理したものが以下です。

  • CSP Level 3 まで対応しているブラウザの場合
    • 'nonce-{random}' が採用されるので 'unsafe-inline' は無視される
    • 'strict-dynamic' が採用されるので https: http: は無視される
    • よって nonce + strict-dynamic で許可されたスクリプトだけが実行される
  • CSP Level 2 まで対応しているブラウザの場合
    • 'nonce-{random}' が採用されるので 'unsafe-inline' は無視される
    • 'strict-dynamic' が解釈できないので https: http: が採用される
    • よって nonce で許可されたインラインスクリプトと外部スクリプトだけが実行される
    • nonce が付与されないインラインスクリプトは実行が制限される
  • CSP Level 1 まで対応しているブラウザの場合
    • 'nonce-{random}' が解釈できないので 'unsafe-inline' が採用される
    • 'strict-dynamic' が解釈できないので https: http: が採用される
    • CSPによる実行制限はない(保護されない)
  • CSPに対応していないブラウザの場合
    • CSPヘッダは何も作用しない
    • CSPによる実行制限はない(保護されない)

出てきた課題2: Strict CSP ベースでは一部ブラウザで Google Tag Manager による違反レポートが発生する

Classi サービスでは Google Tag Manager を利用している箇所がいくつかあり、Google Tag Manager が動的に追加するスクリプトで違反レポートが発生しました。これは一部のブラウザのみで発生し、確認したところ CSP Level 2 まで対応しているブラウザ、2024年5月時点では iOS 14.x〜15.3 の Mobile Safari が該当しました。Classi サービスにおいてまだ一定数のアクセスがある状態です。

CSP Level 2 まで対応しているブラウザでは strict-dynamic は解釈されないため、動的に追加するスクリプトの実行を許可するには nonce を付与する必要があります。 Google Tag Manager のガイドに nonce 対応バージョンの言及があるので、この nonce 対応バージョンとすることで問題なく CSP の実行許可がなされると思われましたが、一つ落とし穴がありました。Google Tag Manager の「カスタム HTML 」で追加されるスクリプトタグには nonce を付与することができませんでした。補足として Google Tag Manager 上で「 document.write をサポートする」というオプションを有効にすることで nonce を付与することは可能になりますが、 Classi での利用方法的にこのオプションを有効にすることもできませんでした。

CSPで制御されるスクリプトの種類とディレクティブの整理

ここまでに遭遇した課題をうまく回避できる CSP 設定はないものかと思い、スクリプトの種類とディレクティブについて整理をしてみました。

種類 具体例 制御ディレクティブ ‘self’ と host-source による許可 nonce による許可 strict-dynamicによる許可
外部スクリプト <script src="outer.js"></script> script-src-elem できる できる できる
インラインスクリプト <script>alert(1)</script> script-src-elem できない できる できる
インラインイベントハンドラー <a onclick="alert('clicked')">link</a> script-src-attr できない できない できない
JavaScript URL <a href="javascript:void(0);">link</a> script-src-elem できない できない できない

a タグの href 属性に設定する javascript: で始まるスクリプト記述は script-src-attr ではなく script-src-elem で制御されるというのは押さえておきたいポイントです。

またこの整理の中で strict-dynamic 以外に script-src-elem と script-src-attr の両ディレクティブについても CSP Level 3 からの機能であることに気づきました。 CSP Level 3 に対応していないブラウザではこれらのディレクティブは単純に無視されます。

Strict CSP を CSP Level 3 に対応したブラウザに絞って適用する

上記の整理を行うことで、以下の CSP 設定を導き出すことができました。

Content-Security-Policy:
  object-src      'none';
  script-src      'report-sample' 'unsafe-inline' 'unsafe-eval' https: http:;
  script-src-elem 'report-sample' 'nonce-xxxxx' 'strict-dynamic';
  script-src-attr 'report-sample' 'none';
  base-uri        'none';
  report-uri      https://your-report-collector.example.com/;

この設定における整理は以下の通りです。

  • CSP Level 3 まで対応しているブラウザの場合
    • script-src-elem, script-src-attr によって Strict CSP と同等の効果が得られる
  • CSP Level 2 / CSP Level 1 まで対応しているブラウザの場合
    • script-src-elem, script-src-attr は無視され script-src のみが採用される
    • CSP による実行制限はない(保護されない)

Classiの運用ポリシーとしてインラインイベントハンドラーで違反となるスクリプトについては極力インラインスクリプト形式にリファクタリングをするべきとしています。リファクタリングが妥当でない場合と JavaScript URL については unsafe-hashes で許可をすることも認めており、 script-src-elem と script-src-attr の適切な方にそれぞれ hash を追加していくことになります。最終的には以下のような設定例になっていきます。

Content-Security-Policy:
  object-src      'none';
  script-src      'report-sample' 'unsafe-inline' 'unsafe-eval' https: http:;
  script-src-elem 'report-sample' 'nonce-xxxxx' 'strict-dynamic' 'unsafe-hashes' 'sha256-xxxxxx';
  script-src-attr 'report-sample' 'unsafe-hashes' 'sha256-xxxxxx';
  base-uri        'none';
  report-uri      https://your-report-collector.example.com/;

まとめ

この CSP 運用を続けていく中でわかったことは以下の通りです。

  • オリジンの許可リストベースではレポート運用負荷が高くなり、CSP バイパスという問題も含めると費用対効果の観点では微妙と判断せざるを得ない
  • Strict CSP ベースで Google Tag Manager を利用している場合に、一部ブラウザでの違反レポートが抑止できない問題が発生しうる

CSP のディレクティブを整理することで、 CSP Level 3 に対応したブラウザに限定されますが適切な設定方法が見出せました。行き詰まった時は一度立ち止まって仕様や実装状況を整理することも大事だということを痛感しました。

現在、 Strict CSP をベースにした前述のポリシーで数週間運用をしていますが、違反レポートのノイズも少なく運用負荷も気になるレベルではありません。 Report Only で運用をしていましたが、一部ページでは Report Only も解除し、実際に不正なスクリプト実行には CSP によるブロックがかかる状態にも問題なく移行できています。 CSP の適用範囲を広げ、よりセキュリティ強度の高い設定を目指していくのが次の目標です。

今後も CSP に関わる運用が続いていきますので、また共有できる事例があれば紹介したいと思います。読んでいただきありがとうございました。

Shinjuku.rb#92をClassiで開催しました

ソフトウェアエンジニア&Shinjuku.rbオーガナイザーの onigra です。5/31にClassiオフィスにてShinjuku.rbを開催いたしました。足元の悪い中ご参加くださった方々、本当にありがとうございました。

Shinjuku.rb とは、新宿周辺のRuby技術者たちが気軽に集まるコミュニティです。 ミートアップでは一方的な発表のみならず、ディスカッションを通じたインタラクションを大事にしているのが特徴の1つです。

今回の開催にあたり、同じくShinjuku.rbオーガナイザーである yuki3738さんが所属する 株式会社mov様より、フード&ドリンクスポンサーの協賛をいただきました。mov様は先日のRubyKaigi2024でもAfter Party Sponsorを行なっており、お世話になった方も多いと思います。心より感謝申し上げます。

Classiのエンジニアメンバーも設営兼ねて数名参加したのですが、今回、千葉県教育委員会から研修でお越しの小川先生がShinjuku.rbに興味をお持ちでしたので、参加いただきました。現役の学校の先生がプログラミングコミュニティに参加するのは珍しい機会だと思いますので、Classiらしさを出せたと思っています。

また、LTにはClassiメンバーのkozy4324さんと2024年新卒の伊森さんが参加してくれました。伊森さんは新卒らしからぬ小慣れた調子で発表しており、驚きました。

DevTools と デバッグ と 私(kozy4324

RubyKaigi行ってきたよレポ(いもり)

Shinjuku.rbはこれからも新宿界隈のRubyコミュニティを盛り上げていきたいと思います! 次回開催の際は是非お越しください!

dbtを導入した話、そしてClassiのデータ基盤「ソクラテス」の現在地

こんにちは、データプラットフォームチームの鳥山(@to_lz1)です。

Classiでは、2019年ごろからデータ基盤に「ソクラテス」の愛称をつけて運用を続けています。初期の構成は2021年に書かれたエントリ*1にも詳しいですが、数年の間に進化したことも増えてきました。

大きな変化の一例として、最近、私たちのチームではdbt*2を導入してジョブ間の依存管理やメタデータの管理を改善しました。

本記事ではこの取り組みをピックアップして紹介します。また、進化したソクラテスの構成図をアップデートするとともに、Classiデータプラットフォームチームの最新版の雰囲気もお伝えできればと思います。

dbt移行前の構成

正直に言ってしまえばdbt移行前の構成でも運用が崩壊するほどには困っていなかったのですが、個人的には大きく以下の2点に課題を感じていました。

  • ジョブ間の依存管理がつらい
  • メタデータの管理がつらい

以下、それぞれ説明します。

ジョブ間の依存管理がつらい

dbt導入以前のジョブはCloud Composer(Airflow)が提供するOperator*3が担っており、1クエリ1タスクになるような構成でした。

ところで、データマートのテーブルは材料となるテーブルがあって初めて作れます。そして材料となるテーブルもまた加工テーブルである可能性があるので、必然的にクエリ間には依存関係が生じます。移行前はこうしたクエリの依存関係を以下のようなyamlの設定ファイルに書いて対応していました。

- task_id: update_classi_dwh.students
  file_path: queries/dwh/students.sql
  destination_table: classi_dwh.students
  table_type: table
  write_disposition: WRITE_TRUNCATE
  dependent_tasks:
    - update_classi_dwh.schools
    - update_classi_dwh.users

生徒のdwhは、学校のテーブルとユーザーのテーブルに依存して作られる...納得感が高いですね。

dependent_tasksが比較的自明であるうちは良いのですが、これだとテーブル数が増えてきたときや、処理の中心に新たにテーブルを加えたくなったときに認知コストが非常に高いという問題があります。また、このアプローチでは依存タスクの記述漏れがあったときにも気づくことができません。依存関係を目で追ってそれを慎重にyamlファイルに反映していくことが作業時間を圧迫するようになってしまっては本末転倒というものです。

メタデータの管理がつらい

Classiでは、過去在籍していたメンバーがFlask-AppBuilder*4を用いたメタデータ管理システムを作ってくれていました。

tech.classi.jp

このシステムの功績は大きく、特にデータエンジニアでないメンバーが定義を書き込めるようになっていたことで、関係各所から「このテーブルのこのカラムはどういう意味?」といった重要なメタデータを収集することに成功しました。

しかしながら、独自管理Webシステムの限界もまたあります。

例えば、新たなテーブルをデータ基盤に追加したときにその説明をわざわざ書きに行くのか?というと微妙なところです。手間が勝ってしまうので充足率がいまいち高くならない、という課題もまた残っていました。

この点、dbtではmodelの定義ファイルにそもそもテーブルごと、カラムごとのdescriptionを記述できます。「新たなテーブルを追加するときに、そのPull Requestには同時にメタデータも書いてある」という世界なら、「リリース後にメタデータを書きに行く」という作業そのものが不要です。データ基盤の改善を加速させていくためにも、早くこのような構図に移行したい、というモチベーションがありました。

過去との差分と、移行への機運

実は、過去Classiではdbtに移行しようとしたものの諸々の状況を考慮して取りやめたことがあります。

tech.classi.jp

当時との状況の差分としては、以下のようなものがあると思います。

周辺ツールのエコシステムが整った

「dbtならyamlにdescriptionを書ける」と言っても、全テーブルの全カラムに手でdescriptionを付けていくのは実際のところかなり大変です。しかし、 dbt-osmosis のようなツールが出てきたことでこうした作業を効率化できるようになり、dbtのエコシステムに乗るメリットが大きくなってきました。

GitHub - z3z1ma/dbt-osmosis: Provides automated YAML management, a dbt server, streamlit workbench, and git-integrated dbt model output diff tools

dbt-osmosisについてはこちらのブログなどが詳しいため、詳細については割愛します。

dbt-osmosisを利用して、なるべくコストを抑えつつ効率的にメタデータ管理を行なう - yasuhisa's blog

エンジニア以外のメンバーがPull Requestを出すことが減った

Classiではかつて、データサイエンティストやビジネス側のメンバーもデータ基盤のリポジトリにPull Request(以下、PR)を出す、という光景が当たり前でした。しかしながら、エンジニアリングを熟知したメンバーばかりではなかったので、これが逆に新たなツールを導入することをためらわせる要因にもなっていました。

時が経ち、ビジネスメンバーからエンジニア陣へのドメイン知識の移転(≒dwh化)はある程度果たされたので、継続的にPRを出し続ける必要は無くなってきました。これにより、「学習コストが隣のチームの人達には厳しすぎるかも...」といった心配をする必要がなくなり、自分たちの裁量で新しいツールをより気軽に導入できるようになりました。*5

移行に際しての工夫点

上に述べたような状況を踏まえて、2023年末から2024年初頭にかけてdbtへの移行をやり切りました。移行においては以下のようなことを意識しました。

少しずつ行う

ビッグバンリリースではトラブル発生時の影響が計り知れないので、独立してリリース可能な範囲を切り分け、比較的難易度・複雑度が低いと思われるものから以下の順に移行を進めていきました。

  1. 社内でしか使わない、独立した小さいワークフロー群
  2. 社内でしか使わないが、データ基盤のコアとなるワークフロー
  3. エンドユーザーにも使われる、プロダクト機能に関連するワークフロー

また、社内データ基盤のコアを移行する際にはテーブルの棚卸しも実施しました。少数ですが、「移行せずとも2024年度になれば必然的に要らなくなる」といったテーブルも混じっていたため、こうしたテーブルはあえてdbt移行せず古いバージョンのまま残しました。

これにより、後述する差分確認の手間を減らしつつ、利用者がいなくなってから削除することでユーザーに影響を与えることなく古い処理系を一掃することに成功しました。

後から思ったことですが、このように徐々に移行を進めていくアプローチは、Martin Fowlerが提案したstrangler figパターンに近いものがあるかもしれません。

strangler fig パターンを実装してモノリシックなアプリケーションからマイクロサービスに移行するプロセスは、変換、共存、排除の 3 つのステップで構成されています。

strangler fig パターン - AWS 規範ガイダンス

差分確認を怠らない、でも楽にやる

さて、dbtへの移行ですが、クエリを移動して、必要な部分を書き換えて、全体のジョブが成功したら終わり...でしょうか?

いえ、それでは不十分なケースもあります。例えば処理は成功しているのにテーブルには差分が出ている...ということになると大変ですよね。データ基盤ではデータの正確性が重要な要素の一つですから、移行前後のデータの比較をしておけると安全です。

BigQueryでは、こうしたテーブル同士の比較はExcept句を用いた集合演算で実現できます。

Query syntax  |  BigQuery  |  Google Cloud

移行前のテーブルbefore、移行後のテーブルafterがあるとして、以下のようなクエリが結果を返さなければ、2つのテーブルは完全に一致していると言えるわけです。

select * from before
except distinct
select * from after

こうしたクエリの作業を全テーブルで行うのは大変なので、小さなシェルスクリプトを書いてチーム内に共有し、簡単に差分比較を行えるようにしました。実際はもう少し機能を追加しましたが、概ね以下のようなものです。

#!/bin/bash

# 比較元と比較先のテーブル名の組をCSVで作っておく
TARGET_FILE="./target.csv"
if [[ ! -f "$TARGET_FILE" ]]; then
    echo "File $TARGET_FILE not found!"
    exit 1
fi

project_id=$1
tables_with_diff=()

while IFS=, read -r compare base; do
    table_id_compare="${project_id}.${compare}"
    table_id_base="${project_id}.${base}"

    echo "Checking diff between ${table_id_compare} and ${table_id_base}."

    SELECT_MINUS_QUERY="\
    (select '${table_id_compare}' as table_name, * from ${table_id_compare} except distinct select '${table_id_compare}' as table_name, * from ${table_id_base} limit 3)\
    union all\
    (select '${table_id_base}' as table_name, * from ${table_id_base} except distinct select '${table_id_base}' as table_name, * from ${table_id_compare} limit 3)"

    result_rows_with_headers=$(bq query -q --nouse_legacy_sql "$SELECT_MINUS_QUERY" | tee /dev/tty | wc -l)
    if [[ $result_rows_with_headers -gt 0 ]]; then
        tables_with_diff+=("${table_id_compare} and ${table_id_base}")
    fi
done < $TARGET_FILE

if [[ ${#tables_with_diff[@]} -gt 0 ]]; then
    echo "Tables with diff:"
    for table in "${tables_with_diff[@]}"; do
        echo "- ${table}"
    done
    exit 1
else
    echo "No diff found with $(wc -l < $TARGET_FILE) table pairs✨"
fi

スクリプトを流した結果をrevieweeがPRに貼ることで、差分がないことについてはわざわざSQLをレビューしなくても分かります。これにより、それ以外の場所、例えばmacroの設計や命名、過去遡及時の冪等性の確保といったより重要な部分のコードレビューに集中できました。

反省点

以上述べたようにスクリプト等も駆使しつつ大きなトラブルなくdbt移行を終えたのですが、一部テーブルでCluster列*6の設定を忘れたまま移行してしまい、移行直後のクエリコストが急騰するという問題が発生しました。

幸い数日で気づけたので影響は小さかったのですが、皆さまに置かれましてはデータだけでなくメタデータ(descriptionに限らず、labelやpartition設定等も含む広義のメタデータ)もきちんと移植できているか、という観点も忘れられませんようご注意下さい。

ソクラテスの現在地

dbt移行などいくつかの刷新を経て、Classiのデータ基盤は今このようなアーキテクチャになっています。

Classiのデータ基盤構成図

ジョブの依存関係の管理の他、メタデータの提供もdbtの機能を使って実現できたのが大きな改善点です。

dbtをDockerizeされたジョブとして動かすようにしたため、model間の依存関係がComposer上では見れなくなってしまったのですが、dbt Docsを確認すればmodelごとのlineageグラフを可視化出来るので、大きな問題は出ていません。

dbt Docs上で可視化されたデータリネージュの一部

先述した独自のメタデータ管理システムについても、クローズする前にすべてのデータをCloud SQLから吸い上げ、dbtのConfigに反映する対応を行いました。これにより、これまでメンバーが書いてくれた有益な情報をdbt Docs上に集約でき、データカタログの利便性も向上したと思います。

dbt移行の他に大きい差分としては、ログのBigQueryへの連携を毎時のバッチで動かすようになりました。

実はそれまではAWS Athenaをベースに作られていたログ分析基盤を使っていました。それはそれで便利な点もあったのですが、AWS内に独自にホストされたRedashがメンテナンス不能になっていたり、BigQuery上のデータともjoinできないなど、負の側面も強くなっていました。このため、2024年初頭にBigQueryへの連携パイプラインを構築し、旧システムも先日すべて無事に削除しました。

これらの取り組みによって、RDBデータはdaily、ログデータはhourlyで、それぞれBigQuery(a.k.a. ソクラテス)に安定して供給される状態が実現できました。

今後の展望

Data Catalog as a Code

dbtによってメタデータの管理性は向上しましたが、今後のメンテナンスが本当に円滑に行われるか、という点では更なる工夫が必要です。メンテナンス業務にあたっては、descriptionをあちらにもこちらにも書かないといけない...といった二重管理を避けることが何より重要ではないでしょうか。

これに関しては、RDBのテーブルと加工テーブルのメタデータをそれぞれ1回記述すれば他のすべてのコンポーネントに反映されるような仕組みを構築中です。目指したい構成は以下のようなものです。

順を追って説明します。

  1. データエンジニアおよびデータ供給者(プロダクトチームのエンジニア)は、RDBのテーブルをBigQueryに連携するときにdescriptionを記述する
  2. bq load時に、書かれたテーブル定義を参照して実テーブルにメタデータが付与される
  3. dbt-source-importerによって、sourceテーブルのメタデータがymlに反映される
  4. dbt-osmosisによって、sourceテーブルのメタデータは加工テーブルのymlにも反映される
  5. データエンジニア及びデータ利用者は、集計カラムなど加工テーブル独自の列があればそのdescriptionを記述する
  6. dbt build時に、ymlを参照して実テーブルにメタデータが付与される
  7. dbt docs serveの定期的な実行により、最新のデータカタログが社内に公開される

dbt-source-importerが初出なので説明しておきますと、これはBigQueryの実テーブルからsourceの設定ファイルの内容を生成してくれるCLIツールです*7。sourceのメンテナンスについて、dbt-helperやdbt-osmosisなど公式に近いツール群ではかゆいところに手が届かないケースがあるのですが、こちらは必要な機能が軽量にまとまっているため重宝しています。

余談ながら、先日機能追加のPRを送った際も迅速に対応いただき、非常にありがたかったです。この場を借りて感謝いたします!

github.com

上図のような世界が実現できれば、人間の手が必要な作業は「RDBのテーブルのメタデータの記述」「加工テーブルのメタデータの記述」だけになります。あとは各ツール群が実テーブルにも設定ファイルにも、データカタログにも最新の状態を反映してくれるという算段で、細かい部分を調整しつつこうした自動化を進めています。

ここに書き切れないさまざまなこと

本記事では技術的なトピックに焦点を当てて来ましたが、ここに書き切れない施策もたくさん行っています。例えば、私のチームが属するプラットフォーム部では、より事業成果に直結する取り組みを増やすべく、全チームそれぞれがプロダクトのKPIに直接コミットするような目標を盛り込みました。

これに伴い、データプラットフォームチームでは例えば、

  • KPIのモニタリングと提言
  • 施策の立案と効果検証
  • ユーザー向け調査の設問設計

などビジネス的にチャレンジングな活動にも取り組み始めています。KPIをタイムリーに測定するためのデータマートの整備もアナリティクスエンジニアリングの一環ですから、当然我々の守備範囲です。データとビジネスが重なることは何でもやる。という覚悟で引き続きやっていきます。

We are hiring!

dbt移行プロジェクトの詳細と、データプラットフォームチームの近況についてお話してきました。何でもやる、とは言いましたが、現在データプラットフォームチームは4月からリーダーとなった筆者を含め3名しかおらず、やりたいことに対しては手が足りていない状況です。

データエンジニアリングもアナリティクスエンジニアリングも存分に行える環境が揃いつつあるため、Classiのデータプラットフォームチームに興味をお持ち頂いた方はカジュアル面談や面接にエントリー頂ければ嬉しいです!

hrmos.co

*1:https://tech.classi.jp/entry/2021/05/31/120000

*2:https://www.getdbt.com/

*3:https://airflow.apache.org/docs/apache-airflow-providers-google/stable/operators/cloud/bigquery.html

*4:https://flask-appbuilder.readthedocs.io/en/latest/

*5:かつてPRを出してくれていたメンバーはSQLやBIツールを使って今もガンガン力を発揮してくれていますし、dbt Docsへのフィードバックやデータ項目の追加依頼をもらうことで間接的にデータ整備にも貢献してくれています。PRが頻繁に飛んでくる文化も素敵とは思うのですが、形を変えて引き続き良い関係が築けているため、自分はこの変化はポジティブに捉えています。

*6:https://cloud.google.com/bigquery/docs/clustered-tables?hl=ja

*7:https://www.yasuhisay.info/entry/2022/01/22/121000

© 2020 Classi Corp.