Why do we need an Ingress for Kubernetes?

Ingress is one of the key concepts in K8s that you need to master if you really want to understand how the traffic flows in and out of your cluster. For us, Mulesoft Architects and Developers, it’s also important to understand it, as it is a key element of a Runtime Fabric deployment.

In this post, we will try to understand the basic concepts of Ingress to better design our architectures with Runtime Fabric.
But before we explain how it works let’s answer this question - Why do we need an Ingress?


Understanding how K8s exposes our Mule applications

When we deploy a Mule app, this app is containerized. The Runtime Fabric creates an image for your application along with the mule runtime. This image is used to deploy your app as a container in one or more replicas. For that, RTF creates a Deployment. The Deployment will control the number of replicas, the persistence, the resources, etc (not the topic today). 

After that, Runtime Fabric will create a K8s Service, which creates an abstraction layer on top of our replicas/deployment. It basically creates one or more endpoints - these endpoints are the entry points of your apps. The pods/replicas are never exposed directly, we don't access the pods via their IP or hostname. This allows K8s to remove and recreate pods easily for your app without having to change these endpoints.

There are different types of Services in K8s. The most basic types of Services in K8s (there are more) are ClusterIP and NodePort:
  • ClusterIP: It provides an internal IP and hostname within the K8s cluster, which can only be accessed from within the cluster
  • NodePort: It exposes the service on a static port on the worker node of the cluster. This creates a mapping between a port in the node and a ClusterIP service. This way a request to http://workernode_hostname:nodeport will be redirected to http://clusterIP:targetPort. So, for each application we want to expose externally we'd create a NodePort service so that all the requests that the worker node gets on that port number will be redirected to this service and then to the pods behind the service. 


As you can imagine, using NodePort as a mechanism to expose multiple applications in our cluster is not a good solution, for several reasons:
  • First, because typically, if we're deploying mule apps for example, we don't want to use a different port for each application. We'd require only ports 80 and 443
  • Second, because this static mapping of ports is not scalable
A solution for this could be to use a proxy in front of your nodes. This way, our end user can use port 80 externally and the proxy would redirect the request to the NodePort port number. We'd also need to create a DNS record to point to our proxy server.

If our K8s is hosted in a cloud provider (AWS, Azure, GCP...) that approach can be automated using another type of K8s service: a LoadBalancer service. This type of service will do everything you do with a NodePort service and, in addition, it will create a request to the cloud provider to provision a load balancer. For example, in AWS, a LoadBalancer service will create a Network Load Balancer that will listen to ports 80 and/or 443 and redirect the request to a port in your K8s nodes.


But again, this is not enough. If our company grows and we deploy and expose more apps and services, we'd need to create more and more load balancers if we follow this approach. That means two things:
  • One, we'd have a different DNS record for each load balancer, that is, for each app. Obviously, we'd like a unique hostname for all of our services exposed. Something like http://mycomany/app1, http://mycomany/app2...
  • Each load balancer has a cost in our cloud provider
To work around this, we could think of an extra proxy or load balancer that uses a unique DNS record and redirects traffic based on the URL, and put this new proxy on top of our Load Balancer.
The problem is that every time we create a new service we'd need to repeat all the steps above. This is too much configuration and complexity for each service to be deployed and maintained.

Is there not a better solution?

This is when the concept of ingress comes in, as a way to manage all of these configurations within the K8s cluster and have all of that configuration within yaml definition files along with the rest of the app files.

Ingress helps us to provide the external users outside the cluster with a single externally accessible URL that we can then configure to route to different services within our cluster based on the URL path. It also helps us to implement SSL. 

Think of ingress as a layer 7 load balancer built into the K8s cluster that can be configured and managed using native K8s primitives just like any other object in K8s. But remember, even with Ingress, you still need to expose it to make it accessible outside the cluster. So you’ll still need to expose the ingress as a NodePort or as a LoadBalancer in your cloud provider, but this is just a one-time configuration.

Once you do that one-time config, you’re going to perform all your load balancing, authentication, SSL and URL-based routing within the K8s cluster (in the ingress controller). Without ingress in K8s, we would use a proxy server or load balancing solution such as NGINX, HAPROXY or TRAEFIK. The idea of an ingress controller is to provison those solutions within the cluster as it was another application running in our cluster.

Lastly, be aware that, a K8s cluster does not come with an ingress controller by default. K8s distros do not include ingress. So, if you create ingress resources without deploying an ingress controller, it won’t work.


How to install an Ingress

Installing an ingress controller is not just using an image of a proxy like NGINX or TRAEFIK and deploying it to a pod. An ingress controller will require more K8s resources. Let's see the example of NGINX:
  • The core of the solution, what we typically call Ingress Controller, is a Deployment that runs the nginx image and provides all the nginx functionalities as proxy server. The deployment does not use an image of the NGINX solution we'd use for a web server. This image is a specific one to be run in K8s. 
  • It has additional logic, as this controller would be constantly monitoring the creation of ingress resources.
  • NGINX uses also Config Maps for the specific configuration of NGINX - path logs, keep alive thresholds, SSL settings, session timeouts, etc.
  • The ingress controller has additional built-in capabilities to monitor the K8s cluster for ingress resources and configure the underlying NGINX server when sth changes. But for that, the ingress controller requires a service account with specific permissions. That’s why we need to create a service account in K8s with the correct roles and roleBindings.
  • And finally, the ingress controller will be exposed with a K8s service, typically a LoadBalancer service. This would be the unique entry point for all your K8s apps. With that, the flow would be External Client > Load Balancer > Ingress Controller (service) > Service > Pods
  • All of these resources are normally created on a separate namespace to keep them protected and isolated from the rest of the workload in the cluster. If you want to know exactly all the resources and their configuration you can have a look at this URL:

https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.1.0/deploy/static/provider/aws/deploy.yaml


Previous Post Next Post