View on GitHub

core

Cloud Robotics Core: Kubernetes, Federation, App Management

Deploying a gRPC service written in C++

Estimated time: 60 min

In this guide we will deploy a gRPC service written in C++ and deploy it to our Google Kubernetes Engine (GKE) cluster in the cloud in such a way that authentication is required for access. We will show how to access the service from the workstation and how to access it from code running in the robot’s Kubernetes cluster.

Prerequisites

All files for this tutorial are located in docs/how-to/examples/greeter-service/.

git clone https://github.com/googlecloudrobotics/core
cd core/docs/how-to/examples/greeter-service

Set your GCP project ID as an environment variable:

export PROJECT_ID=[YOUR_GCP_PROJECT_ID]

Running gRPC server and client locally

We will use gRPC’s quickstart example with small modifications. If you like to learn more about gRPC in C++, follow their guide first.

The gRPC helloworld.Greeter service is defined in proto/helloworld.proto. It accepts a HelloRequest containing a name and responds with a HelloReply containing a message. The server is implemented in server/server.cc and the client is implemented client/client.cc. The client sends the request with name: "world" to the server which responds with message: "Hello <name>".

In this tutorial, we build the server and client code inside Docker containers, so you don’t need to install the gRPC library. If you prefer, you can install the gRPC following these instructions and build the server and client locally using the provided Makefile.

Make sure the Docker daemon is running and your user has the necessary privileges:

docker run --rm hello-world

If this command fails, make sure Docker is installed according to the installation instructions.

The Docker image for the server is configured in server/Dockerfile:

FROM grpc/cxx:1.12.0

WORKDIR /data

COPY server/server.cc ./server/
COPY proto/helloworld.proto ./proto/
COPY Makefile ./

RUN make greeter-server && make clean

CMD ["./greeter-server"]

We use the grpc/cxx Docker image which contains all the build tools and libraries (g++, make, protoc, and grpc) we need to build the greeter-server binary. The Docker image for the client is configured in client/Dockerfile which builds the greeter-client from client/client.cc.

To build the Docker images, run:

docker build -t greeter-server -f server/Dockerfile .
docker build -t greeter-client -f client/Dockerfile .

Note The docker files are in the subfolders greeter-server/server/ and greeter-server/client, but the docker command must be called from greeter-server/ to include the files which are shared between the server and the client.

You should now have an image tagged greeter-server and one tagged greeter-client in your local registry:

docker images | grep greeter

To run the server locally, the container’s port 50051, which specified as gRPC port in server.cc, has to be published to your machine with the flag -p 50051:50051:

docker run --rm -p 50051:50051 --name greeter-server greeter-server

In another console run the client container. The flag --network=host tells the container to use your workstation’s network stack which allows the client to connect to localhost.

docker run --rm --network=host greeter-client ./greeter-client localhost

You should see Greeter received: Hello world in the client’s output and Received request: name: "world" in the server’s output. You can also send your own name in the gRPC request to the server, try:

docker run --rm --network=host greeter-client \
  ./greeter-client localhost $USER

You can stop the server from another terminal by running:

docker stop greeter-server

Uploading the Docker image to the cloud

In order to be able to run the server as a container in our cloud cluster, we need to upload the Docker image to our GCP project’s private container registry.

Enable the Docker credential helper:

gcloud auth configure-docker

Tag the image and push it to the registry:

docker tag greeter-server gcr.io/$PROJECT_ID/greeter-server
docker push gcr.io/$PROJECT_ID/greeter-server

The image should now show up in the container registry.

Deploying the service in the cloud using Kubernetes

Run the following command to create greeter-server.yaml using the provided template:

cat greeter-server.yaml.tmpl | envsubst >greeter-server.yaml

This file contains the information needed by Kubernetes to run the gRPC service in our cloud cluster. The three resources, Ingress, Service, and Deployment, are explained in the deploying a service tutorial. In contrast to the other tutorial, the Ingress tells nginx to forward incoming requests to a gRPC backend.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: greeter-server-ingress
  annotations:
    nginx.ingress.kubernetes.io/backend-protocol: GRPC
    nginx.ingress.kubernetes.io/auth-url: "http://token-vendor.default.svc.cluster.local/apis/core.token-vendor/v1/token.verify?robots=true"
spec:
  ingressClassName: nginx
  rules:
  - host: "www.endpoints.${PROJECT_ID}.cloud.goog"
    http:
      paths:  # must match the namespace and service name in the proto
      - path: /helloworld.Greeter/
        pathType: Prefix
        backend:
          service:
            name: greeter-server-service
            # must match the port used in server.cc
            port:
              number: 50051
---

Make sure that kubectl points to the correct GCP project:

kubectl config get-contexts

If the correct cluster is not marked with an asterisk in the output, you can switch contexts with kubectl config use-context [...].) Then deploy by applying the configuration:

kubectl apply -f greeter-server.yaml

You can explore the various resources that were created on your cluster as a result of this command in the GKE Console or with kubectl, e.g.:

kubectl get pods

The resulting list should contain a running pod with a name like greeter-server-xxxxxxxxxx-xxxxx.

Redeploying after a change

For convenience, deploy.sh provides some commands to create, delete, and update the service. If you make changes to greeter-server.yaml.tmpl, all you have to do is run:

./deploy.sh update_config

If you make changes to server.cc, you need to run:

./deploy.sh update_server

This builds, tags, and pushes the Docker image, and then forces a redeployment of the image by calling kubectl delete pod -l 'app=greeter-server-app'. It also updates the resource definitions, so you don’t have to run ./deploy.sh update_config if you made changes to both files.

Accessing the API

In client/client.cc we use grpc::InsecureChannelCredentials() when talking to localhost while we use grpc::GoogleDefaultCredentials() when talking to any other address. SSL authentication with credentials from the user or robot are necessary when talking to the greeter-server in the Cloud Robotics project.

  if (grpc_endpoint.find("localhost:") == 0 ||
      grpc_endpoint.find("127.0.0.1:") == 0) {
    channel_creds = grpc::InsecureChannelCredentials();
  } else {
    channel_creds = grpc::GoogleDefaultCredentials();
  }

Let’s try to access our server. We have to connect to the nginx ingress which is hosted on www.endpoints.$PROJECT_ID.cloud.goog:443. To ensure we have valid credentials to talk to nginx we have to mount our ~/.config folder in the container.

docker run --rm -v ~/.config:/root/.config greeter-client \
  ./greeter-client www.endpoints.$PROJECT_ID.cloud.goog:443 workstation

Recall that when running ./greeter-server on your workstation you were able to see the server’s log output upon receiving a request. This log output is also recorded when the server is running in the cloud cluster. To inspect it, run:

kubectl logs -l 'app=greeter-server-app'

Or go to the GKE Console, select the greeter-server workload and click on “Container logs”.

Accessing the API from the robot

In order to run greeter-client on the robot’s Kubernetes cluster, we again package it as a Docker image and push it to our container registry, to which the robot also has access. Our deploy script offers a command to build, tag, and push the image to the cloud registry, like we did with the server container:

./deploy.sh push_client

And finally, to execute the script, SSH into robot and run:

export PROJECT_ID=[YOUR_GCP_PROJECT_ID]
docker pull grpc/cxx:1.12.0  # This may take several minutes, depending on WiFi connection
kubectl run -ti --rm --restart=Never --image=gcr.io/$PROJECT_ID/greeter-client greeter-client \
  -- ./greeter-client www.endpoints.$PROJECT_ID.cloud.goog:443 robot

You should see the server’s answer Hello robot.

Two things are noteworthy:

Cleaning up

In order to stop the service in the cloud cluster and revert the configuration changes, run:

./deploy.sh delete