+
We are excited to announce Grafana Labs is to acquire Kausal. Read more

gRPC & Kubernetes: GDG Devfest Ukraine (Part I)

Twitter profile image
on Oct 31, 2017

I recently gave a talk at GDG Devfest Ukraine about our use of gRPC & Kubernetes at Kausal. In the talk we discussed basic concepts of gRPC: Protocol Buffers, code generation and HTTP 2.0. We then discussed some more intermediate-level features, such as native Kubernetes load balancing, HTTP tunneling and instrumentation. This blog post is part one of the write up of the talk, but for those who can’t bear to wait, here’s the video:

Introduction

I started by framing the motivation of gRPC when compared to writing a HTTP/JSON server and client from scratch:

Hello World HTTP/JSON.

I presented a pretty minimal Golang “Hello world” server and client, using JSON and HTTP. The problems with this approach are: - There is no formal definition of the interface. This makes it hard to ensure client or server implementations conform to the correct interface. - This is a lot of code; you have to deal with all the serialisation, exception handling etc. You need to do it for every handler, and need to not make mistakes. Ensuring every handler deals with serialisation and error handling consistently becomes challenging. - Consider how to add things like compression, or support for other content types: it gets harder and messier. - Developers don’t expect a HTTP API anymore, they want client libraries in their language. I’m not an expert in the idiomatic style for every language, so how am I supposed to produce them? - Would I add streaming or server push? How about asynchronous handlers?
- How would I model operations (such as restart or execute) that don’t fit into REST principles?.

gRPC is designed to address these problems. gRPC itself describes itself as “a high performance, open-source universal RPC framework”. With gRPC, you write the interface definition once, and then use code generators to build server and client implementation in many languages. gRPC is technically encoding agnostic, but in practice it uses Protocol Buffers for encoding. I understand support for other encodings is coming soon.

This is what a gRPC interface definition looks like:

syntax = “proto3”;

package hello;

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}

To generate the Go server and client for this, you would run something like this:

$ protoc --go_out=plugins=grpc:. hello.proto

If you’re on a Mac, the protoc tool can be installed with Homebrew ($ brew install protobuf). Most Go projects seem to commit the generated code to their VCS, but I favour having a Makefile (or using go generate) to automatically generate the server and client code at build time - my projects typically do this in a Docker container to prevent developers having to install the tools on their machine.

Everything Old Is New Again: A History Lesson

RPC frameworks and code generation is not a new thing; they have existed for decades. The 1990s was dominated by CORBA, used mainly for enterprise applications. CORBA’s biggest criticism seems to be the quality of the implementations and the overly complicated standard. Java RMI also happened, but as it was mainly restricted to the Java ecosystem, it kind of defeated the point of many RPC solutions: cross language support.

RPC Timeline

With the rise of ‘the web’, CORBA/RMI became less fashionable, as everything had to be XML — this led to SOAP, XMLRPC and WSDL. These formats consisted of wrapping messages up in XML and POST’ing everything to a single HTTP path, and proved both hard for developers to interact with manually, and bad for things like HTTP caching. Then REST happened. Strictly, REST isn’t an RPC framework, more of a philosophy describing how to embed your API into HTTP. I feel like REST was a backlash against the heavy, verbose SOAP & WSDL. REST also espoused qualities such as cache “compatibility” and plain-text developer friendliness.

Simultaneously, Google developed their own RPC system internally called Stubby. Google released the wire encoding for this (Protocol buffers) sometime in 2008. Around the same time some engineers from Facebook wrote an open source RPC library heavily inspired by Stubby, called Thrift. Thrift is now part of the Apache project, and was at various times used by everything in the Java/Big Data community (like Cassandra, Hadoop etc). Thrift is cross languages, but suffered again from poor quality implementations in the non-major languages it supported.

Stubby was too tied to internal Google systems to be opensourced, plus Google was sensitive to the drawbacks of Thrift, RMI etc (all binary wire protocols). So Google made gRPC — concise, performant and simple. gRPC was started by Google in March 2015 and accepted into the CNCF (home of Kubernetes and Prometheus) in March 2017.

HTTP/2

We’ve discussed how gRPC encodes its requests and responses using Protocol Buffers, but we’ve not mentioned how it transports messages over the wire. gRPC uses HTTP/2 as the transport for the messages; HTTP/2 is itself heavily inspired by Google’s SPDY.

A lot has been written about HTTP/2, so I’m not going to say much here. gRPC’s use of HTTP/2 has a series of benefits:

  • Compressed headers reduce per request overheads
  • True multiplexing of streams on a single connection reduces per-connection overheads such as setting up SSL and head-of-line block.
  • Enables one of gRPCs defining features: full bidirectional streaming.

Language Support

There are three core implementations of gRPC: one in C, one in Golang and one in Java. The C implementation is used to support a variety of languages: C++, C#, Python, Ruby, PHP, Obj-C etc. The Golang implementation uses stdlib for SSL, whilst Java uses Netty and BoringSSL.

Although gRPC supports fewer languages than Thrift (~25 vs 10), gRPC has so far managed to avoid some of the problems Thrift had with poor quality language support. gRPC’s generate code tend to be very idiomatic, making it feel like you are using code native to your chosen language, and not transliterated Java code.

gRPC Languages

Conclusion

The code from this talk, with examples of a gRPC client/server and the same using HTTP/JSON can be found on Github: https://github.com/tomwilkie/grpc-example

This blog covers the first half of my talk, an introduction to gRPC and a simple, contrived example. You’ll probably have noticed I haven’t mentioned Kubernetes yet? In Part II of this write up I’ll discuss how we used gRPC to build our microservices architecture on Kubernetes. We’ll cover native gRPC/Kubernetes load balancing, HTTP tunnelling, middleware and monitoring, before moving on to some more advanced topics I did not get a chance to cover in the actual talk.

Kausal's mission is to enable software developers to better understand the behaviour of their code. We combine Prometheus monitoring, log aggregation and OpenTracing-compatible distributed tracing into a hosted observability service for developers.
Contact us to get started.