LoginSignup
8
8

More than 5 years have passed since last update.

gRPC の Stackdriver Trace を Go言語 で使ってみた

Posted at

動機

話題のO11y(詳しくわかってない)を勉強するために最近使っているgRPCをStackdriver Trace を使ってトレーシングしてみます。

まだ理解しきれていない部分が多々あるのでご指摘などありましたらぜひお願いします!

ちなみに分散トレーシングについてはわかりやすく説明してくださっているスライドが公開されています。

shared 20190115 Advanced Distributed Tracing with Stackdriver Trace - Google スライド

調べる

Stackdriver Trace のGo言語用設定を行う

Go 用の Stackdriver Trace の設定 | Stackdriver Trace | Google Cloud

OpenCensus Quickstart

OpenCensus の stackdriver 用の exporter を設定し、Tracing自体はOpencensusuに従うみたい

以下のような感じで Exporterを設定するみたい

import (
        "log"
        "os"

        "contrib.go.opencensus.io/exporter/stackdriver"
        "go.opencensus.io/trace"
)

func main() {
        // Create and register a OpenCensus Stackdriver Trace exporter.
        exporter, err := stackdriver.NewExporter(stackdriver.Options{
                ProjectID: os.Getenv("GOOGLE_CLOUD_PROJECT"),
        })
        if err != nil {
                log.Fatal(err)
        }
        trace.RegisterExporter(exporter)
}

OpenCensus Guides gRPC Go

上記の設定をしてこのリンクに沿ってコードを変更すればトレースができる。

やってく

ローカルでサーバーを立ててアクセスしてみたが、ログが正常に出なかったのでGCEでサーバーを立てて検証してみる。
protoc とか gRPC についてや GCE の Go 環境についてはわかりやすい記事が沢山あるので適宜参照してください!

(Mac側)
$ go version
go version go1.11 darwin/amd64

(サーバー用 Linux側)
$ go version
go version go1.11.4 linux/amd64

gRPC の Helloworld を改変していく感じで利用させていただきます。
grpc-go/examples/helloworld at master · grpc/grpc-go

ディレクトリ構成が自前のサブモジュールを使っているので少し本家と違いますが適宜置き換えてください(わかりにくくてすみません)

go-grpc-template/
├── README.md
├── client
│   └── main.go
├── go.mod
├── go.sum
├── grpc-gen-circleci-template  # コンパイルされた proto コードが入った submodule です
│   ├── doc
│   │   └── readme.md
│   └── helloworld.pb.go
└── server
    └── main.go

go get する

$ go get go.opencensus.io/*
$ go get contrib.go.opencensus.io/exporter/stackdriver 

変更後 コード

server/main.go
package main

 import (
    "context"
    "contrib.go.opencensus.io/exporter/stackdriver"
    pb "go-grpc-template/grpc-gen-circleci-template"
    "go.opencensus.io/plugin/ocgrpc"
    "go.opencensus.io/trace"
    "google.golang.org/grpc"
    "google.golang.org/grpc/reflection"
    "log"
    "net"
    "os"
    "time"
 )

 const (
    port = "0.0.0.0:50051"
 )

 // server is used to implement helloworld.GreeterServer.
 type server struct{}

 // SayHello implements helloworld.GreeterServer
 func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
    log.Printf("ctx: %v", ctx)
    ctx, span := trace.StartSpan(ctx, "grpc-template.server")
    defer span.End()
    log.Printf("Received: %v", in.Name)
    time.Sleep(80 * time.Millisecond)
    return &pb.HelloReply{Message: "Hello " + in.Name}, nil
 }

 func main() {
    // Create and register a OpenCensus Stackdriver Trace exporter.
    exporter, err := stackdriver.NewExporter(stackdriver.Options{
        ProjectID: os.Getenv("GOOGLE_CLOUD_PROJECT"),
    })
    if err != nil {
        log.Fatal(err)
    }
    trace.RegisterExporter(exporter)

    /*
    // Register the views to collect server request count.
    if err := view.Register(ocgrpc.DefaultServerViews...); err != nil {
        log.Fatal(err)
    }
    */
    // Configure 100% sample rate, otherwise, few traces will be sampled.
    trace.ApplyConfig(trace.Config{DefaultSampler: trace.AlwaysSample()})

    lis, err := net.Listen("tcp", port)
    if err != nil {
        log.Fatalf("failed to listen: %v", err)
    }

    // Set up a new server with the OpenCensus
    // stats handler to enable stats and tracing.
    s := grpc.NewServer(grpc.StatsHandler(new(ocgrpc.ServerHandler)))
    pb.RegisterGreeterServer(s, &server{})

    // Register reflection service on gRPC server.
    reflection.Register(s)
    if err := s.Serve(lis); err != nil {
        log.Fatalf("failed to serve: %v", err)
    }
 }

grpc.StatsHandler(new(ocgrpc.ServerHandler))がないとTraceIDが伝搬されていかないので注意

(全く本質ではないですがServer側のアクセス制限に気をつけてください。自分はちょいハマりしました。)

client/main.go
package main

import (
    "context"
        "contrib.go.opencensus.io/exporter/stackdriver"
    pb "go-grpc-template/grpc-gen-circleci-template"
    "go.opencensus.io/plugin/ocgrpc"
    "go.opencensus.io/trace"
    "google.golang.org/grpc"
    "log"
    "os"
    "time"
)

const (
    address = "<VMの外部IP>:50051"
    defaultName = "world"
)

func main() {
    // Create and register a OpenCensus Stackdriver Trace exporter.
    exporter, err := stackdriver.NewExporter(stackdriver.Options{
        ProjectID: os.Getenv("GOOGLE_CLOUD_PROJECT"),
    })
    if err != nil {
        log.Fatal(err)
    }
    trace.RegisterExporter(exporter)

    // Configure 100% sample rate, otherwise, few traces will be sampled.
    trace.ApplyConfig(trace.Config{DefaultSampler: trace.AlwaysSample()})

    // Set up a connection to the server.
    conn, err := grpc.Dial(address, grpc.WithInsecure(), grpc.WithStatsHandler(&ocgrpc.ClientHandler{}))
    if err != nil {
        log.Fatalf("did not connect: %v", err)
    }
    defer conn.Close()
    c := pb.NewGreeterClient(conn)

    // Contact the server and print out its response.
    name := defaultName
    if len(os.Args) > 1 {
        name = os.Args[1]
    }

    for i := 0; i < 10; i++ {
        // Create a span with the background context, making this the parent span.
        // A span must be closed.
        ctx, span := trace.StartSpan(context.Background(), "grpc-template.client")
        time.Sleep(80 * time.Millisecond)
        r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name})
        if err != nil {
            log.Fatalf("could not greet: %v", err)
        }
        log.Printf("trace id: %v", ctx)
        log.Printf("Greeting: %s", r.Message)
        span.End()
    }
}

こちらも同様にgrpc.WithStatsHandler(&ocgrpc.ClientHandler{})を忘れると伝搬がなされないので注意!

実行する

(client, server それぞれのディレクトリで)
$ export GOOGLE_CLOUD_PROJECT="<YOUR_PROJECT_ID>"
$ go run main.go

コンソールを確認する (Google Cloud Platform Stackdriver Trace)

Image

すごいいいいいいいい!!
僕の知識不足で詰まるところはいくつかあったもののこれだけ簡単にトレースできるのは素晴らしすぎる…

次は gRPC Stream とかでも試してみたい

8
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
8