Kubernetes API: Client-side Load Balancer
Server-Side Load Balancing
The intuitive way to handle load balancing in Kubernetes is to leverage your infrastructure and setup an Ingress
that pipes to a backend Service
.
For example, the Ingress
below terminates SSL traffic on port 80
and then dispatches to the Service
pool blog-frontend
which may contain any number of Pods
listing on port 3000
.
ingress.yml
# this is the top level ingress point for the cluster DNS points *.eggie5.com here
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: eggie5-blog
annotations:
kubernetes.io/ingress.global-static-ip-name: "com-eggie5-production-ingress"
spec:
tls:
- secretName: tls
backend:
serviceName: blog-frontend
servicePort: 80
frontend-service.yml
apiVersion: v1
kind: Service
metadata:
name: blog-frontend
labels:
name: web
spec:
type: NodePort
ports:
# The port that this service should serve on.
- port: 80
targetPort: 3000
protocol: TCP
# Label keys and values that must match in order to receive traffic for this service.
selector:
name: web
However you can move this work from Kubernetes/cloud-provider to the client-side, or in other words, setup the load-balancer yourself. The difficult issue here isn’t setting up the load balancer, we can easily configure nginx for that purpose. The real difficulty, being in a distributed environment, is discovery: how do we know what hosts are in our cluster and how to address them. Discovery something that Kubernetes does best. Maybe we can leverage Kubernetes to build our load balancer.
Client-side Load Balancing
We can leverage Kubernetes API in our load balancer to find what hosts are available and where they are located.
Kelsey Hightower recently published a simple example of how to do this in Kubernetes:
The Kubernetes API makes it easy to do client-side load balancing. The Endpoints Load Balancer is a great example. https://t.co/jPL4qz7sMQ pic.twitter.com/oJxTbk7Wzx
— Kelsey Hightower (@kelseyhightower) November 6, 2016
He builds a package (in go) called endpoints.go
, which encapsulates the functionality of round-robin endpoint selection and synchronizes the list of endpoints w/ Kubernetes API.
Your implementation would then use the endpoints package with namespace
and service
arguments to get the next available endpoint within that set of endpoints (per round-robin) and then forward the request respectively. This would be packaged up as a Docker container and then run as a Pod
in Kubernetes.
Here’s an example implementation from Kelsey’s documentation:
Load balancers are created using a Config:
config := &endpoints.Config{
Namespace: namespace,
Service: service,
}
lb := endpoints.New(config)
Before an endpoints load balancer can be used endpoints must
be synchronized from the Kubernetes API:
err := lb.SyncEndpoints()
Once the initial set of endpoints have been populated clients
can call the Next method to get an endpoint:
endpoint, err := lb.Next()
// ...
url := fmt.Sprintf("http://%s:%s", endpoint.Host, endpoint.Port)
resp, err := c.Get(url)
// ...
Endpoints can be synchronized in the background:
err := lb.StartBackgroundSync()
Shutdown terminates a load balancer instance by stopping any
background watches and reconciliation loops against the Kubernetes
API server:
lb.Shutdown()
This was a good introduction to the Kubernetes API and powerful example of the flexibility that Kubernetes offers. The full example from Kelsey Hightower is here: https://github.com/kelseyhightower/endpoints
Appendix
Kubernetes API
The endpoints
module gets a list of endpoints in our cluster by using the GET /api/v1/namespaces/{namespace}/endpoints/{name}
API, where namespace
and name
are the respective Namespace
and Service
the contain Pod
s for which you want the LB to discover.
Permalink: kubernetes-api
Tags: