Docusaurusを利用したProtocol Buffersのドキュメント化

サムネイル

こんにちは。オンラインサロン開発部 開発グループ アーキテクトチームの神宮です。

DMMオンラインサロンでは現行のシステムにおける課題を解決し、ビジネス的な課題を抽出しつつリプレース・リアーキテクトを進めるプロジェクト「neon」を中期的に取り組んでいます。

「neon」では、マイクロサービス・モジュラモノリス間の通信プロトコルにはgRPC、シリアライズフォーマットにはProtocol Buffersを採用しています。「neon」で採用している技術スタックやアーキテクチャに関して詳しくは、以前の記事をご覧ください。

今回は、このProtocol BuffersによるAPI定義のドキュメント化に関して、アーキテクトチームが取り組んでいることについてお話したいと思います。

経緯と背景

Protocol Buffersのドキュメント生成にはいくつかの方法がありますが、最も有名な方法はBuf Schema Registry(以後BSRと記載)を利用する方法だと思います。BSRについての詳細な説明はここでは行いませんが、BSRはProto定義をモジュール化して管理すること以外にも、ドキュメントとしての側面を持っています。

「neon」でも、当初はBSRを採用していました。しかし、2023年に行われた価格改定を受けて、それ以降の利用を断念することにしたという経緯があります。

新しい料金体系では、月額料金が管理するType数に応じて増加するものになり、当時管理していたAPI定義で月額5万円ほどになる見込みでした。現状ドキュメントとしての利用が主な理由であり、利用ケースに見合ったコストではないと判断したことが、継続利用を断念した主な理由となっていました。

また、Type数に応じた料金体系となったことがAPIの設計に与える影響への懸念も、利用を断念した理由の一つでした。本来であれば利用範囲や責務に応じてメッセージやメソッドを分割するべきケースであっても、コストを意識してまとめてしまう、といったことが起こるかもしれません。そうでなくとも、コストを意識して設計方針にバイアスがかかる事態は、可能であれば避けたいところです。

このような理由から、今回我々はProto定義のドキュメントをDocusaurus + docusaurus-protobuffetを用いて生成し、自前でホスティングするという手段をとることにしました。

docusaurus-protobuffetとは

docusaurusとは

Docusaurusは、Meta社(旧Facebook社)によって公開されているオープンソースのドキュメント(を始めとする静的サイト)生成ツールで、様々な技術やツールのドキュメントに利用されています。また、様々なプラグインが公開されており、ドキュメントに検索機能を追加したり、GitHubリポジトリからコードを参照してきれいに表示したりと、ドキュメントをより便利にするための枠組みがあるのも特徴です。

Docusaurusの詳細な説明は他に譲りますが、DocusaurusのランディングページもDocusaurusを用いて生成されているので、どのようなものが作れるか知りたい方はぜひ触ってみてください。

docusaurus-protobuffetとは

docusaurus-protobuffetはProto定義のドキュメント作成のための、コミュニティによるDocusaurusツールセットです。Protoco定義のドキュメントをDocusaurus向けに生成したり、一からProto定義のドキュメントをDocusaurusを用いて生成するツール群になっています。

既にDocusaurusドキュメントを運用しており、そこにProto定義のドキュメントを追加するケースでも、新規にProto定義のドキュメントをDocusaurusによって作成するケースでも対応可能です。

docusaurus-protobuffetのランディングページも同様に、このdocusaurus-protobuffetを用いて生成されているため、どのようなドキュメントが生成されるのか見たい方はそちらを参照してください。

なぜ docusaurus-protobuffet を使うのか

ここでは、docusaurus-protobuffetを採用した理由と、他に検討した方法についていくつか紹介します。

docusaurus-protobuffetを採用した理由の1つは、既存のドキュメント生成ツールであるDocusaurusの恩恵を受けられることです。

Docusaurusは前述の通り、Meta社がオープンソースで公開しているものです。ナビゲーションをはじめとしたUIの完成度の高さや、拡張機能による拡張性の高さなどの恩恵を受けることができます。加えて、インターネット上に存在する情報の多さや、将来的にメンテナンスが継続される可能性が高いという側面もあります。

他にも、多くのサービスやツールのドキュメントとして利用されているため、比較的慣れ親しんだ外観であることや、自由にページを追加して、Proto定義だけでなく付随する情報も含めやすい点などがありました。

他に検討したもの

ー sabledocs

sabledocsは protoc コマンドでコンパイルした情報を用いてドキュメントを生成するツールで、見やすく整理されたドキュメントと検索機能があります。

Docusaurusの恩恵があるdocusaurus-protobuffetに対して、こちらは拡張性が少ない点を考慮する必要があります。

sabledocsで生成されるドキュメントのサンプルは以下で確認できます。https://markvincze.github.io/sabledocs/demo/index.html

ー protoc-gen-doc

protoc-gen-docは、 protoc のプラグインで、Proto定義から簡易なドキュメントを生成できるものです。

生成されるドキュメントは以下のようなものになっています。
https://rawgit.com/pseudomuto/protoc-gen-doc/master/examples/doc/example.html

シンプルで直感的なドキュメントが生成できる一方で、規模が大きくなってくると見づらくなってきたり、検索機能が欲しくなりそうです。

docusaurus-protobuffetを用いたドキュメント生成

それではさっそくdocusaurus-protobuffetを用いたProtoI定義のドキュメント作成方法について説明していきます。

Docusaurusのセットアップ

まずは、Proto定義ドキュメントを乗せる土台となるDocusaurusドキュメントを生成していきます。セットアップ方法はDocusaurusの公式ドキュメントに記載の通りです。詳しいカスタマイズ方法も公式ドキュメントに記載がありますが、今回はこちらを実行して、ベーシックな雛形を作成します。

 npx create-docusaurus@latest protobuf-document classic --typescript

生成された雛形に含まれる package.json にはいくつかのスクリプトが含まれており、npm start を実行することで手元でドキュメントを確認することができます。

protobuffetによるドキュメントページの作成

次に、protobuffetを用いて先程作成したドキュメントにProto定義のページを追加していきます。こちらも公式に記載のある導入手順に従って進めていきます。

protobuffetは protoc-gen-doc の生成する結果を利用するので、まずはこちらの導入を行う必要があります。Golangの環境が既にある場合はそちらから導入することもできますし、依存するツールセットを含んだDockerイメージも公開されているので、お好みの方法で導入してください。

protoc-gen-docが導入できたら、次のコマンドによって各々のProto定義をコンパイルし、Proto定義のドキュメント情報を含むJSONファイルを生成します。

 protoc --doc_out=./fixtures --doc_opt=json,proto_workspace.json protos/**/*.proto

ここからは、protobuffetの設定に入っていきます。

まずは、生成したDocusaurusドキュメントのパッケージに、protobuffetを導入します。

 npm install --save docusaurus-protobuffet

次に、生成したDocusaurusドキュメントディレクトリ配下にある、 docusaurus.config.js に設定を記述していきます。 preset プロパティの値である配列に以下の設定を追加します。 fileDescriptorsPath の値は protoc-gen-doc で生成したJSONファイルを指すようにします。

[

  'docusaurus-protobuffet',

  {

    protobuffet: {

      fileDescriptorsPath: '../salon-protobuf.json' // 先程生成したJSONのパス

    }

  }

],

// ... classicなど他のプリセット設定情報

これで、ドキュメント生成に必要な準備は整いました。

最後に、次のコマンドを実行して、Proto定義の情報を含むJSONファイルから、Docusaurusのドキュメントファイルを生成します。

 npx docusaurus generate-proto-docs

もしここでエラーが発生した場合は、Docusaurusドキュメントディレクトリ直下に、Proto定義の情報の出力先である protodocs ディレクトリと、 sidebarsProtodocs.js を作成してみてください。

これでProto定義のドキュメントページを作成することができましたが、このままでは既存のDocusaurusドキュメントページから一切リンクされていないので、トップページから到達できません。トップページやヘッダー・フッダーなどからアクセスできるようにする必要があります。公式ドキュメントにはヘッダーに含む例が記載されていました。

これでドキュメントの完成です。 npm start を実行するなどすることで、生成したドキュメントを手元で確認できます。 また、ドキュメントをホスティングするために静的なリソースを生成するには、 npm run build を実行します。デフォルトでは ドキュメント配下の build ディレクトリに出力されます。

CIでドキュメントを生成する

ここまで、基本的な protobuffet によるドキュメント生成の方法について説明してきました。

実際にドキュメントを運用していく上では、Proto定義に変更があるたびに自動でドキュメントも追従するようにするのが好ましいです。今回は、Githubリポジトリ上で、mainブランチにAPI定義の変更が取り込まれるたびにドキュメントを更新するようにしたいと思います。

先程説明した protobuffet の使用方法のうち、protoc-gen-doc の実行から npm run build の実行までは、定義に変更があるたびに実行する必要があります。

このことを踏まえたGithub Actionsのワークフロー例は、以下のようになります。この例では、リポジトリ直下にドキュメントを含む docs ディレクトリ、protoファイルを含む protos ディレクトリがあることを想定し、ホスティング先としてGithub Pagesを使用しています。

name: Publish Proto Documentation

on:

  push:

    branches:

      - 'main'

    paths:

      - 'protos/**/**.proto'

      - 'docs/**'



jobs:

  Build-Documentation:

    runs-on: ubuntu-latest

    steps:

      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4

        with:

            node-version: '20.x'

            cache: 'npm'

            cache-dependency-path: ./docs/package-lock.json

      - run: npm ci

        working-directory: ./docs

      - name: protoc-gen-doc

        run: bash scripts/protoc-gen-doc.sh # protoc-gen-doc を実行してJSONファイルを生成

      - name: Generate Docusaurus Proto Docs

        run: npx docusaurus generate-proto-docs

        working-directory: ./docs

      - name: Build Documentation

        run: npm run build

        working-directory: ./docs

      - name: Archive Documentation

        uses: actions/upload-artifact@v3

        with:

          name: docusaurus-documentation

          path: ./docs/build

Publish-Documentation:

    needs: Build-Documentation

    permissions:

        contents: write

    runs-on: ubuntu-latest

    steps:

      - name: Download Built Documentation

        uses: actions/download-artifact@v3

        with:

          name: docusaurus-documentation

          path: built-document

      - name: Publish-Docs

        uses: peaceiris/actions-gh-pages@v3

        with:

            github_token: ${{ secrets.GITHUB_TOKEN }}

            publish_dir: ./built-document

結果と課題

このようにして、Proto定義の変更がmainブランチに取り込まれるたびに、ドキュメントを更新し、最新のドキュメントをGithub Pagesで公開することができました。

このままでも十分に見やすいですが、規模が大きくなり、探している定義に遷移するのに苦労するのであれば、Docusaurusのプラグインを活用して、検索機能を取り入れることでより便利にもなりそうです。

一方で、ここまでで作成したドキュメントには問題もあります。最も大きな問題は、Proto定義に含まれるユーザー定義のカスタムオプションがドキュメントに含まれないことです。これはprotobuffetの制限というよりも、protoc-gen-doc由来の問題であると思われ、こちらのIssueなどでも議論されていました。

カスタムオプションは、認証、トレース、デバッグ情報の追加など様々な用途で使われるもので、「neon」でもこれらの用途で実際に使用しています。定義の基本的な部分を確認するだけであれば、今回作成したドキュメントは便利ではありますが、カスタムオプションを含む詳細を確認するためには直接コードを見なければならないため、まだまだ改善の余地がありそうです。

おわりに

ここまで、docusaurus-protobuffetを用いてProto定義のドキュメントを自前で作成する方法について説明してきました。

当初のBuf Schema Registryを利用したソリューションよりもコストを削減はできたものの、カスタムオプションの情報を含めることができない課題もある結果となりました。Proto定義のドキュメント運用を検討している方の参考になれば幸いです。

DMMオンラインサロンでは開発グループのメンバーを募集しています。Protocol BuffersやgRPCを用いたバックエンドシステムの開発に興味がある方や、「neon」プロジェクトに関心をお持ちいただいた方は、是非気軽にご連絡ください。