🎃

Buf(v2)を用いて複数の.protoファイルを1つの.pbにまとめる

2024/09/25に公開

Bufとは

Protobufのビルドを容易にするツール。
protoファイルをprotocでビルドしようとするとオプションまみれになってややこしいため、オプションなどの設定をyamlファイルとして整理できる

公式: https://buf.build/docs/introduction

やりたいこと

異なる複数のディレクトリにある.protoファイルをビルドして単一の.pbファイルにしたい。
その上で、EnvoyのgRPC JSON TranscoderをもちいてREST APIとして公開したい。

背景

以前、Envoyを使ってgRPCサーバをREST APIから呼び出せるようにした。
https://zenn.dev/regmarmcem/articles/a824c8ddd94cfd

このとき、http_filtersを以下のように設定した。

  http_filters:
  - name: envoy.filters.http.grpc_json_transcoder
    typed_config:
      "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_json_transcoder.v3.GrpcJsonTranscoder
      proto_descriptor: /etc/envoy/protos/articles.api.pb
      services:
      - articlespb.ArticlesService
    (省略)

このとき、articles.api.pbという単一のpbファイルを用意しないといけない。
※以下にある通り、http_filtersを複数用意して対応することはできない。
https://github.com/envoyproxy/envoy/issues/14602#issuecomment-1120494809

前提

それぞれの.protoファイルは以下のパスに置かれている。
このとき、個々のファイルの名前はユニークである必要があることに注意

├── articles
│   ├── buf.yaml
│   ├── articlespb
│   │   └── articles.api.proto
├── orders
│   ├── buf.yaml
│   └── orderspb
│       └── orders.api.proto
└── users
│   ├── buf.yaml
    └── userspb
        └── users.api.proto

手順

1. buf.yamlの作成

プロジェクトのルートディレクトリにbuf.yamlを配置する。

version: v2
lint:
  except:
    - FIELD_NOT_REQUIRED
    - PACKAGE_DIRECTORY_MATCH
    - PACKAGE_NO_IMPORT_CYCLE
    - PACKAGE_VERSION_SUFFIX
  enum_zero_value_suffix: _UNKNOWN
  disallow_comment_ignores: true
breaking:
  use:
    - FILE
  except:
    - EXTENSION_NO_DELETE
    - FIELD_SAME_DEFAULT
deps:
  - buf.build/googleapis/googleapis
modules:
  - path: articles
  - path: users
  - path: orders

2. モジュールの依存関係のアップデート

buf mod update

3. 単一の.pbファイルの作成

buf build . -o combined.api.pb

4. envoyの設定ファイルの修正

envoy.yaml
        port_value: 51051
    filter_chains:
    - filters:
      - name: envoy.filters.network.http_connection_manager
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
          stat_prefix: grpc_json
          codec_type: AUTO
          route_config:
            name: local_route
            virtual_hosts:
            - name: local_service
              domains: ["*"]
              routes:
*              - match: { prefix: / }
*                route: { cluster: grpc, timeout: 60s }
          http_filters:
          - name: envoy.filters.http.grpc_json_transcoder
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_json_transcoder.v3.GrpcJsonTranscoder
              proto_descriptor: /etc/envoy/protos/combined.api.pb
              services:
+              - articlespb.ArticlesService
+              - userspb.UsersService
+              - orderspb.OrdersService
              print_options:
                preserve_proto_field_names: false

結果

無事に、localhost:51051/articles, localhost:51051/users, localhost:51051/ordersにアクセスできた。

Discussion