Guest post originally published on Kong’s blog by Viktor Gamov, Principal Developer Advocate at Kong
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: