Skip to content

Commit

Permalink
Merge pull request #1043 from fengli79/master
Browse files Browse the repository at this point in the history
Add an end to end example for gRPC A66 metrics.
  • Loading branch information
fengli79 authored Jan 31, 2024
2 parents 8132882 + 2fa5594 commit 07e34a7
Show file tree
Hide file tree
Showing 17 changed files with 2,126 additions and 0 deletions.
130 changes: 130 additions & 0 deletions examples/grpc-observability/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# gRPC-Observability Example

## Features

* End to end examples to generate the gRPC metrics with Prometheus, including:
* A frontend microservice (
in [gRPC-Spring](https://github.com/grpc-ecosystem/grpc-spring)), which
keeps calling the backend microservices.
* A backend microservice (
in [gRPC-Spring](https://github.com/grpc-ecosystem/grpc-spring)), which
serves unary/client streaming/server streaming and bidi streaming example
services.
* Instructions to containerize them and deploy them in kubernetes
* Monitoring dashboards to support
the [gRPC A66](https://github.com/grpc/proposal/blob/master/A66-otel-stats.md)
spec.
* Deploy the Grafana and import
the [gRPC A66](https://github.com/grpc/proposal/blob/master/A66-otel-stats.md)
dashboard

## Build the end to end example

Under the root directory of your grpc-spring repo

```
./gradlew build
```

## Run the end to end example

Run the backend microservice locally

```
./gradlew examples:grpc-observability:backend:bootRun
```

Run the frontend microservice locally. Please note that the backend needs to be
started first to be ready to serve the client calls.

```
./gradlew examples:grpc-observability:frontend:bootRun
```

The backend microservice will

- listen on the TCP port 9091 for the gRPC calls from the frontend microservice.
- listen on the TCP port 8081 for the Prometheus scrape requests.
The frontend microservice will
- send the unary/client streaming/server streaming/bidi streaming calls to the
backend microservices via TCP port 9091.
- listen on the TCP port 8080 for the Prometheus scrape requests.

## Containerize the frontend/backend microservices

Build the docker image for the backend microservice

```
docker build -t grpc-observability/grpc-spring-example-backend examples/grpc-observability/backend
```

Run the backend microservice with docker

```
docker run --network host grpc-observability/grpc-spring-example-backend
```

Build the docker image for the frontend microservice

```
docker build -t grpc-observability/grpc-spring-example-frontend examples/grpc-observability/frontend
```

Run the frontend microservice with docker

```
docker run --network host grpc-observability/grpc-spring-example-frontend
```

## Deploy the end to end example to kubernetes

To deploy the example to kubernetes, please upload the docker images to a
registry by yourself and modify the frontend.yaml/backend.yaml with your docker
image location, and then run following commands.

Deploy the backend microservice in kubernetes

```
kubectl apply -f ./examples/grpc-observability/backend/backend.yaml
```

Deploy the frontend microservice in kubernetes

```
kubectl apply -f ./examples/grpc-observability/frontend/frontend.yaml
```

## Set up the Prometheus to collect the gRPC metrics

Once the frontend/backend microservices are deployed (either locally or on
cloud), you may set up the Prometheus to start scraping the metrics from them.
Depends on where you run the frontend/backend microservices, you may need to
deploy the Prometheus to a proper location to be able to access them, such as,
the same cloud, etc.

If you
use [Google Managed Prometheus](https://cloud.google.com/stackdriver/docs/managed-prometheus),
you may need to configure the PodMonitoring resource to tell where are endpoints
to scrape the Prometheus metrics.

```
kubectl apply -f ./examples/grpc-observability/pod_monitoring.yaml
```

## Set up the Grafana dashboard

Once we have the gRPC metrics scraped by the Prometheus, we may set up a Grafana
server to visualize them.

- Set up a Grafana server, deploy it to a place where can connect the Prometheus
server as its data source.
- Create a Grafana dashboard by importing the
[examples/grpc-observability/grafana/prometheus/microservices-grpc-dashboard.json](http://github.com/grpc-ecosystem/grpc-spring/blob/master/examples/grpc-observability/grafana/prometheus/microservices-grpc-dashboard.json)
file.

If you
use [Google Managed Prometheus](https://cloud.google.com/stackdriver/docs/managed-prometheus),
you need to follow
the [Grafana Query User Guide](https://cloud.google.com/stackdriver/docs/managed-prometheus/query)
to set up the Grafana server
and [data source syncer](https://github.com/GoogleCloudPlatform/prometheus-engine/tree/main/cmd/datasource-syncer).
3 changes: 3 additions & 0 deletions examples/grpc-observability/backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
FROM eclipse-temurin:17-jdk-alpine
COPY build/libs/backend.jar backend.jar
ENTRYPOINT ["java","-jar","/backend.jar"]
52 changes: 52 additions & 0 deletions examples/grpc-observability/backend/backend.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Copyright 2023 gRPC Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend
spec:
replicas: 1
selector:
matchLabels:
app: backend
template:
metadata:
labels:
app: backend
monitor: prometheus
spec:
containers:
- name: backend
# Please upload the docker image of grpc-observability/grpc-spring-example-backend to an image registry, such as https://cloud.google.com/artifact-registry.
image: <your image of the backend>
ports:
- name: monitoring
containerPort: 8081
- name: grpc
containerPort: 9091
---
apiVersion: v1
kind: Service
metadata:
name: backend
spec:
clusterIP: None
selector:
app: backend
ports:
- name: monitoring
port: 8081
- name: grpc
port: 9091
26 changes: 26 additions & 0 deletions examples/grpc-observability/backend/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright 2023 The gRPC-GCP-Mobile Authors
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
plugins {
id 'org.springframework.boot'
}

dependencies {
implementation project(':examples:grpc-observability:proto')
implementation project(':grpc-server-spring-boot-starter')
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation "org.springframework.boot:spring-boot-starter-web"
implementation 'io.micrometer:micrometer-registry-prometheus'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright (c) 2016-2023 The gRPC-Spring Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package net.devh.boot.grpc.examples.observability.backend;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class BackendApplication {

public static void main(String[] args) {
SpringApplication.run(BackendApplication.class, args);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/*
* Copyright (c) 2016-2023 The gRPC-Spring Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package net.devh.boot.grpc.examples.observability.backend;

import java.util.concurrent.ThreadLocalRandom;

import io.grpc.Status;
import io.grpc.stub.StreamObserver;
import net.devh.boot.grpc.examples.observability.proto.BidiStreamingRequest;
import net.devh.boot.grpc.examples.observability.proto.BidiStreamingResponse;
import net.devh.boot.grpc.examples.observability.proto.ClientStreamingRequest;
import net.devh.boot.grpc.examples.observability.proto.ClientStreamingResponse;
import net.devh.boot.grpc.examples.observability.proto.ExampleServiceGrpc.ExampleServiceImplBase;
import net.devh.boot.grpc.examples.observability.proto.ServerStreamingRequest;
import net.devh.boot.grpc.examples.observability.proto.ServerStreamingResponse;
import net.devh.boot.grpc.examples.observability.proto.UnaryRequest;
import net.devh.boot.grpc.examples.observability.proto.UnaryResponse;
import net.devh.boot.grpc.server.service.GrpcService;

@GrpcService
public class ExampleServiceImpl extends ExampleServiceImplBase {

private boolean InjectError() {
// We create ~5% error.
return ThreadLocalRandom.current().nextInt(0, 99) >= 95;
}

@Override
public void unaryRpc(UnaryRequest request,
StreamObserver<UnaryResponse> responseObserver) {
if (InjectError()) {
responseObserver.onError(Status.INTERNAL.asException());
} else {
responseObserver.onNext(UnaryResponse.newBuilder().setMessage(request.getMessage()).build());
responseObserver.onCompleted();
}
}

@Override
public StreamObserver<ClientStreamingRequest> clientStreamingRpc(
StreamObserver<ClientStreamingResponse> responseObserver) {
return new StreamObserver<>() {
@Override
public void onNext(ClientStreamingRequest value) {
responseObserver.onNext(
ClientStreamingResponse.newBuilder().setMessage(value.getMessage()).build());
}

@Override
public void onError(Throwable t) {
responseObserver.onError(t);
}

@Override
public void onCompleted() {
if (InjectError()) {
responseObserver.onError(Status.INTERNAL.asException());
} else {
responseObserver.onCompleted();
}
}
};
}

@Override
public void serverStreamingRpc(ServerStreamingRequest request,
StreamObserver<ServerStreamingResponse> responseObserver) {
if (InjectError()) {
responseObserver.onError(Status.INTERNAL.asException());
} else {
responseObserver.onNext(
ServerStreamingResponse.newBuilder().setMessage(request.getMessage()).build());
responseObserver.onCompleted();
}
}

@Override
public StreamObserver<BidiStreamingRequest> bidiStreamingRpc(
StreamObserver<BidiStreamingResponse> responseObserver) {
return new StreamObserver<>() {
@Override
public void onNext(BidiStreamingRequest value) {
responseObserver.onNext(
BidiStreamingResponse.newBuilder().setMessage(value.getMessage()).build());
}

@Override
public void onError(Throwable t) {
responseObserver.onError(t);
}

@Override
public void onCompleted() {
if (InjectError()) {
responseObserver.onError(Status.INTERNAL.asException());
} else {
responseObserver.onCompleted();
}
}
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Port serves the monitoring traffic.
server.port=8081
# Expose the prometheus metrics via the monitoring port.
# By default, expose on `/actuator/prometheus`.
management.endpoints.web.exposure.include=prometheus
# Port serves the gRPC traffic.
grpc.server.port=9091
3 changes: 3 additions & 0 deletions examples/grpc-observability/frontend/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
FROM eclipse-temurin:17-jdk-alpine
COPY build/libs/frontend.jar frontend.jar
ENTRYPOINT ["java","-jar","/frontend.jar"]
Loading

0 comments on commit 07e34a7

Please sign in to comment.