gRPC の使用

このページでは、デベロッパーが gRPC を使用し、Cloud Run サービスを他のサービスと接続して、たとえば、内部のマイクロサービス間のシンプルでパフォーマンスの高い通信を行う方法について詳しく説明します。Cloud Run では、すべての gRPC タイプ(ストリーミングまたは単項)を使用できます。

考えられるユースケースには、次のようなものがあります。

  • 内部マイクロサービス間の通信。
  • 高負荷のデータ(gRPC では、プロトコル バッファを使用します。これは REST 呼び出しの最大 7 倍の速度です)。
  • 単純なサービス定義だけが必要で、完全なクライアント ライブラリは作成しないケース。
  • gRPC サーバーで gRPC ストリーミングを使用して、より応答性の高いアプリケーションと API を構築する。

サービスを gRPC と統合するには:

  • ストリーミング gRPC を使用している場合に HTTP/2 を使用するようにサービスを構成します。HTTP/2 は、gRPC ストリーミングの転送メソッドです。
  • リクエスト メッセージとレスポンスを proto ファイルに定義し、コンパイルします。
  • リクエストを処理してレスポンスを返す gRPC サーバーを作成します。このサーバーは PORT 環境変数をリッスンする必要があります。
  • リクエストを送信し、gRPC サーバーからのレスポンスを処理するクライアントを作成します。
  • 必要に応じて、認証を追加します。
  • サービスをビルドしてデプロイします。

HTTP/2 を使用するようにサービスを構成する

ストリーミング gRPC を使用している場合は、HTTP/2 を使用するようにサービスを構成します。

proto ファイルでメッセージを定義してコンパイルする

proto 定義に追加するその他のものや Cloud Run 固有のものはありません。gRPC の他の場合での使用と同様に、サービス定義とデータのシリアル化には gRPC プロトコル バッファを使用します。

gRPC クライアントを作成する

gRPC を使用するクライアントに追加するその他のものや Cloud Run 固有のものはありません。クライアント コードのサービス定義と、言語別の gRPC チュートリアルで提示されるサンプル クライアントの使用については、gRPC ドキュメントを参考にしてください。

Cloud Run サービスで gRPC リクエストをリッスンする

Cloud Run で実行される gRPC サーバーの唯一の特別な要件は、コードに示されているように PORT 環境変数で指定されたポートをリッスンすることです。

Go

func main() {
	log.Printf("grpc-ping: starting server...")

	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
		log.Printf("Defaulting to port %s", port)
	}

	listener, err := net.Listen("tcp", ":"+port)
	if err != nil {
		log.Fatalf("net.Listen: %v", err)
	}

	grpcServer := grpc.NewServer()
	pb.RegisterPingServiceServer(grpcServer, &pingService{})
	if err = grpcServer.Serve(listener); err != nil {
		log.Fatal(err)
	}
}

サービスとの gRPC 接続を開く

gRPC メッセージを送信できるようにサービスへの gRPC 接続を開くには、ホストドメイン(Cloud Run サービスの URL、またはサービスにマッピングされたカスタム ドメイン)を、ポート 443(gRPC で使用されると予想されるポート)とともに指定する必要があります。

Go


import (
	"crypto/tls"
	"crypto/x509"

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

// NewConn creates a new gRPC connection.
// host should be of the form domain:port, e.g., example.com:443
func NewConn(host string, insecure bool) (*grpc.ClientConn, error) {
	var opts []grpc.DialOption
	if host != "" {
		opts = append(opts, grpc.WithAuthority(host))
	}

	if insecure {
		opts = append(opts, grpc.WithInsecure())
	} else {
		systemRoots, err := x509.SystemCertPool()
		if err != nil {
			return nil, err
		}
		cred := credentials.NewTLS(&tls.Config{
			RootCAs: systemRoots,
		})
		opts = append(opts, grpc.WithTransportCredentials(cred))
	}

	return grpc.Dial(host, opts...)
}

認証なしで gRPC リクエストを送信する

次のサンプルは、前述のように構成された gRPC 接続を使用して、認証なしでリクエストを送信する方法を示しています。

Go


import (
	"context"
	"time"

	pb "github.com/GoogleCloudPlatform/golang-samples/run/grpc-ping/pkg/api/v1"
	"google.golang.org/grpc"
)

// pingRequest sends a new gRPC ping request to the server configured in the connection.
func pingRequest(conn *grpc.ClientConn, p *pb.Request) (*pb.Response, error) {
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	client := pb.NewPingServiceClient(conn)
	return client.Send(ctx, p)
}

認証付き gRPC リクエストの送信

次のサンプルは、呼び出し側のサービスが受信側のサービスに対する呼び出し元権限を持っている場合に、サービス間の認証を使用する方法を示しています。このコードは、適切な ID トークンを持つ承認ヘッダーを作成します(必須)。必要な権限と承認ヘッダーの詳細については、サービス間認証をご覧ください。

Go


import (
	"context"
	"fmt"
	"time"

	"google.golang.org/api/idtoken"
	"google.golang.org/grpc"
	grpcMetadata "google.golang.org/grpc/metadata"

	pb "github.com/GoogleCloudPlatform/golang-samples/run/grpc-ping/pkg/api/v1"
)

// pingRequestWithAuth mints a new Identity Token for each request.
// This token has a 1 hour expiry and should be reused.
// audience must be the auto-assigned URL of a Cloud Run service or HTTP Cloud Function without port number.
func pingRequestWithAuth(conn *grpc.ClientConn, p *pb.Request, audience string) (*pb.Response, error) {
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	// Create an identity token.
	// With a global TokenSource tokens would be reused and auto-refreshed at need.
	// A given TokenSource is specific to the audience.
	tokenSource, err := idtoken.NewTokenSource(ctx, audience)
	if err != nil {
		return nil, fmt.Errorf("idtoken.NewTokenSource: %v", err)
	}
	token, err := tokenSource.Token()
	if err != nil {
		return nil, fmt.Errorf("TokenSource.Token: %v", err)
	}

	// Add token to gRPC Request.
	ctx = grpcMetadata.AppendToOutgoingContext(ctx, "authorization", "Bearer "+token.AccessToken)

	// Send the request.
	client := pb.NewPingServiceClient(conn)
	return client.Send(ctx, p)
}

gRPC ストリーミングのサンプルコード

サンプルコードについては、お使いの言語の gRPC 基本チュートリアルの RouteGuide の実装をご覧ください。たとえば、Go をお使いの場合は、RouteGuide の実装をご覧ください。