Gateway API Power for LiteSpeed Ingress Controller
The LiteSpeed Ingress Controller expands the very powerful LiteSpeed WebADC engine into the Kubernetes cluster space for load-balancing. Introducing new Gateway API power for LiteSpeed Ingress Controller.
The LiteSpeed documentation introduces the features available in the Gateway API. The Gateway API introduction focuses on the role-oriented nature of the new features and these are very useful. This blog will go further and show you some of the advanced features available using the Gateway API which were previously available but only as proprietary extensions.
The Gateway API now standardizes some of the more complex needs many customers have in their Kubernetes environments. In particular red-blue and canary deployments. These features have been available using the LiteSpeed Ingress Controller for some time and this blog will show you a new way to implement them.
Red-Blue and Canary Deployments
As described in the Advanced Deployments section of the LiteSpeed documentation, when you have a new image of software and want to try it out on a subset of users. Say, give it to 10% of your user population and see if there are problems. This is typically described as a “canary deployment.” You might want to increase the percentage until most of the population is using it, and then pass over control completely to the new group.
Or you may want to set up the deployment, and swap it over knowing you can swap it back quickly. That’s a “red-blue deployment.”
Also supported by Gateway is 100% header-based routing for the safest of canary deployments.
Red-blue deployment
Our first example will be to modify the Gateway example described in our documentation for olsup-gw.sh and we’ll create a red-blue deployment where you can switch between the old and new software quickly and easily. The modifications we’ll make below can be applied to the running pods and will take effect when executed.
The first step would be to download and extract the samples. Note that the version number is in the file and we’d recommend taking the newest version. For the version 0.2.0:
$ wget https://github.com/litespeedtech/helm-chart/raw/main/helm-chart-sources/ls-k8s-webadc/samples/ls-k8s-webadc-0.2.0.tgz
$ tar xf ls-k8s-webadc-0.2.0.tgz
$ cd ls-k8s-webadc
And then bring up the sample gateway. We’ll be using it as a test base here.
$ ./olsup-gw.sh
In that example you have a Deployment, Service, Gateway and HTTPRoute. For the new version you wish to deploy, you’ll need a new Deployment and Service to make your new software available. Modeled on the examples/ols-backend.yaml
, the new Deployment definition is named new-backend.yaml
and looks like this:
apiVersion: apps/v1
kind: Deployment
metadata:
name: new-backend
spec:
selector:
matchLabels:
app: new-backend
replicas: 2
template:
metadata:
labels:
app: new-backend
spec:
containers:
- name: new-backend
image: k8s.gcr.io/echoserver:1.10
imagePullPolicy: Always
ports:
- containerPort: 8080
The only differences are the names which must change and the image itself, which would always change for a new version. For this example, we’re using an image which is completely different so that we can see when it uses the new image.
For the Service definition, create a new file named new-backend-svc.yaml
modeled after ols-backend-svc.yaml
:
apiVersion: v1
kind: Service
metadata:
name: new-backend-svc
spec:
selector:
app: new-backend
ports:
- port: 80
targetPort: 8080
name: new-backend-http
All that is changed are the names. Apply them so they’ll be available:
$ kubectl apply -f new-backend.yaml -n sandbox
$ kubectl apply -f new-backend-svc.yaml -n sandbox
Surprisingly, the Gateway definition does not have to change at all. Only the HTTPRoute needs to change. From the original definition, modify it to add a single line, weight: 100
and save it as httproute-red-weight-old.yaml
:
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
name: ols-gateway.com
spec:
parentRefs:
- name: ols-gateway.com
hostnames:
- ols-gateway.com
rules:
- matches:
- path:
value: /
type: PathPrefix
backendRefs:
- name: ols-backend-svc
port: 80
weight: 100
You can then immediately apply it. It will have no affect because weight is not based on a percentage or any other numbering system and defaults to 1. However, most examples use the percentage based assumption for simplicity and compatability.
$ kubectl apply -f httproute-red-weight-old.yaml -n sandbox
As described in the gateway doc, you can test it using curl. kubectl get gateway -n sandbox ols-gateway.com
will return the address. You can then use it with curl:
$ curl https://ADDRESS/ -H 'Host: ols-gateway.com' -k
This should return the LiteSpeed server default screen’s HTML.
To begin the new “blue” deployment, create a new definition and save it as httproute-blue-weight-new.yaml
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
name: new-gateway.com
spec:
parentRefs:
- name: ols-gateway.com
hostnames:
- ols-gateway.com
rules:
- matches:
- path:
value: /
type: PathPrefix
backendRefs:
- name: new-backend-svc
port: 80
weight: 100
Because it uses the existing gateway parentRefs name, we use the existing name and the existing hostname. However, we use the service name we created and assign a weight of 100 to represent that it gets 100% of the traffic. When you are ready to give it a try, you can apply it:
$ kubectl apply -f httproute-blue-weight-new.yaml -n sandbox
For the moment, both the old and new software will see traffic. To turn traffic off on the old site, copy the httproute-red-weight-old.yaml
file to httproute-blue-weight-old.yaml
and edit it, changing the weight to 0:
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
name: ols-gateway.com
spec:
parentRefs:
- name: ols-gateway.com
hostnames:
- ols-gateway.com
rules:
- matches:
- path:
value: /
type: PathPrefix
backendRefs:
- name: ols-backend-svc
port: 80
weight: 0
And apply it:
$ kubectl apply -f httproute-blue-weight-old.yaml -n sandbox
If you again run the same curl you ran above, you can see the change in the output to a listing of the HTTP header. Keeping the files around gives you a quick and easy way to switch between old and new software.
To remove all that you did you can reverse the entire process, with the final step being to take down the sample gateway:
$ kubectl delete -f httproute-blue-weight-old.yaml -n sandbox
$ kubectl delete -f httproute-blue-weight-new.yaml -n sandbox
$ kubectl delete -f new-backend-svc.yaml -n sandbox
$ kubectl delete -f new-backend.yaml -n sandbox
$ ./olsdown-gw.sh
Canary testing with headers
The HTTP protocol includes easily modified request headers which allow a tester to reach the new software without giving the regular browser user access to it. Gateway allows you to match on received headers and query strings which gives you a choice of which method to test with. Query strings can be modified in a brower and thus make some sense for testing there, but since many applications count on the contents of a query string to be specifically defined, this example will use headers which are easily manipulated with curl. Note that using query strings is no more complicated than the method using headers here.
Either method gives developers and testers a way to deploy into a live environment without worrying about affecting production users since users will not generally have access to these mechanisms. As a last step in development before final deployment, it can be important as it gives you assurance that you can survive live internet attacks, access to real databases, performance verification, load testing and everything that the real environment provides.
We’ll be using the LiteSpeed Gateway samples and the deployment and service we created above.
As above, start by running the olsup-gw.sh
script and apply the deployment and service we created above.
$ ./olsup-gw.sh
$ kubectl apply -f new-backend.yaml -n sandbox
$ kubectl apply -f new-backend-svc.yaml -n sandbox
Also as above, the Gateway definition does not have to change at all. Even the HTTPRoute does not need to change as the absence of the new header will not affect traffic to the old backend.
To begin a canary deployment, create a new canary definition and save it as httproute-canary-hdr.yaml
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
name: new-gateway.com
spec:
parentRefs:
- name: ols-gateway.com
hostnames:
- ols-gateway.com
rules:
- matches:
- headers:
- name: canary
value: new
path:
value: /
type: PathPrefix
backendRefs:
- name: new-backend-svc
port: 80
Because it uses the existing gateway parentRefs name, we use the existing name and the existing hostname. However, we use the service name we created and assign a header canary:new
. When you are ready to give it a try, you can apply it:
$ kubectl apply -f httproute-canary-hdr.yaml -n sandbox
Test access to the old software using the old access technique with curl:
$ curl https://ADDRESS/ -H 'Host: ols-gateway.com' -k
Test access to the new software by adding the required header:
$ curl https://ADDRESS/ -H 'Host: ols-gateway.com' -k -H 'canary:new'
To remove all that you did you can reverse the entire process, with the final step being to take down the sample gateway:
$ kubectl delete -f httproute-canary-hdr.yaml -n sandbox
$ kubectl delete -f new-backend-svc.yaml -n sandbox
$ kubectl delete -f new-backend.yaml -n sandbox
$ ./olsdown-gw.sh
Canary deployment 90%-10%
Our final example will be to modify the Gateway example described in our documentation for olsup-gw.sh and we’ll create a canary deployment where 90% of the traffic goes to the old software and 10% to the new. We’ll be using the LiteSpeed Gateway samples and the deployment and service we created above.
As above start by running the olsup-gw.sh
script and apply the deployment and service we created above.
$ ./olsup-gw.sh
$ kubectl apply -f new-backend.yaml -n sandbox
$ kubectl apply -f new-backend-svc.yaml -n sandbox
As above, the Gateway definition does not have to change at all. Only the HTTPRoute needs to change. From the original definition, modify it to add a single line, weight: 90
and save it as httproute-canary-weight-old.yaml
:
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
name: ols-gateway.com
spec:
parentRefs:
- name: ols-gateway.com
hostnames:
- ols-gateway.com
rules:
- matches:
- path:
value: /
type: PathPrefix
backendRefs:
- name: ols-backend-svc
port: 80
weight: 90
You can then immediately apply it. It will have no affect because weight is not based on a percentage or any other numbering system and defaults to 1. However, most examples use the percentage based assumption for simplicity and compatability.
$ kubectl apply -f httproute-canary-weight-old.yaml
As described in the gateway doc, you can test it using curl. kubectl get gateway -n sandbox ols-gateway.com
will return the address. You can then use it with curl:
$ curl http://ADDRESS/ -H 'Host: ols-gateway.com'
This should return the LiteSpeed server default header.
To begin a canary deployment, create a new canary definition and save it as httproute-canary-weight-new.yaml
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
name: new-gateway.com
spec:
parentRefs:
- name: ols-gateway.com
hostnames:
- ols-gateway.com
rules:
- matches:
- path:
value: /
type: PathPrefix
backendRefs:
- name: new-backend-svc
port: 80
weight: 10
Because it uses the existing gateway parentRefs name, we use the existing name and the existing hostname. However, we use the service name we created and assign a weight of 10 to represent that it gets 10% of the traffic. When you are ready to give it a try, you can apply it:
kubectl apply -f httproute-canary-weight-new.yaml -n sandbox
Because of caching there’s no good way to test it. When a curl is done from the same machine, you’ll get the same entry you get the first time. Take our word for the fact that 10% of the traffic will be sent to the canary backend.
To remove all that you did you can reverse the entire process, with the final step being to take down the sample gateway:
$ kubectl delete -f httproute-canary-weight-old.yaml -n sandbox
$ kubectl delete -f httproute-canary-weight-new.yaml -n sandbox
$ kubectl delete -f new-backend-svc.yaml -n sandbox
$ kubectl delete -f new-backend.yaml -n sandbox
$ ./olsdown-gw.sh
Just scratching the surface
All of these techniques just scratch the surface of what you can do with gateway. We’ve demonstrated switching between old and new dynamically, using a header and using weights. As mentioned above you can also use query strings. You can also select on host name using wildcards (better than those for Ingress), and multiple levels of path, headers and query strings. You can add, set or delete headers to allow control of your backends. There are a whole set of role features. Gateway adds a whole new world to Kubernetes.
Comments