Classi開発者ブログ

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

ポストモーテムを読む会を開催しました

こんにちは。ソフトウェアエンジニアのいもりです。

研修を終えた後*1、新卒OJTの一環として欠席連絡チームに所属しています。

先日、社内で「ポストモーテムを読む会」を開催しましたので、その様子をお伝えします。

ポストモーテムを読む会とは?

「ポストモーテムを読む会」とは、過去に発生した障害を記録した文書(ポストモーテム)を第三者である私が読み、その内容をもとに当事者と意見交換を行う場です。具体的には、ポストモーテムを読んだ感想を伝えたり、ネクストアクションについて質問したり、対応を通じて得られた教訓を共有してもらいます。

今回はClassiの欠席連絡機能に関するポストモーテムを取り上げ、その内容について当事者と話し合いました。

ポストモーテムの構成と注目したポイント

ポストモーテムは以下の構成でまとめられていました:

  • 概要
  • 対応メンバー
  • 時系列まとめ
  • 振り返り
  • ネクストアクション

ポストモーテムの当事者と意見交換する場において、会を自分にとってもより充実した場とするために、ポストモーテムを読む際には以下の視点を意識しました:

1. 障害対応を通じて良かったアクション

どの行動が成功につながったのか、ポジティブな側面を見つけること。

2. 「良かった」とされる点への再検証

振り返りで「良かった」と評価された点が本当に効果的だったかを考えること。

3. ネクストアクションの実行状況の確認

記載されたネクストアクションが適切に進められているかを確認すること。

4. その他、率直な感想や疑問

素朴な感想や新たに浮かんだ疑問について投げかけること。

ポストモーテムの構成と注目したポイント

1. 障害発生に対する迅速な対応力

まず、メンバーの障害発生時の対応力が非常に高いと感じました。

今回の読む会で扱った障害では、調査開始からわずか10分で原因を特定し、収束に至っています。また、1時間後には障害発生のお知らせを公開しており、この迅速さには驚かされました。

背景には、エンジニアメンバーのアラートに対する高い意識があると考えられます。アラートチャンネルにログが流れた際、エンジニアメンバーはすぐに反応し、調査を始めています。興味深かったのは、初動のアクションが「確認します」など非常にシンプルな発信から始まることが多い点です。

会では「些細なことでいいからアラートに反応する習慣を身につけよう」という話題も挙がりました。些細な発信でも、アラートを認知している意思表示となり、その発信を見た詳しいメンバーが共に対応してくれる効果があります。これにより、アラート対応が特定の個人に依存せず、組織全体で支え合う仕組みが形成されていることが分かりました。

ちなみに、アラート対応に関連して「朝当番」という社内文化もありました。直近のブログ記事で詳しく紹介されているので、興味があればぜひ読んでみてください。

tech.classi.jp

2. ドキュメントの透明性と活用の課題

もう一つ素晴らしいと感じた点は、失敗を風化させず、第三者でも気軽にアクセスできる形でドキュメントが共有されていることです。

今回読んだポストモーテムは社内ドキュメントツールであるesaに記載されており、社員であれば誰でも閲覧可能です。また、エンジニア組織の定例で直近で行われたポストモーテム事例の共有も行われています。過去の失敗や得られた教訓が組織全体の共有財産となることで、同じ過ちを繰り返さない取り組みが行われています。障害事例や過ちが公開されているということは、問題が新たに発生した場面において、問題を隠そう、小さく一部だけに伝わるようにしよう、といったネガティブな文化がほとんどないという環境だということです。

しかし一方で、課題も見えてきました。今回のような機会は頻繁に設けられているわけではなく、ポストモーテムを読むかどうかが個人の判断に委ねられている現状があるという指摘がありました。そのため、せっかくの知見が十分に活用されていないのでは、という課題感が共有されました。

当たり前のことに気づいていなかったのですが、ただポストモーテムを「読める場所に置いておくだけ」では不十分だということです。過去の障害や対応策を生かすためには、今回のようなイベントを継続的に実施し、知見を組織全体で活用する仕組みづくりが求められていると感じました。

3. リリースタイミングと対応における教訓

今回扱った障害は、金曜日に実行したDBマイグレーションが原因で、土曜日に発生したものでした。 幸い、休日にもかかわらずDB管理者をはじめ有志のエンジニアが迅速に対応してくださったため解決に至りましたが、もし誰も反応できなかった場合、休日の間中エラーが発生し続けていた可能性もありました。

リリース後に問題が発生した場合、すぐに対応できない状況だとトラブルが長引いてしまうリスクがあります。今回のような特に影響範囲が大きいDBのリリースについては、対応可能な時間帯を選定すること、リリースによる影響を把握しておくこと、リリース後のフォローアップ体制を整えて、仮にトラブルが発生しても被害を最小化できるように備えておくことが重要だと認識しました。

今回のDBマイグレーションの失敗を踏まえ、社内のドキュメントには新たに「マイグレーション実施時の注意点」という項目が追加されました。以下が実際に追加されたドキュメントです。根本的な解決には至らなくとも、できる対策をすぐに実践する姿勢は非常に学びがあると感じました。

会では「愚者は経験から学び、賢者は歴史から学ぶ」という言葉を引用しながら、DBに関しては特に「考古学」の視点が重要だという話がありました。過去の失敗から学び、未来に生かす姿勢を持ってほしい、というアドバイスが印象的でした。

今回の会を通じて、ポストモーテムなどの過去の事例から学ぶことの重要性を改めて意識するようになりました。また、リリースに対して対応できる時間や影響範囲について意識できるようになりました。まだ自分の失敗から学ぶことが多い日々ですが、少しずつ「歴史から学べる賢者」を目指して成長していきたいと思います。

おまけ

余談ですが、「ポストモーテムを読む会」を計画していた当日の午後に、偶然障害が発生しました。エラー発生手順を調査して社内に報告したところ、「良いアクションだった」と褒めていただけました。

結果的に、障害の原因は私が報告したエラー発生手順と関係ありませんでしたが、その時褒めてもらえたことで、自分自身も「次も主体性を持ってアラートに向き合おう」という意識が育まれた気がします。

Classiでは、「良かったことを素直に褒める」文化が根付いています。褒められた側はもちろん、褒めた側もお互いが「次も頑張ろう」という前向きな気持ちが生まれ、仕事の中で良いサイクルを実感しています。

これからも、社内で良い影響を与えられるよう、日々小さなことから積み重ねていきたいと思います。

*1:新卒研修のブログ記事はコチラ https://tech.classi.jp/entry/2024/09/06/163000

(イベント参加レポート)【Edtech Meetup】急成長サービスの秘訣と実践戦略

こんにちは、プロダクト本部プラットフォーム部SREチームの坪井(@boy2)です。当社の伊藤が2024年11月27日(水)にアマゾン ウェブ サービス ジャパン合同会社(以下、AWS)で開催された「【Edtech Meetup】急成長サービスの秘訣と実践戦略」のパネルディスカッションへ登壇しました。複数のEdtech企業のCxOと業界の特性や急成長の秘訣について語りました。今回はそのイベント模様の一部をご紹介します。

パネルディスカッション

オープニングの挨拶の後、3人のパネリストの自己紹介と会社紹介が行われました。テーマとしては、どのようにサービスを広めていったのかというテーマにてパネリストたちが回答してくれました。

左はファシリテーターのAWS 尾島氏 右は伊藤

Classiでは「tetoru」の成功事例を挙げています。

tetoruが急速に普及した要因

  • コロナ禍の影響: 新型コロナウイルス感染症の拡大により、オンライン学習の需要が急増。tetoruのようなクラウド型プラットフォームが注目されるきっかけとなりました。
  • 高いユーザー満足度: NPS(Net Promoter Score)*1が高いことからも分かるように、tetoruはユーザーから高い評価を得ています。(参考資料)
  • 口コミによる拡散: ユーザー満足度の高さから、口コミで広がり、多くの学校に導入される結果となりました。

Classiのサービスで経験したシステム障害の教訓を踏まえ、tetoruの開発を進めたと述べています。特に、スケーラビリティの確保や、突発的なアクセス増加への対応などを重視し、AWSサービスを活用することで、安定したサービス提供を実現しています。

続いて会場に来ている参加者からの質問にパネラーたちが回答しました。

Classiへの質問に対して、伊藤が回答しました

  • 質問:離脱校対策について
  • 伊藤の回答:具体的な数値は公表できませんが、生徒一人ひとりの状況を深く理解し、適切な支援を行うことが重要です。生徒への理解を深めるために、Classiではプロダクトの改善を日々行っています。生徒一人ひとりのニーズに合ったサービスを提供することで、離脱校を減らすことを目指しています。
  • 質問:Classiとtetoruの導入における課題
  • 伊藤の回答:Classiでは、生徒や先生のアカウント発行など、初期設定がやや複雑です。このため、導入サポートを強化しています。tetoruでは、シンプルに設定できるように設計されていますが、教育現場ではデジタルに慣れていない人が多いため、手厚いサポートを行っています。

会場では語られなかった具体的なエピソードを伊藤にヒアリングしました

「Classiでは、多層化する学力の生徒向けに個別最適な学習体験を提供する学習トレーニングの機能を開発・運用しています。学校の授業における理解度もばらついており、先生方が一人一人に細やかに対応することも限界があるため、Classiでは生徒一人一人の解答状況に応じて個別の問題を推薦する機能などを提供してきました。最近では、問題が解けた=できたという体験だけではなく、その学習単元が理解できる=わかった体験を提供するために動画サービスを連動させ、一人一人の学習を支援しています。」

詳細はプレスリリースにも掲載していますのでぜひご覧ください。 他のパネリストのお話も大変興味深いものでした。パネルディスカッション後は、EdTech企業の皆様によるLTも開催され、AIによる学習サポートやプログラミング学習での課題など、興味深い話題が盛りだくさんでした。AWSによる開催レポートが公開されていますので、詳細についてはそちらをご覧ください。

成長を支えるAWSサービスのご紹介

イベントの最後には、3人のAWS Solutions ArchitectによるAWSサービス紹介がLT形式で実施されました。一挙にまとめて紹介したいと思います。

  • Amazon Bedrock*2: モデルのアップデート(Claude Updates: Sonnet 3.5 v2, Haiku 3.5)、生成AI処理フローのオーケストレーション(Amazon Bedrock Prompt Flows)、責任あるAIのさらなる一歩(Guardrails for Amazon Bedrock)など、最新情報が紹介されました。

  • Redshift ML*3: 自身のデータで生成AIアプリケーションを作成したいというニーズに応え、Redshift MLとAmazon Bedrockの統合について紹介されました。zero-ETLとRedshift MLを組み合わせることで簡単に自身のデータを使った生成AIアプリケーションを構築できるようになります。

  • ガバナンス&セキュリティ: AWS Trusted Advisor*4の再点検など、すぐに実行できるセキュリティ対策の事例が紹介されました。ガバナンスとセキュリティのベストプラクティスを活用し、AWS環境をより安全に利用する方法が提案されました

特に、Redshift MLとAmazon Bedrockの統合は、生成AIの活用を加速させる画期的な取り組みと言えるでしょう。また、AWS Trusted Advisorを活用したセキュリティ対策は、全てのAWSユーザーにとって重要なことです。さっと確認することができますので、年末までに一度、ご自身の環境を見直してみてはいかがでしょうか。

最後に

今回のEdTechイベントは、改めて自社のサービスへの注目度の高さを感じました。AIによるパーソナライズドラーニングやオンライン教育の普及など、教育業界の最前線を垣間見ることができ、大変刺激的でした。参加者の熱量も高く、EdTechに対する注目度の高さが伺えました。

EdTech MeetUpは、業界を牽引するイベントとして、ますます盛り上がっていくことでしょう。次回の開催も楽しみにしています。

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


*1:ネットプロモータースコア(NPS)とは、顧客が自社の商品やサービスを友人や同僚にどれくらい薦めるかを数値化した指標です。例えば、あるECサイトのNPSが80%の場合、顧客の80%がそのサイトを積極的に人に薦めていることを示し、顧客ロイヤルティが高い状態であると評価できます。NPSは、顧客満足度を測り、自社の改善点を見つけるために活用されます。

*2:Amazon Bedrockは、様々な生成AIモデルを簡単に利用できるAWSのサービスです。テキスト生成や画像生成など、多様なタスクに対応でき、自社のデータでモデルをカスタマイズすることも可能です。

*3:Amazon Redshift MLは、Amazon Redshift データウェアハウス上で、SQLクエリを使って機械学習モデルを構築・トレーニングし、予測を行うことができるサービスです。

*4:Trusted AdvisorはAWSの無料サービスで、AWS環境を継続的に監視し、コスト削減、パフォーマンス向上、セキュリティ強化などのための推奨事項を提供します。

Classiの教科コンテンツ制作 - 紙教材をデジタルでも -

こんにちは。教材コンテンツディレクターの今井です。

教材コンテンツディレクターとは、一言でいうと「Classiの教科コンテンツの企画・制作」を担当している職種です。今回はその中でも、特に「教科コンテンツの制作」についてご紹介します。

教科コンテンツ制作の流れ

Classiの教科コンテンツ制作は図のような流れで、問題の出題内容や原稿制作は編集プロダクション、システムにコンテンツを搭載する作業はデータ入力会社など、社外とも協業しながらの制作を行っています。社外にも関係者が多いため制作進行管理を丁寧に行う必要がありますし、企画の主旨に沿った出題内容になるように編集プロダクションとはコミュニケーションをしっかり取りながら制作を進めています。

Classiの教科コンテンツ制作の流れ

紙教材をデジタルでも

Classiでは、教材の質を担保するためにも、学校の先生方から長らく評価いただいている既存の教材に掲載されている問題をベースとしたコンテンツ制作も行っています。 とはいえ、紙だから実現しやすいことと、デジタルだから実現しやすいことには違いがあるので、「そのまま乗せ換える」のではなく、紙教材の特徴を踏まえながらデジタル教材に落とし込んでいく必要があります。紙教材では当たり前にできた体験をどのようにデジタル教材に昇華していくか、教材コンテンツディレクターそれぞれがもつ教材制作の知見や教科知識を集結しながら企画ごとに最適な形を模索しています。

企画内容や各教科の特性にあわせて都度検討を行っているので、下記で紹介するのはあくまでも一例ではありますが、教材コンテンツディレクターがどのように教科コンテンツに向き合っているかご紹介します。

今回紹介するのは、義務教育範囲の学習のつまずきが残っている生徒の学び直しのための紙教材をもとに、Classiで制作したデジタル教材(数学)についてです。

学習の流れ

もとの紙教材は以下のような流れで学習を進める教材でした。

  1. 見開きページの冒頭にある学習のポイントを確認
  2. 問題演習の手前に掲載されたヒントや例題で具体的な解法や考え方を確認
  3. 実際に複数の問題に取り組む(このとき、同じページにヒントがあるので、生徒は必要に応じてそちらを確認しながら解答できる)

もとの紙教材の構成

Classiをスマホで利用する生徒さんも多く、紙教材1枚をそのまま1画面に収めるとスクロール量が多くなるため、問題1問あたり1画面になるよう分割して教科コンテンツを制作します。
このとき、ヒント1/問題1-1/問題1-2/……と単純に要素単位で画面を分割してしまうと、もとの紙教材がもつ「学習のつまずきが残っている生徒でも、ヒントを確認しながら問題に解答できる」という特徴を損なうことになります。
そのため本企画では、次の図のように問題内にヒントを掲載したアコーディオン形式のコーナーを新設しました。

ヒントを確認できるコーナーを新設

一方、紙教材では2問目以降の問題も1問目と同様にヒントを見ながら解答できる体験でしたが、少しずつステップアップするような体験を届けたいと考え、デジタル教材では2問目以降はヒントなしで取り組む体験に変更しました。

Classi上での学習の流れ

例えば、数学の「2桁+2桁の加法(足し算)」の学習では、以下のような流れで学習を進めます。

  1. 学習のポイントで2桁+2桁の計算について概要を確認する
  2. 「百の位の繰り上がりがない加法」のヒントを確認しながら問題1-1に取り組む
  3. 「百の位の繰り上がりがない加法」の類題(問題1-2、1-3……)にヒントなしで取り組む
  4. 「百の位の繰り上がりがある加法」のヒントを確認しながら問題2-1に取り組む
  5. 「百の位の繰り上がりがある加法」の類題(問題2-2、2-3……)にヒントなしで取り組む

「2桁+2桁の加法(足し算)」の場合のClassi上での学習の流れ

出題形式・解答形式

学習の流れと同様に、デジタル教材は問題の出題形式や解答形式に紙教材とは異なる制約が出てくる場面があります。
もとの紙教材は、計算過程や答えの数値を教材に直接書き込む記述形式の問題が中心で、グラフ作図問題も一部含まれていました。そのような教材をどのように工夫してデジタル教材に落とし込んでいったのかをご紹介します。

記述問題の場合

スマホ利用の生徒さんでも簡単に解答できるように選択式に改題することが多いです。
この場合、問題文はそのままでも、選択肢(特に誤答の選択肢)を新たに作成することになります。実際の生徒さんが誤った考え方や計算でたどり着く可能性が高そうな誤答を盛り込みつつ、選択肢を作成しています。
画像の例では、「繰り上がりがある加法」の問題なので繰り上がりを忘れている「125」の誤答を用意しています。

繰り上がりを忘れた「125」の誤答を用意

作図を含む問題

紙教材なら、教材上に印刷されたグラフに書き込みながら取り組むことができますが、デジタル教材(特にスマホ利用)では紙のように直接教材上に作図するのは難しくなります。
このような場合、作図を伴う問題は掲載しない判断もあり得ますが、せっかくの学習機会を奪ってしまうことになるので、紙教材に近しい学習機会を提供できないか検討します。

数学のグラフ作図を含む問題では、もとの紙教材ではどのような過程を踏んで正答にいたるのか?に着目することが多いです。
例えば「3x−2y−8=0のグラフをかきなさい。」という問題であれば、

  1. y=ax+bの形に変形する
  2. y切片と傾きの値を把握する
  3. グラフに作図する

の3段階を踏むので、デジタル教材では全体の考え方の流れや問題の難易度を壊しすぎないように、

  1. y=ax+bの形に変形する
  2. y切片と傾きの値を把握する
  3. 選択肢のグラフの中からy切片と傾きが一致するものを選ぶ

のような過程で解けるグラフ選択形式の問題に改題する……といった形で対応しています。
前述の記述問題の場合と同様にこちらも選択問題になるので、1段階目の式変形で正負を誤った場合の誤答を用意しています。

作図を含む問題はグラフ選択形式に改題

さいごに

ここまで義務教育範囲の学習につまずきが残っている生徒向けの学び直しの教科コンテンツ(数学)制作を例に、Classiの教科コンテンツ制作をご紹介しました。実際にこちらのコンテンツに取り組んだ生徒さんからは「解答直前にヒントがあるので自分で考えながら取り組める」「自分でも取り組める難易度でよかった」といったお声もいただきました。「学習に対して苦手意識のある生徒さんにも取り組みやすい」という紙教材の特徴を残しつつデジタル端末でも取り組みやすいコンテンツを作成することができたのではないかと考えています。

企画の主旨や各教科の特徴を考慮しながら、今後も、ご利用いただく学校・先生・生徒のみなさまに寄り添った教科コンテンツを制作・提供していきたいと考えています。

なお、今回ご紹介した教材コンテンツディレクターのメンバーを募集中です。ご興味をもっていただけましたら、気軽にカジュアル面談等お問い合わせください。 https://hrmos.co/pages/classi/jobs/0509

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

dbt周辺ツールを使った「圧倒的に楽」なメタデータ管理

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

本記事はdbt Advent Calendar 2024の10日目の記事です。

データ基盤を扱う上で避けて通れない「メタデータの管理」ですが、あなたのチームではどのように取り組まれているでしょうか?独自メタデータカタログを作ったけど、運用がつらくなっているとか、はたまた構築初期なのであえてdescriptionなしのまま突き進んでいるなど、データ基盤整備のフェーズによっても各社さまざまかと思います。

弊社も例に漏れず上記のようなフェーズを通過してきましたが、最近ではdbtやその周辺ツールを使った効率化がうまく回り始めてきました。本記事ではその仕組みをどのように構築したのかをご紹介します。

  • そもそもメタデータとは?
  • 旧来の仕組み
  • 新しい仕組み
    • dbt-osmosisの導入
    • dbt-source-importerの導入とCI化
    • 大元のRDBからのメタデータ吸い上げ
    • 最終的な形
  • 今後の展望
続きを読む

朝当番をやめた話

こんにちは。プロダクト本部プラットフォーム部SREチームのid:ut61zです。

サービスを運用していくうえで監視は避けて通れません。
Classiでは長らく朝当番という制度を設け、平日朝のピークタイムにエンジニアを監視担当としてアサインし、何らかの問題が発生した際、即座に対応できる体制を整えてきました。

2020年9月からスタートした朝当番制度ですが、2024年4月をもってこの制度を終了しました。

今回はその経緯や理由、現在のClassiの運用体制などをご紹介します。

朝当番とは

あらためて朝当番制度とは、平日朝のピークタイムにエンジニアが常に1名待機し監視する制度になります。担当エンジニアは各種メトリクスに異常がないかチェックし、問題や気になったことがあれば関係各所に情報連携を行う役割を担います。

朝当番制度の目的、背景、期待される効果など、以下のブログに詳細が記載されていますので興味があればぜひご覧ください。

tech.classi.jp

上記ブログ執筆時点(2022年)でもすでに19人のエンジニアが朝当番を経験し、今日に至るまでには述べ51人のエンジニアが朝当番を担当してきました。

Classiのシステム、そしてエンジニア組織の変化と共に朝当番もアップデートされてきました。

遍歴を追ってみましょう。

朝当番の遍歴

朝当番発足当初の運用は以下のようなかたちでした。仮にV1としましょう。

朝当番運用 V1

  • 朝当番担当を5人募集し、平日の各曜日に1人アサイン(3ヶ月で交代)
  • 朝当番担当は、08:00~09:30の時間帯、Datadogを見ながら異常がないか確認。Datadogのmonitorやダッシュボードの改善、その日のピークアクセス数や気になった点などをドキュメントにまとめる等を実施
  • Infra Performance Working Group(以下 Infra PWG) という会を隔週で実施し、朝当番のふりかえりを行い、朝当番をブラッシュアップしていく

しばらく運用を続け、アラートに反応できるメンバーが増えてきたなかで「サービスダウンが発生することはなく、朝当番中にインシデント対応する機会もほぼなくなった」という声があがるようになってきました。
そこで、朝当番担当の時間を短縮、さらに毎日ドキュメントにまとめていた内容を簡素化し、ピークアクセス数、気になったこと、次の曜日の担当者への申し送りをSlackに記述するという運用に変更しました。V2です。

朝当番運用 V2

  • 朝当番担当を5人募集し、平日の各曜日に1人アサイン(3ヶ月で交代)
  • 朝当番担当は、08:15~08:45の時間帯、Datadogを見ながら異常がないか確認。可能であれば、Datadogのmonitorやダッシュボードの改善、その日のピークアクセス数や気になった点などをSlackで報告
  • Infra PWG で朝当番のふりかえりを行い、朝当番をブラッシュアップしていく

同時に朝当番の目的のひとつであった担当エンジニアの育成は徐々に Infra PWGで吸収し、参加者の学習機会となるようにアップデートしてきました。
後ほど詳細について触れようと思います。

さて、朝当番経験者が増え、2周目、3周目を担当するエンジニアも現れ始めました。
朝当番担当でなくてもピークタイムにアラートが発砲されたときは、担当チームのエンジニアが自然と反応し、対応するケースも多く見受けられるようになってきました。
朝当番を経験し、どう振る舞えばよいかが各チームのエンジニアに知見として根付いてきたと言ってよいでしょう。
さらに朝当番の運用を縮小しました。

朝当番運用 V3

  • 朝当番担当を3人募集し、月・水・金に1人アサイン(3ヶ月で交代)
  • 朝当番担当は、08:15~08:45の時間帯アラートがあれば反応して確認、担当チームにSlackで確認
  • Infra PWG で朝当番のふりかえりを行い、朝当番をブラッシュアップしていく

火・木は朝当番なしでしばらく運用をしてみました。

朝当番をやめる

徐々に縮小していった朝当番制度ですが、朝当番がいない曜日があっても特別不安を感じなくなってきました。

朝当番制度のそもそものモチベーションはなんだったのか振り返ると、以下2点が大きな割合を占めていました。

  • 朝のピークタイムに不安定な状態になることが多発しており、エンジニアが張り付いて対応をスムーズに行えるようにしたかった
  • アラートに反応できるエンジニアがシニアエンジニアに偏っていたため、対応できるエンジニアを育成する機会を設けたかった

この2点は大きく改善されました。

ピークタイムの安定状態の獲得

朝のピークタイムはかなり安定している状態を維持できるようになってきました。

Classiのサービスは、複数のアプリケーションコンポーネントで構成され、多くのコンポーネントがAmazon ECS Fargate上で稼働しています。

ピークタイムにサービスダウンするような事態は解消されつつも、アクセスが急増することでコンポーネントのうちのいずれかが不安定な状況に陥ることはしばしば発生していました。

特に Fargate タスクのスケールアウトが間に合わず、レイテンシの悪化や接続エラーを招くような事態が頻繁に発生していました。

対して、各チームが自チームのECSサービスに対して以下のような取り組みを実施しつつ微調整を重ね、最適化を進めてきました。*1

  • オートスケーリングのターゲット追跡ポリシーの調整
  • 時間指定でのオートスケーリングの導入
  • タスク数を一気に増やすステップスケーリングの導入

最近では、直接的なコンピューティングリソースやリクエスト数とは別の、独自のCloudWatch Alarmを作成・利用してスケーリングを実現するなど、さらなる改善に取り組む事例も出てきています。それについてはまた別の機会に触れたいと思います。

また、Infra PWGで、年間のおおよそのアクセス数のトレンドを把握し「来週はアクセス数が例年スパイクする時期なので事前にECSタスク数を増やしておこう」といった会話がなされ、緩やかに人為的な調整も行っています。
これは、Classiが学校向けのサービスということもあって、比較的年間イベントサイクルが安定しているからできることではありますが、学校の年間イベントの理解を深めることが安定性に寄与するという体験は、事業会社ならではという感触があります。

そのようなシステム面、運用面での改善を重ねた結果、現在の安定した状態を維持できています。

アラートに反応できるエンジニアの増加

各プロダクトチームのエンジニアの監視への理解や姿勢が成熟し、アラートに反応できるエンジニアが増えました。

朝当番としてもっとも称賛されるべき振る舞いは「なにかおかしい、誰か見て!」と誰よりもはやくSlackのオープンチャンネル等、社内のメンバーにすぐ伝わる場で発言することです。
そうすることで、アラート対応に巻き込まれることこそ朝当番の肝だと私は思っています。
当事者になることで、仮に自身ではどこからどう調査すべきかわからなかったとしても、その後他の誰かと一緒にメトリクスを見て原因を突き止めていくという体験をすることが、サービス全体の理解や、メトリクスの見方の学習に繋がります。

そのような学習の積み重ねにより、現在ではSREやシニアエンジニアが反応せずとも、各チームでアラートに対応することが当たり前になってきています。

場合によっては、CSチーム等、関係部署への情報連携を要求されることもあります。
Classiでは「障害報」と呼んでいますが、問題発生時、起こっている事象をまとめ、すばやく関係部署に情報連携する運用を立て付けています。
朝当番として障害報を発する機会を得たエンジニアは、有事の際どうアクションすべきかという、ある種の筋肉が鍛えられてきたと言えるでしょう。

他にもアラートのメンテナンス(不要なものの削除、しきい値の調整、メンション先を都度適切なチームに変更するなど)が各チームのエンジニアにて、日常的に行われるようになってきました。

それらの状況を踏まえ、朝当番を終了することを決定しました。

やめてみて

朝当番を終了してみて半年以上経ちましたが、朝当番がいなくて対応が遅れたということは今のところ起きていません。
特に警戒している学校の長期休暇明けの日であっても、問題も起きず安定したサービス提供ができています。

もちろんアラートがゼロになったわけではないし、まだまだ信頼性やパフォーマンスの改善の余地はあります。

しかしアラートに誰も反応できないということは少なくとも起きておらず、常に担当チームによって適切な対応がなされていると言えます。

また、チーム単位でInfra PWGが開催されるなど、チームによる自律的な運用体制の強化も進んでいます。

プラットフォーム部SREチームとして

現在SREチームは、監視運用体制という観点では、Infra PWGにて各チームが監視運用をスムーズに行えるようにアラートの原因を深堀りして知見を共有したり、Datadogの便利なTipsの紹介や年間イベントの確認などを行っています。

広くエンジニアが監視運用に携わるというステージからステップアップし、各チームの運用力を洗練させていくということがSREチームに求められていると思っています。
プラットフォーム部として、各チームの運用力を継続的に底上げすることに今後さらにリソースを割いていきたいです。

また、ここまで触れてきていませんでしたが、昨年からSLI/SLOを段階的に各コンポーネントに対して定義していくことを主導しています。
今後の展望としては、SLI/SLOとエラーバジェットをベースにしたアラートの構築・運用を強化していくことを推進したいと思っています。

さいごに

朝当番は、人的リソースを充ててサービスレベルを底上げする取り組みでもありました。

人的リソースを割く以上そこには少なくないコストが発生しています。
単純なエンジニアの稼働時間というのに加え、フレックスに働けるという働きやすさを一部手放すという面も人によってはあったと思います(3クール担当した私はありました)。

しかし、それと同時にサービスと監視・運用に対する理解や姿勢の醸成のための時間でもありました。それが各チームに浸透した結果、エンジニアの時間の使い方を再配分しながらサービスレベルを向上させることができたとも言えるので、とても実りある時間の使い方だったと思います。

「また朝当番を配置しなきゃね」という事態にならないようにこれからも気を引き締めたいと思います。

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

*1:もちろんECSタスク数は多ければ多いほど安定性は増しますが、コストとのトレードオフになるので、適切な時間帯に適切なタスク数が稼働していることを目指すという前提のもと調整を重ねてきました。

GraphQL スキーマから API ドキュメントを自動生成して GitHub Pages へデプロイする

Classi で提供している学習トレーニング機能を裏で支えているコンテンツ管理システム ( 以下、内部 CMS ) では、バックエンドに GraphQL を採用しています。 この GraphQL は Classi 内の様々なシステムで広く利用されています。

tech.classi.jp

内部 CMS の開発チームでは、この GraphQL スキーマの API ドキュメントを自動生成して GitHub Pages でホスティングしています
GitHub Pages は GitHub Actions ワークフローを作成するだけで簡単に静的サイトをデプロイすることができます。また、 Classi では GitHub Enterprise Cloud を利用しており GitHub Pages のアクセス範囲を制限することもできるため、社内向けドキュメントも安全にホスティングすることが可能です。

docs.github.com

GitHub Pages でホスティングすることで社内メンバーはいつでも簡単に API ドキュメントを参照できるため、チーム内外を問わず GraphQL スキーマに基づいた議論やコミュニケーションを円滑にする上で非常に役に立っています。

API ドキュメントの自動生成には Magidoc を使用しています。

この記事では Magidoc を利用して GraphQL スキーマから API ドキュメントを自動生成し、生成したドキュメントを GitHub Pages でホスティングする方法を紹介します。

Magidoc について

Magidoc は GraphQL スキーマから API ドキュメントを自動生成するためのツールです。API ドキュメントは HTML 形式で生成されます。

実際にどのようなドキュメントが生成されるのかは以下のサイトで確認できます。

Magidoc の使い方

1. インストール

Magidoc CLI は npm パッケージとして提供されているため、 npm コマンドでインストールできます。

# グローバルにインストールするコマンド例
$ npm install -g @magidoc/cli@latest

インストールが完了すると magidoc コマンドが実行できるようになります。

$ magidoc --help
Usage: Magidoc [options] [command]

Magidoc CLI helps you build beautiful and fully customizable GraphQL static documentation websites in seconds.

Options:
  -V, --version       output the version number
  -h, --help          display help for command

Commands:
  generate [options]  Generates a full static website using a template. Using this command gives you access to a limited
                      range of customization. If you wish to customize the website further than what is available, use the
                      eject command.
  preview [options]   Preview the documentation website generated with the generate `generate` command.
  dev [options]       Starts a development server with hot-reload as changes occur to watched files.
  eject [options]     Ejects from Magidoc basic template configuration, to allow for full customization of the template.
                      This will initialize a folder from a template of your choice, which can then be modified however you
                      wish.
  help [command]      display help for command

もちろん npx コマンドで実行することもできます。

$ npx @magidoc/cli@latest --help

2. 設定ファイルを作成する

Magidoc の設定は magidoc.mjs というファイルに記述します。

// magidoc.mjs
export default {
  introspection: {
    type: "sdl", // スキーマをファイルから読み込む場合は "sdl" を指定する
    paths: ["schemas/**/*.graphqls"], // スキーマファイルのパスを指定する
  },
  website: {
    // テンプレートを指定する
    // v6.1.0 時点でデフォルトで用意されてるのは "carbon-multi-page" のみ
    template: "carbon-multi-page",

    // Web サイトのルートパスを指定する
    // Public な GitHub Pages の URL は https://<オーナー名>.github.io/<リポジトリ名>/ のようになるので、
    // ここには "/<リポジトリ名>" を指定する必要がある
    siteRoot: "/example-repository",
  },
};

他にも様々な設定が用意されています。以下はその一例です。

  • URL から GraphQL スキーマを読み込む
  • 設定ファイルに直接 GraphQL スキーマを記述する
  • カスタムページを追加する
  • etc.

詳細については公式ドキュメントをご参照ください。

magidoc.js.org

3. API ドキュメントを生成する

magidoc generate コマンドで API ドキュメントを生成することができます。

$ magidoc generate
# npx を使用する場合
$ npx @magidoc/cli@latest generate

生成されたドキュメントは ./docs/ ディレクトリに出力されます。 ( 出力先は設定ファイルで変更可能 )

docs/
├─ index.html
├─ _app/
│  └─ ...
├─ introduction/
│  └─ ...
├─ mutations/
│  └─ ...
├─ queries/
│  └─ ...
└─ types/
   └─ ...

生成されたドキュメントをローカルで確認するには magidoc preview コマンドを実行します。サーバーが起動し、 http://localhost:4000 からドキュメントを閲覧することができます。

$ magidoc preview
# npx を使用する場合
$ npx @magidoc/cli@latest preview

localhost:4000

生成したドキュメントを GitHub Pages にデプロイする

GitHub Pages にデプロイするには以下の 2 通りの方法がありますが、今回は GitHub Actions からデプロイする方法を紹介します。

  • ブランチからデプロイする
  • GitHub Actions からデプロイする ← 今回紹介するのはこちら

それぞれの方法の詳細については公式ドキュメントをご参照ください。

docs.github.com

1. GitHub リポジトリの設定

GitHub リポジトリの Settings をクリックして設定画面に遷移します。

左サイドメニューから Pages をクリックして、 SourceGitHub Actions に設定します。

2. GitHub Actions ワークフローを作成する

GitHub Actions から GitHub Pages をデプロイするには以下の 2 つのアクションを利用します。

ワークフローは以下のようになります。この例では、 main ブランチに変更が push されるたびに GraphQL API ドキュメントを生成して GitHub Pages にデプロイします。

name: Publish API Docs

on:
  push:
    branches:
      - main

jobs:
  publish-api-docs:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      # actions/deploy-pages に必要な権限
      # 参考: https://github.com/actions/deploy-pages#security-considerations
      pages: write
      id-token: write
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    steps:
      # setup
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20

      # build
      - run: npx @magidoc/cli@latest generate

      # publish
      - uses: actions/upload-pages-artifact@v3
        with:
          path: ./docs
      - uses: actions/deploy-pages@v4
        id: deployment

GitHub Pages へのデプロイが完了すると https://<OWNER>.github.io/<REPOSITORY> から API ドキュメントを閲覧できるようになります。


冒頭で紹介した内部 CMS の GraphQL API ドキュメントのデプロイでは、実際には以下のようなワークフローを作成しています。内部 CMS の GraphQL スキーマのリリースには googleapis/release-please-action を利用しているので、 release_created output を参照することで「新しいバージョンがリリースされるたびに API ドキュメントを更新する」ということを実現しています。

tech.classi.jp

name: Release Please

on:
  push:
    branches:
      - main

jobs:
  release-please:
    runs-on: ubuntu-latest
    outputs:
      release_created: ${{ steps.release-please.outputs.release_created }}
    steps:
      # ...省略
      - uses: googleapis/release-please-action@v4
        id: release-please
        with:
          release-type: node

  publish-api-docs:
    needs:
      - release-please
    # 新しいリリースが作成されたときに API ドキュメントを更新する
    if: ${{ needs.release-please.outputs.release_created == 'true' }}
    # ...省略

まとめ

リッチな API ドキュメントがあるとテンションが上がります。
よりイケてるドキュメントにするために設計や説明文もしっかり書こうという気持ちになりますね。モチベーション大事。

sql.Openとsql.OpenDBの違い、そしてドライバーごとにsql.OpenDBを使うべきかの検討

id:aerealです。Goの話をします。

sql.Openとsql.OpenDBの違い

GoでRDBMSなどに繋ぐ際にはふつうdatabase/sqlを使います。 ORMを使う場合でも内部的にはこのパッケージに依存していることがほとんどです。

特定のデータベースに対して接続を確立したりクエリを実行する実装をドライバーと呼び、契約によって定められたインターフェースを実装したドライバーを利用者がdatabase/sqlに渡すことで、拡張性と独立性を実現しています。

sql.DBa database handle representing a pool of zero or more underlying connections.と説明されており、いわゆるアプリケーションレベルのコネクションプールを表現した構造体といってよいでしょう。 Perl monger的にはsql.DBはDBIx::Handlerに相当するものというとわかりやすいでしょう。

sql.DBを得る方法は2つあり、ひとつはsql.Open, もうひとつはsql.OpenDBです。 sql.OpenDBはGo 1.10で追加されたAPIでより新しいです。

新しいAPIが導入された動機は以下の通りです。

Drivers that want to construct a sql.DB for their clients can now implement the Connector interface and call the new sql.OpenDB function, instead of needing to encode all configuration into a string passed to sql.Open.

Drivers that want to parse the configuration string only once per sql.DB instead of once per sql.Conn, or that want access to each sql.Conn’s underlying context, can make their Driver implementations also implement DriverContext’s new OpenConnector method.

sql.Openはdata source name (以下、単にDSN) と呼ばれるDBへ接続するための情報を文字列として受け取ります。

DBへ接続する際は、ふつうドライバーが文字列から構造体へ変換してたとえば接続先のホストやポートなどを抽出して使用します。

この変換処理は新しい接続を確立する度に行われますが非効率です。

加えて単純な文字列であるDSNを用いる場合、関数やポインタなどリッチな言語表現が使えません。

たとえばイベントに応じて呼ばれるコールバック関数を受け取りたいという場合、標準のdatabase/sqlのAPIだけでは相互運用できず不便です。

こうした主に2つの問題を解決するために新しいsql.OpenDBというインターフェースが導入されました。

ドライバーごとの接続情報の事情

ここでは主にgo-sql-driver/mysqljackc/pgxの話をします。

まずこれらドライバーの接続設定を表す構造体は以下の通りです:

mysqlのほうは比較的単純ですがpgxはOnNoticeやOnPgErrorなど関数として持つフィールドが散見されます。

関数や構造体 (のポインタ) をとるフィールドを見ているとDSNをパースして得られる値と同等の値をアプリケーション内で組み立てられるのかが気になってきます。 つまりデフォルト値が公開されているのか、それともコピペしなければいけないのかということです。

pgxのDSNをパースする処理を見ると、BuildFrontendやBuildContextWatcherHandlerなどの関数のデフォルト実装そのものは公開されていませんが、公開メンバのみを使っているのでコピペすることは可能そうです。

気になるのがTLS接続の設定を決めるconfigTLSという実装です。

これはパラメータに加えてファイルシステムに配置された証明書の状況に応じてTLS接続に用いるオプションを決めるもので、最終的にtls.Configを返すのですが、かなり込み入っていることが一目でわかります。

この複雑なロジックはlibpqのsslmodeなどのパラメータ定義に倣ったもので、利用者にとっても明確な定義が外部にあることは嬉しいことが多いのですが、このconfigTLS関数は現時点で公開されておらずコピペするほかありません。

アプリケーションにおいてTLS接続設定の決定は、これほど柔軟である必要はないでしょう。せいぜい商用環境でTLS接続を厳格な設定で用い、ローカルでは無効にする程度の設定で十分です。

しかし先に述べた関数を値にとるフィールドが少なからずあることなどを踏まえると、ライブラリのデフォルト値にせいぜいホスト名やポート番号程度を加工した程度のpgconn.Configを得るためにpgconn.ParseConfigを用いずスクラッチから組み立てる実装を作るのはかなり不合理に思えます。

まとめ

database/sqlの簡単な歴史について触れ、MySQLとPostgreSQLのドライバー実装を読み解き、「ライブラリが提供するパース関数を使うか、それとも構造体をスクラッチから作るか」の実用性について検討しました。

変換処理が明らかにボトルネックでない場合、つまり文字列ではなく構造体として設定を持ち回ることで見通しをよくすることだけが目的であれば、go-sql-driver/mysqlはsql.OpenDBに移行してよい・jackc/pgxは割に合わないので従来通りsql.Openでも構わないという評価に落ち着きました。

MySQLとPostgreSQL両方に繋ぐアプリケーションで統一感を図ろうと考えたことがきっかけですが、結果的にこれまできちんと読んでこなかったドライバーの実装を読み進められました。

go-sql-driver/mysqlはフラットなパッケージ構成で極めてシンプルな実装でそれが設定構造体にも表れており、対してjackc/pgxはもともとlib/pqに欠けていた機能を補完するという目的があったためか現時点でも十分にリッチでかつ拡張性も見据えた実装になっています。

最後に、どちらも活発に開発されているライブラリなので記事の記述と実装に乖離がある場合、最新の実装が常に正しい点をご留意ください。

© 2020 Classi Corp.