Building a Dune Quote Service Using Kubernetes Ingress gRPC

Written by konginc | Published 2021/09/03
Tech Story Tags: kubernetes | grpc | kubernetes-ingress | kong-gateway | kong-ingress-conroller | insomnia | api-testing | good-company

TLDRIn this tutorial, I’ll explain how to deploy a gRPC service to Kubernetes and provide external access to the service using Kong's Ingress Controller.via the TL;DR App

APIs come in all different shapes and forms. In this tutorial, I’ll show you a Kubernetes Ingress gRPC example. I’ll explain how to deploy a gRPC service to Kubernetes and provide external access to the service using Kong’s Kubernetes Ingress Controller. 
And to hype you up a little bit about the upcoming live-action movie, Dune, based on Frank Herbert’s book, I created a Kubernetes service that delivers Dune quotes. It’s arguably one of the most outstanding science fiction books ever written…what do you think? Let me know @GAMUSSA on Twitter.
Before we dive into the code, let’s review a few basics.

How Does gRPC Work With Kong?

gRPC is a procedural framework initially developed by Google in 2015. Based on HTTP2 protocol for transport and Protocol Buffer (Protobuf) as the interface definition language, gRPC has seen growing adoption in recent years. gRPC has several capabilities that traditional REST APIs struggle with, such as bidirectional streaming and efficient binary encoding.
Kong supports TCP streams and can proxy any protocol built on top of a TCP or TLS. We felt that native support for gRPC would allow a growing user base to leverage Kong to manage their REST and gRPC services uniformly.
Now that we’ve covered the basics, let’s take a look at the Protobuf definition of my Dune Quote service.

Protobuf Definition

My Protobuf definition contains a quote service that uses two methods. One method is to deliver a simple quote on any gRPC request. That method doesn’t require any input parameter, so we’ll be using the Protobuf empty type. And as a return, it will have a quote message that will include a message field.
And another method that I have here is a GetQuoteStream method that also doesn’t require any parameters to provide. In this case, I also used an empty type. But in this case, I’m returning a stream of these results.
syntax = "proto3";
 
package io.kong.developer.quoteservice;
 
import "google/protobuf/empty.proto";
 
option java_multiple_files = true;
option java_package = "io.kong.developer.quoteservice";
 
service QuoteService {
 
  rpc GetQuote(google.protobuf.Empty) returns (QuoteMessage) ;
  
  rpc GetQuoteStream(google.protobuf.Empty) returns (stream QuoteMessage) ;
 
}
 
message QuoteMessage{
  string message = 1;
}
I’m using Java to generate a server-side implementation of the service. However, you can take this proto file and implement this service in any language that you use. 
Protobuf comes with various tools in different languages, including some code generators that support your language of choice.
Let’s take a look at the deployment for our Dune Quote service.

Kubernetes Deployment Review

I’m using the same image in my deployment, but my active service will be using version 3.
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: dune-quote-service
spec:
  replicas: 1
  selector:
    matchLabels:
      app: dune-quote-service
  template:
    metadata:
      labels:
        app: dune-quote-service
    spec:
      containers:
        - image: gamussa/reactive-quote-service:0.0.3
          imagePullPolicy: Always
          name: dune-quote-service
          ports:
            - containerPort: 9001
          env:
            - name: GRPC_SERVER_PORT
              value: "9001"
            - name: GRPC_SERVER_SECURITY_ENABLED
              value: "true"
            - name: GRPC_SERVER_SECURITY_CERTIFICATECHAIN
              value: "file:/mnt/grpc-cert-chain/server.crt"
            - name: GRPC_SERVER_SECURITY_PRIVATEKEY
              value: "file:/mnt/grpc-pk/server.key"
          volumeMounts:
            - mountPath: /mnt/grpc-cert-chain
              name: grpc-cert-chain
            - mountPath: /mnt/grpc-pk
              name: grpc-pk
      volumes:
        - name: grpc-cert-chain
          secret:
            secretName: grpc-cert-chain
        - name: grpc-pk
          secret:
            secretName: grpc-pk
My application will be running the GRPC_SERVER on port 9001. And this port will be available to the service to expose this inside my Kubernetes cluster.
My service has security enabled. And I do provide some certificates in the gRPC server private key.
          name: dune-quote-service
          ports:
            - containerPort: 9001
          env:
            - name: GRPC_SERVER_PORT
              value: "9001"
            - name: GRPC_SERVER_SECURITY_ENABLED
              value: "true"
            - name: GRPC_SERVER_SECURITY_CERTIFICATECHAIN
              value: "file:/mnt/grpc-cert-chain/server.crt"
            - name: GRPC_SERVER_SECURITY_PRIVATEKEY
              value: "file:/mnt/grpc-pk/server.key"
I’m using a self-signed TLS certificate in this example. You’ll need to use a certificate signed by a certification authority that you trust in a production use case. 
I created the secrets. After that, I mount those secrets as files in my deployment.
          volumeMounts:
            - mountPath: /mnt/grpc-cert-chain
              name: grpc-cert-chain
            - mountPath: /mnt/grpc-pk
              name: grpc-pk

Service Review

My service will use port 9001 as my target port. Pay attention to Line 6, where I have the limitation: 
konghq.com/protocol
. It will respond to gRPC as a protocol because my service has security enabled, and I need to notify my Kong Gateway that my service understands gRPCS protocol.
---
apiVersion: v1
kind: Service
metadata:
  annotations:
    konghq.com/protocol: grpcs
  name: dune-quote-service
  labels:
    app: dune-quote-service
spec:
  ports:
    - name: grpc
      port: 9001
      targetPort: 9001
  selector:
    app: dune-quote-service
And last but not least, I need to create an ingress.

Ingress Setup

You see some familiar things like 
ingress.class
 that corresponds to my open-source Kong Ingress Controller. Now, I also have an annotation here: 
konghq.com/protocols
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: dune-quote-service
  annotations:
    konghq.com/protocols: grpc,grpcs
    kubernetes.io/ingress.class: kong
    kubernetes.io/tls-acme: "true"
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  tls:
    - secretName: kong.gamov.dev
      hosts:
        - kong.gamov.dev
  rules:
    - host: kong.gamov.dev
      http:
        paths:
          - path: /
            pathType: ImplementationSpecific
            backend:
              service:
                name: dune-quote-service
                port:
                  number: 9001
It’s an ingress configuration that allows Kong Gateway to understand what kind of incoming clients we expect. In this case, we’re creating this ingress for gRPCS because we’re expecting gRPC clients to connect to the service. We’re using gRPC or gRPCS protocols. 

Before deploying the Kubernetes service, we’ll need to set up Kong Ingress Controller and Kubernetes cert-manager.

Ingress Controller and Cert Manager Setup

I’ve already installed the Kong Ingress Controller. If you haven’t, follow along in my previous getting started tutorial.
I configured my cluster to use cert-manager. I will be using certificates issued by LetsEncrypt. In Kong’s documentation, there is a getting started guide for cert-manager that allows you to configure your Kong Ingress Controller and your API Gateway to use automatically issued certificates for TLS.
Next, we need to follow the Kubernetes cert-manager documentation
Then, we’ll be able to request the TLS certificate from the certification authority. And after that, the application will use port 443 TLS to communicate with external clients. 
That’s why I have this annotation, 
kubernetes.io/tls-acme
, to use an automatic certificate management extension enabled for my cluster. I’m using the LetsEncrypt server production version to receive the certificate.
kubernetes.io/tls-acme: "true"
cert-manager.io/cluster-issuer: letsencrypt-prod
The next thing is to configure my ingress. I need to specify that I’m using TLS for my ingress and issue a certificate for this domain name. This domain name corresponds to the external IP address that Google Cloud Platform provided.
spec:
  tls:
    - secretName: kong.gamov.dev
      hosts:
        - kong.gamov.dev
It’s the same configuration rules for the particular path. By default, that will correspond to my Dune Quote service.
         - path: /
            pathType: ImplementationSpecific
            backend:
              service:
                name: dune-quote-service
                port:
                  number: 9001
Inbound traffic will listen on gRPC or gRPCS protocols. My Kong Gateway and Dune Quote service will also use the gRPCS protocol because I deployed my service using gRPCS.
   konghq.com/protocols: grpc,grpcs
   kubernetes.io/ingress.class: kong
   kubernetes.io/tls-acme: "true"
   cert-manager.io/cluster-issuer: letsencrypt-prod
The next thing is to configure my ingress. I need to specify that I’m using TLS for my ingress and issue a certificate for this domain name. This domain name corresponds to the external IP address that Google Cloud Platform provided.
spec:
  tls:
    - secretName: kong.gamov.dev
      hosts:
        - kong.gamov.dev
It’s the same configuration rules for the particular path. By default, that will correspond to my Dune Quote service.
         - path: /
            pathType: ImplementationSpecific
            backend:
              service:
                name: dune-quote-service
                port:
                  number: 9001
Inbound traffic will listen on gRPC or gRPCS protocols. My Kong Gateway and Dune Quote service will also use the gRPCS protocol because I deployed my service using gRPCS.
   konghq.com/protocols: grpc,grpcs
   kubernetes.io/ingress.class: kong
   kubernetes.io/tls-acme: "true"
   cert-manager.io/cluster-issuer: letsencrypt-prod

Deploy the Kubernetes Service

Before we can deploy, we need to provide two secrets.
We can provide the secrets with the below script. It generates a self-signed certificate and, after that, applies this certificate and key as my secrets.
#!/usr/bin/env bash
 
openssl req -x509 -nodes -subj "/CN=gamov.dev" -newkey rsa:4096 -sha256 -keyout server.key -out server.crt -days 3650
 
kubectl create secret generic grpc-cert-chain --from-file=server.crt=server.crt
kubectl create secret generic grpc-pk --from-file=server.key=server.key
Next, let’s restart the deployment and bring up our service.

Test the Service

Once our service is up, we can use Insomnia to hit the service.
In Insomnia, let’s upload our proto file. Once that’s uploaded, we’ll see two methods, GetQuote and GetQuoteStream.
When we hit GetQuote, we get some quotes from Dune. It works!
Next, let’s see if streaming works by switching to the GetQuoteStream. When I click Start, my response contains 10 responses from the gRPCS server. Nice!
And my implementation has an interceptor that will log all the calls to my service.

That’s a Wrap!

In this article, I showed you how to deploy a gRPC service to Kubernetes and expose it outside of the cluster using Kong Ingress Controller.
Once you’ve finished this Kubernetes Ingress gRPC example, you may find these other Kubernetes tutorials helpful:
Have questions or want to stay in touch with the Kong community? Join us wherever you hang out:

Written by konginc | Build apps faster with the API, Ingress, and Service Mesh Connectivity Platform.
Published by HackerNoon on 2021/09/03