gRPC 사용

이 페이지에서는 gRPC를 사용하여 Cloud Run 서비스를 다른 서비스(예: 내부 마이크로서비스 간의 간단하면서 고성능의 통신 제공)와 연결하려는 개발자를 위한 Cloud Run 관련 세부정보를 보여줍니다. Cloud Run으로 모든 gRPC 유형, 스트리밍 또는 단항을 사용할 수 있습니다.

사용 가능한 사용 사례는 다음과 같습니다.

  • 내부 마이크로서비스 간의 통신
  • 대량의 데이터. 이 경우 gRPC는 REST 호출보다 최대 7배 빠른 프로토콜 버퍼를 사용합니다.
  • 간단한 서비스 정의만 필요한 경우에는 전체 클라이언트 라이브러리를 작성하지 않아도 됩니다.
  • gRPC 서버에서 스트리밍 gRPC를 사용하여 응답형 애플리케이션 및 API를 빌드합니다.

서비스를 gRPC와 통합하려면 다음 안내를 따르세요.

  • 스트리밍 gRPC를 사용하는 경우 HTTP/2를 사용하도록 서비스를 구성합니다. HTTP/2는 gRPC 스트리밍의 전송 방법입니다.
  • proto 파일에서 요청 메시지와 응답을 정의하고 컴파일합니다.
  • 요청을 처리하고 응답을 반환하는 gRPC 서버를 만듭니다. 이 서버는 PORT 환경 변수를 리슨해야 합니다.
  • gRPC 서버에서 요청을 보내고 응답을 처리하는 클라이언트를 만듭니다.
  • 원할 경우 인증을 추가합니다.
  • 서비스를 빌드하고 배포합니다.

HTTP/2를 사용하도록 서비스 구성

Cloud Run에 gRPC를 사용하는 경우 HTTP/2를 사용하도록 서비스를 구성하는 것이 좋습니다. 일부 간단한 gRPC 기능은 HTTP/2를 사용하지 않아도 작동하지만 스트리밍 및 메타데이터와 같은 여러 gRPC 기능에 HTTP/2가 필요합니다.

proto 파일에서 메시지 정의 및 컴파일

proto 정의에 추가할 추가 또는 Cloud Run 관련 항목이 없습니다. 다른 용도로 gRPC를 사용할 때와 마찬가지로 서비스 정의 및 데이터 직렬화에는 gRPC 프로토콜 버퍼를 사용합니다.

gRPC 클라이언트 만들기

gRPC를 사용하는 클라이언트에 추가할 추가 또는 Cloud Run 관련 항목이 없습니다. 클라이언트 코드의 서비스 정의 사용에 관한 gRPC 문서나 언어별 gRPC 튜토리얼에 제공된 샘플 클라이언트를 따르세요.

자동 확장 및 부하 분산

Cloud Run은 클라이언트와 Cloud Run 인스턴스 간에 별도의 연결을 유지하는 Google 관리형 부하 분산기를 사용합니다. gRPC를 사용하면 자동 확장이 다음과 같이 작동합니다.

  • 클라이언트의 gRPC 연결은 에지 부하 분산기에서 종료됩니다. KeepAlive 설정을 조정하면 Cloud Run 인스턴스가 아닌 부하 분산기에 대한 연결에만 영향을 줍니다. 클라이언트는 인스턴스가 삭제될 때 이를 인식하지 못합니다.
  • 수평 축소 중에 부하 분산기는 백엔드 인스턴스가 종료될 때 GOAWAY 메시지를 전송하여 연결을 종료합니다.
  • 수평 확장 중에 부하 분산기는 백엔드 인스턴스에 대해 새 연결을 만듭니다. 이러한 모든 작업은 클라이언트에 투명합니다.
  • 자동 확장 중에 많은 인스턴스가 시작될 수 있으며 클라이언트와 프록시 부하 분산기 간의 단일 연결로 다중화될 수 있습니다.
  • 동시 실행은 메시지의 인스턴스당 최대 동시 요청 수에 따라 결정됩니다. 스트리밍에서 각 스트림은 최대 동시 요청에 대해 한 번만 계산됩니다.

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 또는 해당 서비스에 매핑된 커스텀 도메인인 호스트 도메인을 gRPC에서 사용할 것으로 예상되는 포트 443과 함께 지정해야 합니다.

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 {
		// Note: On the Windows platform, use of x509.SystemCertPool() requires
		// go version 1.18 or higher.
		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: %w", err)
	}
	token, err := tokenSource.Token()
	if err != nil {
		return nil, fmt.Errorf("TokenSource.Token: %w", 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 구현을 참조하세요.