Kubernetes Ingress: The "Fun" Way to Control Traffic

Looking back on when I started my journey in the Kubernetes ecosystem, one thing among many, that I didn't understand much was Kubernetes Ingress. This initial misunderstanding remained even today when I work with Kubernetes daily.

Yes, I don't know that much about Kubernetes Ingress. I think I cover the basics, but that is all. However, with this article, I want to change that! I want to tackle the topic of Kubernetes Ingress - what it is, how can be enabled and used, what to use it for, and so on.

I want to move from "Fun" to Fun!

The approach I'm going to take is this - I'm going to be reading the official Kubernetes documentation about it and trying to digest it for myself, and hopefully for many of you readers as well. This article however will not be a deployment or a how-to instruction, it will only explain as simply as possible, what each part is. So, let's begin.

What is Kubernetes Ingress?

In plain words - Kubernetes Ingress is just a group of rules that exposes routes outside the cluster to the services inside the cluster. These routes include only HTTP and HTTPS routes. By adding Ingress, you will not automatically have routing. Ingress only defines the rules.

The actual routing is done by Ingress Controller. The Ingress controller is the one responsible for fulfilling the Ingress.

And what is the Ingress controller? It is a specialized type of load balancer for Kubernetes. It is a type of controller, but the only difference between this and other controllers is that the Ingress controller is not run as a part of kube-controller-manager. That means that it is not started automatically with the cluster. You will need to choose a third-party provider for the Ingress controller.

The bottom line is this - by deploying Ingress into the Kubernetes cluster you will not get anything until you deploy the Ingress controller. One doesn't work without the other. The deployment itself will not fail, it just won't work, and that's it.

Below you can see an example diagram of what goes where.

ingress-diagram
Diagram of a client request going through Ingress-managed Load Balancer, Ingress, Service, and finishing up in Kubernetes Pod. Source

This part was so hard for me to understand in the beginning and I kept mixing the Ingress with Ingress controllers, load balancers, services, and so on. However, I hope my explanation above is good and you can get a grasp of it.

When to use it?

Ingress and Ingress controllers can be used whenever you want to expose some HTTP and/or HTTPS route outside of the cluster. If you have many services you want to expose - going in the Ingress and Ingress controller direction is a way to go. Add to that the always-terrifying SSL configuration, and this option is a no-brainer.

There is another option, a simpler one. It is, however, a bit more specific - it applies only if you run a Kubernetes cluster on a cloud provider.

If you run a GKE, AKS, or EKS cluster, and you want to expose only one application or test the exposure of it - you can use a Kubernetes Service instead. You just define Type: LoadBalancer within that Service, and voila. The cloud provider (if you set it up properly) will spin up the load balancer and it will tie it to your service. And you can access that service externally. Some of the cloud providers offer you to specify the IP address of the load balancer within the Service, but some of them don't offer that option. So, this option is simpler, but, as always, there is a trade-off.

What does the Ingress look like?

Deploying Ingress resource is quite easy - you just define it, apply it to the cluster, and that's it! But, that is not enough! You will need an Ingress controller to make it work. If you hate yourself, you can even have multiple Ingress controllers, but, we'll not do that here.

Following is just an example of what the Ingress looks like. I will go through and explain each part of the resource in specific.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: minimal-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx-example
  rules:
  - host: "foo.bar.com"
    http:
      paths:
      - path: /testpath
        pathType: Prefix
        backend:
          service:
            name: test
            port:
              number: 80

One paragraph explanation - each request that goes to the foo.bar.com/testpath URL will be rewritten to foo.bar.com and will be routed to the service test on Kubernetes, on port 80.

Drilling down the specifics

Now, to get into some of the "deets", what all this above actually means.

The first part of the above resource definition is the metadata. It contains general information about the resource itself. The important part is the annotations block. This specific annotation shows us the Ingress controller-specific configuration. In this case, it is an Nginx Ingress controller rewrite annotation. It defines the target URI where the traffic must be redirected. Whenever the foo.bar.com/testpath is received, it will be rewritten to foo.bar.com.

Important to note is that these annotations vary from one Ingress controller to another, so you need to be aware of them.

The next part is the spec block. The ingressClassName option defines an Ingress class to be used. And an Ingress class is yet another resource, which defines the controller that should implement the class and a controller-specific configuration. This field can be omitted, but it is recommended to always specify the ingressClassName because, in the scenario where we have multiple Ingress controllers, we might not be aware of what is the default one.

The spec.rules block defines the rules where the traffic will go. This specific rule shows us that everything that goes to the host foo.bar.com with the URL path /testpath will be routed to the service called test and to port 80 of that service.

The host field above is optional. It tells us to which host this rule applies. When the host field is not specified, it applies to all inbound HTTP traffic through the IP address specified by the Ingress controller.

One thing to mention here - for this to work you will need to have a DNS record that matches the domain foo.bar.com to the specific IP. This part is covered with the Ingress controller configuration, but I want to note it nevertheless.

The pathType: Prefix tells us that every request with /testpath in the URL will go to the defined backend. For example - http://foo.bar.com/testpath will go to the test backend, as well as http://foo.bar.com/testpath/hello. So everything under the /testpath will be matched.

If we don't want to match everything under the /testpath, we can use pathType: Exact option. It will only match if the request has that exact path specified. For example - http://foo.bar.com/testpath will go to the test backend, however, http://foo.bar.com/testpath/hello will not.

The last but not least, the backend block of the rules defines where the request will go. So, as mentioned and shown, it will go to the test service and port 80. Besides the service, it can be a specific resource backend.

And, a resource backend is a reference to another Kubernetes resource, within the same namespace as the Ingress object. In order for Ingress to work, we need to specify either a resource or a service backend. The former could be used if we want to ingress data to an object storage backend with some static assets.

A Resource backend is an ObjectRef to another Kubernetes resource within the same namespace as the Ingress object. A Resource is a mutually exclusive setting with the Service backend and will fail validation if both are specified. A common usage for a Resource backend is to ingress data to an object storage backend with static assets.

💡 Takeaways

This is it, at least for this week's article! I hope I didn't remove Fun! from the Kubernetes Ingress.

To wrap the story up, the following are some of the key takeaways.

  • The Ingress resource only provides a set of rules, and it will not work without an Ingress controller.
  • With annotations, you can provide different configuration parameters.
  • Use Ingress/Ingress controller when you need to access multiple services externally.
  • A valid DNS record needs to match the IP address of a cluster where everything is running.
  • You can route traffic to a Service, or some other Kubernetes resource specified with the Resource backend.

To find out more about Ingress, Ingress controllers, and everything else connecting them, check out the link to the official Kubernetes documentation below.

Ingress
Make your HTTP (or HTTPS) network service available using a protocol-aware configuration mechanism, that understands web concepts like URIs, hostnames, paths, and more. The Ingress concept lets you map traffic to different backends based on rules you define via the Kubernetes API.