Skip to content

Commit 7472b60

Browse files
committed
Add CSM Observability example
1 parent 3267089 commit 7472b60

File tree

7 files changed

+303
-11
lines changed

7 files changed

+303
-11
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# CSM Observability
2+
3+
This examples shows how to configure CSM Observability for gRPC client and
4+
server applications (configured once per binary), and shows what type of
5+
telemetry data it can produce for certain RPCs with additional CSM Labels. The
6+
gRPC Client accepts configuration from an xDS Control plane as the default
7+
address that it connects to is "xds:///helloworld:50051", but this can be
8+
overridden with the command line flag --server_addr. This can be plugged into
9+
the steps outlined in the CSM Observability User Guide.
10+
11+
## Try it (locally if overwritten xDS Address)
12+
13+
```
14+
go run server/main.go
15+
```
16+
17+
```
18+
go run client/main.go
19+
```
20+
21+
Curl to the port where Prometheus exporter is outputting metrics data:
22+
```
23+
curl localhost:9464/metrics
24+
```
25+
26+
# Building
27+
From the grpc-go directory:
28+
29+
Client:
30+
docker build -t <TAG> -f examples/features/csm_observability/client/Dockerfile .
31+
32+
Server:
33+
docker build -t <TAG> -f examples/features/csm_observability/server/Dockerfile .
34+
35+
Note that this example will not work by default, as the client uses an xDS
36+
Scheme and thus needs xDS Resources to connect to the server. Deploy the built
37+
client and server containers within Cloud Service Mesh in order for this example
38+
to work, or overwrite target to point to :<server serving port>.
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Copyright 2024 gRPC authors.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
# Dockerfile for building the example client. To build the image, run the
16+
# following command from grpc-go directory:
17+
# docker build -t <TAG> -f examples/features/csm_observability/client/Dockerfile .
18+
FROM golang:1.21-alpine as build
19+
20+
RUN apk --no-cache add curl
21+
22+
# Make a grpc-go directory and copy the repo into it.
23+
WORKDIR /go/src/grpc-go
24+
COPY . .
25+
26+
# Build a static binary without cgo so that we can copy just the binary in the
27+
# final image, and can get rid of the Go compiler and gRPC-Go dependencies.
28+
RUN cd examples/features/csm_observability/client && go build -tags osusergo,netgo .
29+
30+
FROM alpine
31+
RUN apk --no-cache add curl
32+
COPY --from=build /go/src/grpc-go/examples/features/csm_observability/client/client .
33+
ENV GRPC_GO_LOG_VERBOSITY_LEVEL=99
34+
ENV GRPC_GO_LOG_SEVERITY_LEVEL="info"
35+
ENTRYPOINT ["./client"]
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
*
3+
* Copyright 2024 gRPC authors.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*/
18+
19+
package main
20+
21+
import (
22+
"context"
23+
"flag"
24+
"fmt"
25+
"log"
26+
"net/http"
27+
"time"
28+
29+
"google.golang.org/grpc"
30+
"google.golang.org/grpc/credentials/insecure"
31+
"google.golang.org/grpc/examples/features/proto/echo"
32+
"google.golang.org/grpc/stats/opentelemetry"
33+
"google.golang.org/grpc/stats/opentelemetry/csm"
34+
_ "google.golang.org/grpc/xds" // To install the xds resolvers and balancers.
35+
36+
"github.com/prometheus/client_golang/prometheus/promhttp"
37+
"go.opentelemetry.io/otel/exporters/prometheus"
38+
"go.opentelemetry.io/otel/sdk/metric"
39+
)
40+
41+
var (
42+
target = flag.String("target", "xds:///helloworld:50051", "the server address to connect to")
43+
prometheusEndpoint = flag.String("prometheus_endpoint", ":9464", "the Prometheus exporter endpoint")
44+
)
45+
46+
func main() {
47+
flag.Parse()
48+
exporter, err := prometheus.New()
49+
if err != nil {
50+
log.Fatalf("Failed to start prometheus exporter: %v", err)
51+
}
52+
provider := metric.NewMeterProvider(metric.WithReader(exporter))
53+
go http.ListenAndServe(*prometheusEndpoint, promhttp.Handler())
54+
55+
cleanup := csm.EnableObservability(context.Background(), opentelemetry.Options{MetricsOptions: opentelemetry.MetricsOptions{MeterProvider: provider}})
56+
defer cleanup()
57+
58+
cc, err := grpc.NewClient(*target, grpc.WithTransportCredentials(insecure.NewCredentials()))
59+
if err != nil {
60+
log.Fatalf("Failed to start NewClient: %v", err)
61+
}
62+
defer cc.Close()
63+
c := echo.NewEchoClient(cc)
64+
65+
// Make a RPC every second. This should trigger telemetry to be emitted from
66+
// the client and the server.
67+
for {
68+
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
69+
r, err := c.UnaryEcho(ctx, &echo.EchoRequest{Message: "this is examples/opentelemetry"})
70+
if err != nil {
71+
log.Printf("UnaryEcho failed: %v", err)
72+
}
73+
fmt.Println(r)
74+
time.Sleep(time.Second)
75+
cancel()
76+
}
77+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Copyright 2024 gRPC authors.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
# Dockerfile for building the example server. To build the image, run the
16+
# following command from grpc-go directory:
17+
# docker build -t <TAG> -f examples/features/csm_observability/server/Dockerfile .
18+
19+
FROM golang:1.21-alpine as build
20+
RUN apk --no-cache add curl
21+
# Make a grpc-go directory and copy the repo into it.
22+
WORKDIR /go/src/grpc-go
23+
COPY . .
24+
25+
# Build a static binary without cgo so that we can copy just the binary in the
26+
# final image, and can get rid of the Go compiler and gRPC-Go dependencies.
27+
RUN cd examples/features/csm_observability/server && go build -tags osusergo,netgo .
28+
29+
FROM alpine
30+
RUN apk --no-cache add curl
31+
COPY --from=build /go/src/grpc-go/examples/features/csm_observability/server/server .
32+
ENV GRPC_GO_LOG_VERBOSITY_LEVEL=99
33+
ENV GRPC_GO_LOG_SEVERITY_LEVEL="info"
34+
ENTRYPOINT ["./server"]
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
*
3+
* Copyright 2024 gRPC authors.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*/
18+
19+
package main
20+
21+
import (
22+
"context"
23+
"flag"
24+
"fmt"
25+
"log"
26+
"net"
27+
"net/http"
28+
29+
"google.golang.org/grpc"
30+
pb "google.golang.org/grpc/examples/features/proto/echo"
31+
"google.golang.org/grpc/stats/opentelemetry"
32+
"google.golang.org/grpc/stats/opentelemetry/csm"
33+
34+
"github.com/prometheus/client_golang/prometheus/promhttp"
35+
"go.opentelemetry.io/otel/exporters/prometheus"
36+
"go.opentelemetry.io/otel/sdk/metric"
37+
)
38+
39+
var (
40+
port = flag.String("port", "50051", "the server address to connect to")
41+
prometheusEndpoint = flag.String("prometheus_endpoint", ":9464", "the Prometheus exporter endpoint")
42+
)
43+
44+
type echoServer struct {
45+
pb.UnimplementedEchoServer
46+
addr string
47+
}
48+
49+
func (s *echoServer) UnaryEcho(ctx context.Context, req *pb.EchoRequest) (*pb.EchoResponse, error) {
50+
return &pb.EchoResponse{Message: fmt.Sprintf("%s (from %s)", req.Message, s.addr)}, nil
51+
}
52+
53+
func main() {
54+
flag.Parse()
55+
exporter, err := prometheus.New()
56+
if err != nil {
57+
log.Fatalf("Failed to start prometheus exporter: %v", err)
58+
}
59+
provider := metric.NewMeterProvider(metric.WithReader(exporter))
60+
go http.ListenAndServe(*prometheusEndpoint, promhttp.Handler())
61+
62+
cleanup := csm.EnableObservability(context.Background(), opentelemetry.Options{MetricsOptions: opentelemetry.MetricsOptions{MeterProvider: provider}})
63+
defer cleanup()
64+
65+
lis, err := net.Listen("tcp", ":"+*port)
66+
if err != nil {
67+
log.Fatalf("Failed to listen: %v", err)
68+
}
69+
s := grpc.NewServer()
70+
pb.RegisterEchoServer(s, &echoServer{addr: ":" + *port})
71+
72+
log.Printf("Serving on %s\n", *port)
73+
74+
if err := s.Serve(lis); err != nil {
75+
log.Fatalf("Failed to serve: %v", err)
76+
}
77+
}

examples/go.mod

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,14 @@ go 1.21
44

55
require (
66
github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b
7+
github.com/prometheus/client_golang v1.19.1
8+
go.opentelemetry.io/otel/exporters/prometheus v0.49.0
9+
go.opentelemetry.io/otel/sdk/metric v1.27.0
710
golang.org/x/oauth2 v0.21.0
811
google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117
912
google.golang.org/grpc v1.64.0
1013
google.golang.org/grpc/gcp/observability v1.0.1
14+
google.golang.org/grpc/stats/opentelemetry v0.0.0-20240604165302-6d236200ea68
1115
google.golang.org/protobuf v1.34.1
1216
)
1317

@@ -22,10 +26,11 @@ require (
2226
cloud.google.com/go/monitoring v1.19.0 // indirect
2327
cloud.google.com/go/trace v1.10.7 // indirect
2428
contrib.go.opencensus.io/exporter/stackdriver v0.13.15-0.20230702191903-2de6d2748484 // indirect
29+
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.23.0 // indirect
2530
github.com/aws/aws-sdk-go-v2 v1.27.1 // indirect
26-
github.com/aws/aws-sdk-go-v2/config v1.27.17 // indirect
27-
github.com/aws/aws-sdk-go-v2/credentials v1.17.17 // indirect
28-
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.4 // indirect
31+
github.com/aws/aws-sdk-go-v2/config v1.27.4 // indirect
32+
github.com/aws/aws-sdk-go-v2/credentials v1.17.4 // indirect
33+
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.15.2 // indirect
2934
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.8 // indirect
3035
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.8 // indirect
3136
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect
@@ -35,6 +40,7 @@ require (
3540
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.4 // indirect
3641
github.com/aws/aws-sdk-go-v2/service/sts v1.28.11 // indirect
3742
github.com/aws/smithy-go v1.20.2 // indirect
43+
github.com/beorn7/perks v1.0.1 // indirect
3844
github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect
3945
github.com/cespare/xxhash/v2 v2.3.0 // indirect
4046
github.com/envoyproxy/go-control-plane v0.12.0 // indirect
@@ -48,11 +54,16 @@ require (
4854
github.com/google/uuid v1.6.0 // indirect
4955
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
5056
github.com/googleapis/gax-go/v2 v2.12.4 // indirect
57+
github.com/prometheus/client_model v0.6.1 // indirect
58+
github.com/prometheus/common v0.53.0 // indirect
59+
github.com/prometheus/procfs v0.15.0 // indirect
5160
go.opencensus.io v0.24.0 // indirect
61+
go.opentelemetry.io/contrib/detectors/gcp v1.27.0 // indirect
5262
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.52.0 // indirect
5363
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.52.0 // indirect
5464
go.opentelemetry.io/otel v1.27.0 // indirect
5565
go.opentelemetry.io/otel/metric v1.27.0 // indirect
66+
go.opentelemetry.io/otel/sdk v1.27.0 // indirect
5667
go.opentelemetry.io/otel/trace v1.27.0 // indirect
5768
golang.org/x/crypto v0.24.0 // indirect
5869
golang.org/x/net v0.26.0 // indirect
@@ -67,3 +78,5 @@ require (
6778
)
6879

6980
replace google.golang.org/grpc => ../
81+
82+
replace google.golang.org/grpc/stats/opentelemetry => ../stats/opentelemetry

0 commit comments

Comments
 (0)