This is Part 1 of the multi-part series on Exposing Kubernetes Services via Ingress. You can find the link to Part 2 here.

Ingress in Kubernetes offers us an easy way to route traffic from one point to another without hassle. In most cases, simply adding the ingress component and pointing it to the service backend works.

There are different Ingress controllers for Kubernetes like Nginx, Contour, and HAProxy, which match incoming traffic against the rules provided by YAML Manifest files that the user deploys into the cluster.

Using Ingress, we can effectively use a single external network Load Balancer and let the Ingress handle the routing between different services and everything else in between.

Pre-requisites

You'll need the following to work along with this article:

  • Cloud-Based Kubernetes Cluster (EKS, DOKS, AKS, GKE, etc.)
  • Domain Name

Working with Contour Ingress Controller

In this article, we'll use the Contour Ingress Controller. Contour works nicely with HTTP/HTTPS-based and WebSocket based applications, so it's a great choice for clusters requiring ingress for both types of applications.

Ingress Controllers like Nginx Ingress Controller and HAProxy Ingress Controller also work with WebSockets but are prone to dropping traffic when they reload. Reloads occur when the ingress configuration is changed. As the ingress controller will be used for different services, keeping traffic flowing without reloads will not be possible.

Contour Ingress Controller solves this issue by using Envoy Proxy as the routing backend. Envoy works well with WebSocket traffic and ensures flawless network information delivery to and fro.

More on how the Contour Ingress Controller works is explained below.

Installing and Configuring Contour Ingress Controller

To work with Contour, we'll need a domain name to point to the Public IP provided to our Load Balancer. I've already set up the domain to point to my Load Balancer IP. Please do so before proceeding further.

The steps to configure are:

  1. Create a wildcard subdomain entry for the domain you wish to use
  2. Point it to the IP of Contours Service, which is the actual IP of our LoadBalancer
kubectl get svc -n projectcontour                                               
NAME      TYPE           CLUSTER-IP       EXTERNAL-IP    PORT(S)                      AGE
contour   ClusterIP      10.245.147.133   <none>         8001/TCP                     26d
envoy     LoadBalancer   10.245.169.78    192.168.22.9    80:30217/TCP,443:31782/TCP   26d
The Envoy Service has been provided with a Public IP. We'll point a wildcard domain to that IP.
ℹ️
A Non-Wildcard domain can also be used for our ingress. Using a wildcard domain enables us to work with different services served over different sub-domain names.

Setting up a Backend Service

Ingress works by routing the requests to a service backend. The service backend always listens for traffic at a particular port in the cluster.

Let's create a simple deployment. We'll use a web server that displays a pre-configured web page for demo purposes. We'll go with httpd on this one, but you are free to choose any web server you prefer, as the connection through ingress works the same way for all.

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: httpd
  name: httpd
spec:
  replicas: 1
  selector:
    matchLabels:
      app: httpd
  template:
    metadata:
      labels:
        app: httpd
    spec:
      restartPolicy: Always
      containers:
      - image: httpd
        name: httpd
        imagePullPolicy: Always
        ports:
        - containerPort: 80
          protocol: TCP
        resources:
          requests:
              cpu: "10m"
          limits:
              cpu: "100m"
---
apiVersion: v1
kind: Service
metadata:
    name: httpd-service
spec:
  selector:
    app: httpd
  type: ClusterIP
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
HTTPd WebServer Deployment

Now we can start configuring ingress to point to our backend.

#An example of a host-based ingress routing!
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: contour
  name: httpd
  #Ingress Namespace
  namespace: default
spec:
  rules:
  #Provide name of domain here
  - host: httpd.wildcard.example.com
    http:
      paths:
      - backend:
          service:
          	#Provide your service name here
            name: httpd-service
            #Provide your servie port here
            port:
              number: 80
        path: /
        pathType: Prefix
Ingress Manifest

Ingress needs to be created in the namespace where your deployment is. It's impossible to configure ingress to serve traffic to and from service in another namespace. It can be possible after some tweaking to Contour, but that's not in the scope of this article.

✍️
If you have a wildcard domain entry, then any subdomain will be resolved to the provided IP address of the Load Balancer.

How It Looks

Once the ingress manifest is applied, we can query kubeapi for ingresses created in a particular namespace. Since I'm in the default namespace, I'll run the following command to fetch the ingresses.

kubectl get ingress -n default

NAME    CLASS    HOSTS                       ADDRESS        PORTS   AGE
httpd   <none>   httpd.wildcard.example.com   192.168.22.9   80      5d2h
Ingress Created in Cluster

Opening it in the browser shows a page like this.

Doing curl shows that the request is being served through Envoy Proxy.

curl http://httpd.wildcard.example.com

*   Trying 192.168.22.9:80...
* Connected to httpd.wildcard.example.com (192.168.22.9) port 80 (#0)
> GET / HTTP/1.1
> Host: httpd.wildcard.example.com
> User-Agent: curl/7.79.1
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< date: Wed, 24 Aug 2022 10:56:15 GMT
< server: envoy
< last-modified: Mon, 11 Jun 2007 18:53:14 GMT
< etag: "2d-432a5e4a73a80"
< accept-ranges: bytes
< content-length: 45
< content-type: text/html
< x-envoy-upstream-service-time: 3
< vary: Accept-Encoding
<
<html><body><h1>It works!</h1></body></html>
* Connection #0 to host httpd.wildcard.example.com left intact
Output while curling the domain, we can see that envoy is serving the requests back and forth.

How It Works

Contour Ingress Controller consists of two deployments; Contour and Envoy. Each deployment consists of two replicas by default. The Contour pods watch the Kubernetes Control Plane for ingress resource objects. Anytime a new resource is created, or an existing one is updated, Contour picks it up and updates its rules accordingly.

The ingress resources consist of traffic routing rules for the backend service we want to route requests to. Ingress can be used to perform host-based and path-based routing.

Example for Host-Based Routing:

Using Host-Based routing, we can route requests for abc.example.com to a backend service.

Example for Path-Based Routing:

Using Path-Based routing, we can route requests for example.com/abc to a backend service.

In both of the above cases, the service name can be anything we want, but it needs to be correctly specified in the ingress manifest for routing to work. Otherwise, we'll be provided with a completely blank page.

When Contour is installed in our cluster, it automatically provides a Load Balancer if you use a Kubernetes Cluster from a Cloud Service Provider.

ℹ️
On Minikube, I haven't been able to set up the Load Balancer to work using MetalLB. An IP is provided to the Envoy service, but requests don't get routed to the backend service we specify.

The provisioned Load Balancer will direct all external traffic to Contour Ingress Controller. This will instruct the Envoy Proxy to route traffic to different services according to the rules.

Conclusion

This article taught us to set up a Contour Ingress Controller to route and balance our requests to backend services in the Kubernetes cluster.

In this guide, you set up an Nginx Ingress to load balance and route external requests to backend services inside your Kubernetes cluster. You also secured the ingress by installing the cert-manager certificate provisioner and setting up a Let's Encrypt certificate for two host paths.

In the next part, we'll learn to secure our Ingress with SSL/TLS certificates through Let's Encrypt.

Please leave a comment if you have any questions. Thank you for reading.