Using Ingress Controller in Kubernetes (part II)
This is the second part in the Kubernetes Ingress series. Please refer to the first one for the basic setup and the info about Ingresses in Kubernetes.
Today we’re going to talk about Multiple Services and how to handle HTTPS traffic with Ingress. Let’s start with Multiple Services:
Multiple Services
Let’s create a second app, it’s basically the same NodeJS code but with a slightly different output:
const http = require('http');
const os = require('os');
console.log("Gordon Server is starting...");
var handler = function(request, response) {
console.log("Received request from " + request.connection.remoteAddress);
response.writeHead(200);
response.end("Hey, I'm the next version of gordon; my name is " + os.hostname() + "\n");
};
var www = http.createServer(handler);
www.listen(8080);
Save it as app-v2.js
and create the following Dockerfile
:
FROM node:10.5-slim
ADD app-v2.js /app-v2.js
ENTRYPOINT ["node", "app-v2.js"]
Build the Docker Image via:
$ docker build -t <your_name>/gordon:v2.0
Push it to Docker Hub or to your favorite docker registry (don’t forget to tag it accordingly!).
Now, let’s create a second ReplicationController and a Service:
$ cat <<EOF | kubectl create -f -
apiVersion: v1
kind: ReplicationController
metadata:
name: gordon-v2
spec:
replicas: 3
template:
metadata:
name: gordon-v2
labels:
app: gordon-v2
spec:
containers:
- image: evalle/gordon:v2.0
name: nodejs
imagePullPolicy: Always
---
apiVersion: v1
kind: Service
metadata:
name: gordon-service-v2
spec:
selector:
app: gordon-v2
ports:
- port: 90
targetPort: 8080
EOF
Don’t forget to change the image
in this YAML file to your own docker image!
As you can see our second service is using port 90.
Let’s check if our services are up and running:
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
gordon-service-v1 ClusterIP 10.108.177.42 <none> 80/TCP 1h
gordon-service-v2 ClusterIP 10.105.110.160 <none> 90/TCP 1h
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 4d
Great, now to the ingress:
$ cat <<EOF | kubectl create -f -
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: gordon
spec:
rules:
# this ingress maps the gordon.example.com domain name to our service
# you have to add example.com into DNS
# or you can just add it to /etc/hosts for testing purposes
- host: gordon.example.com
http:
paths:
- path: /v1
backend:
serviceName: gordon-service-v1
servicePort: 80
- path: /v2
backend:
serviceName: gordon-service-v2
servicePort: 90
EOF
The first service should be accesible via path /v1
and the second service -
via path /v2
Let’s test it:
$ for i in {1..5}; do curl http://gordon.example.com/v1; done
You've hit gordon v1, my name is gordon-v1-btkvr
You've hit gordon v1, my name is gordon-v1-64hhw
You've hit gordon v1, my name is gordon-v1-z99d6
You've hit gordon v1, my name is gordon-v1-btkvr
You've hit gordon v1, my name is gordon-v1-64hhw
$ for i in {1..5}; do curl http://gordon.example.com/v2; done
Hey, I'm the next version of gordon; my name is gordon-v2-g6pll
Hey, I'm the next version of gordon; my name is gordon-v2-c78bh
Hey, I'm the next version of gordon; my name is gordon-v2-jn25s
Hey, I'm the next version of gordon; my name is gordon-v2-g6pll
Hey, I'm the next version of gordon; my name is gordon-v2-c78bh
As you can see, our Ingress setup for multiple services works!
OK, and what about multiple hosts?
Multiple hosts
We can map different service to different hosts. Let’s delete the previous version of our ingress first:
$ kubectl delete ing gordon
Here is how the multiple hosts setup should look like:
$ cat <<EOF | kubectl create -f -
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: gordon
spec:
rules:
# this ingress maps the gordon.example.com and example.gordon.com domains to our services
# you have to add those names into DNS
# or you can just add it to /etc/hosts for testing purposes
- host: gordon.example.com
http:
paths:
# all requests will be sent to port 80 of the gordon-nodeport service
- path: /v1
backend:
serviceName: gordon-service-v1
servicePort: 80
- host: example.gordon.com
http:
paths:
- path: /v2
backend:
serviceName: gordon-service-v2
servicePort: 90
EOF
The service gordon-service-v1
should be accessible via http://gordon.example.com/v1
and the
service gordon-service-v2
- via https://example.gordon.com/v2
.
Again, don’t forget to add the name example.gordon.com to /etc/hosts
file:
$ echo `192.168.64.8 example.gordon.com` >> /etc/hosts
Let’s test it:
$ curl http://gordon.example.com/v1
You've hit gordon v1, my name is gordon-v1-7jj4n
$ curl http://example.gordon.com/v2
Hey, I'm the next version of gordon; my name is gordon-v2-s6tml
Works like a charm!
Let’s clean it up again:
$ kubectl delete ing gordon
and we can move on to HTTPS section.
Handling HTTPS traffic with Ingress
Currently, the Ingress can handle incoming HTTPS connections, but it terminates the TLS connection and sends requests to the services unencrypted. Since the Ingress terminates the TLS connection, it needs a TLS certificate and private key to do that. The two need to be stored in a Kubernetes resource called a Secret.
Create a certificate, a key and save them into Kubernetes Secret:
$ openssl genrsa -out tls.key 2048
$ openssl req -new -x509 -key tls.key -out tls.cert -days 360 -subj '/CN=gordon.example.com'
$ kubectl create secret tls tls-secret --cert=tls.cert --key=tls.key
secret "tls-secret" created
Now let’s create our new ingress:
$ cat <<EOF | kubectl create -f -
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: gordon
spec:
tls:
- hosts:
- gordon.example.com
secretName: tls-secret
rules:
- host: gordon.example.com
http:
paths:
- path: /v1
backend:
serviceName: gordon-service-v1
servicePort: 80
- path: /v2
backend:
serviceName: gordon-service-v2
servicePort: 90
EOF
And let’s run some tests:
$ curl http://gordon.example.com/v1
<html>
<head><title>308 Permanent Redirect</title></head>
<body bgcolor="white">
<center><h1>308 Permanent Redirect</h1></center>
<hr><center>nginx</center>
</body>
</html>
$ curl -k https://gordon.example.com/v1
You've hit gordon v1, my name is gordon-v1-64hhw
Note that In case of HTTPS setup nginx-ingress listening on port 443.
Outro
So, here you go, we learned how to setup Ingress for Multiple Services, Multiple Hosts (of course, you can combine those two methods for some complex setups) and how to handle HTTPS traffic with Ingress controller.
Useful Links
- “Kubernetes in Action” - by Marko Lukša - one of my favorite books about Kubernetes