bufを使ってProtocol Buffersの環境を整える

Tech KAYAC Advent Calendar 2023 の18日目の記事です。

カヤックボンドでエンジニアをやっております中野です。
ここ最近golang、gRPCに触れており、Protocol Buffersの環境を整備するにあたって便利だったものを紹介します。

はじめに

Protocol Buffersは、Googleが開発したオープンソースのデータシリアライゼーションフォーマットで、 幅広いサービスで使用されております。protoファイルの管理、使用の設定などが複雑になりがちです。  

「buf」は、このプロセスを簡素化し、効率的なワークフローを提供するツールセットです。

protobuf.dev

bufとは

bufは、Protocol Buffersのファイル管理、依存関係解決、APIの互換性チェックなど容易にするツールです。 これにより、プロジェクトのメンテナンス性が向上します。 また、protoファイルのLinterやFormatterも提供されており、非常に便利です。

github.com

導入方法

Bufのインストール

インストールは公式サイトの手順で導入します。

macOSの場合は下記のコマンドでインストールできます。

brew install bufbuild/buf/buf
buf --version
1.28.1

プロジェクトの作成

下記のようなフォルダ構成を用意していきます

bufbuild-sample % tree    
.
├── buf.gen.yaml
├── buf.work.yaml
├── proto
│   ├── buf.lock
│   ├── buf.yaml
│   └── service
│       └── hello.proto

1. モジュール初期化

cd proto
buf mod init
buf mod update

※buf.yamlファイルが生成される。

proto/buf.yaml

version: v1
breaking:
  use:
    - FILE
lint:
  use:
    - DEFAULT

buf.work.yamlファイルの作成

touch buf.work.yaml

buf.work.yaml

# buf.work.yaml
version: v1
directories:
  - proto

2. lint設定の変更

デフォルトのlintのルール設定は、すこし厳しいのでルールをカスタマイズするのをお勧めします。

proto/buf.yaml

version: v1
breaking: #破壊的変更の検知
  use:
    - FILE
lint:     #lintの設定
  use:
    - DEFAULT # デフォルトルール
  except: #lintの除外ルール設定
      - PACKAGE_VERSION_SUFFIX
      - PACKAGE_DIRECTORY_MATCH

hello.protoファイルの用意

syntax = "proto3";

package service;

option go_package = "bufbuild-sample/pb/service";

// The greeting service definition.
service GreeterService {
  // Sends a greeting
  rpc SayHello(SayHelloRequest) returns (SayHelloResponse) {}
}

message SayHelloRequest {
  string name = 1;
}

message SayHelloResponse {
  string name = 1;
}
  • Linter実行
buf lint
  • Formatter実行
buf format -w

3. 設定ファイルの作成&コード自動生成

コードの自動生成の設定ファイルを作成します。

touch proto/buf.gen.yaml

設定を編集して、go用の設定を追加します。

# buf.gen.yaml

version: v1
plugins:
  - plugin: go ## plugin名
    out: ./pb ## 生成されるフォルダ
    opt: ## pluginへのオプション
      - paths=source_relative
  - plugin: go-grpc
    out: ./pb
    opt:
      - paths=source_relative

pluginが依存しているパッケージをインスールします。

go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

コードを自動生成します。

buf generate

pbフォルダにコードが生成されます

動作確認

動作確認のため下記のような構成になるようにファイルを作成します。

mkdir -p cmd/server service
touch cmd/server/main.go service/hello_service.go
 bufbuild-sample % tree
.
├── cmd
│   └── server
│       └── main.go
├── go.mod
├── go.sum
└── service
    └── hello_service.go

cmd/server/main.go

package main

import (
    pb "bufbuild-sample/pb/service"
    "bufbuild-sample/service"
    "fmt"
    "log"
    "net"

    "google.golang.org/grpc"
    "google.golang.org/grpc/reflection"
)

func main() {
    if err := run(); err != nil {
        log.Fatalf("failed to run: %v", err)
    }
}

func run() error {
    // 起動するポート番号を指定
    port := 8080
    listenPort, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
    if err != nil {
        return err
    }

    // gRPCサーバーの生成
    server := grpc.NewServer()

    // サービスをハンドラーに登録
    pb.RegisterGreeterServiceServer(server, service.NewGreeterServiceServer())

    reflection.Register(server)
    // サーバーを起動
    return server.Serve(listenPort)
}

service/hello_service.go

package service

import (
    "bufbuild-sample/pb/service"
    "context"
)

func NewGreeterServiceServer() *greeterServiceServer {
    return &greeterServiceServer{}
}

type greeterServiceServer struct {
    service.UnsafeGreeterServiceServer
}

func (svc *greeterServiceServer) SayHello(ctx context.Context, r *service.SayHelloRequest) (*service.SayHelloResponse, error) {
    name := r.GetName()

    return &service.SayHelloResponse{Name: "Hello again " + name}, nil
}

サーバーを起動する

go run cmd/server/main.go

postmanなどを使用して、リクエストを投げるとレスポンスを確認できます。

おまけ

その他の機能として、Github Actionを使用したCIも提供されています。 PRに対してLinterの実行などが可能です。 buf.build

また、protoファイルにバリデーションルールを記載することも可能です。   ※現在betaリリースとなっております github.com

おわりに

bufの導入により、APIの設計、開発、メンテナンスが容易になります。 Protocol Buffersを検討する際に、導入の候補になってくるようなツールかと思います。 皆様も導入の候補としてご検討ください。

最後まで読んでくれてありがとうございました!

カヤックボンドでは一緒に技術力を高め合えるエンジニアを募集しています!kayac.bond