Skip to content

Understanding Ingress


Generally, Kubernetes Service NodePort exposes service within an organization or a particular boundary. The external load balancer can also be configured to expose the service out on the internet using type=LoadBalancer in cloud infrastructure but this approach has one significant downside — you need to scale and rolling out one load balancer for one service is expensive plus it adds maintenance cost.

Kubernetes Ingress indends to solve this problem by routing HTTP/HTTPS traffic to appropriate service using host based, path based routing and so on.

The History

Before Ingress, K8s Service was go to way for deploying app in Kubernetes world because most applications were monolith which on deployment requires one load balancer to setup and configure with K8s Service can provide easily. You pay for one load balancer and maintain one load balancer.

    flowchart TD
    A([👤 User]) --> B{{"LoadBalancer (Cloud)"}} --> C[Monolith Service]
  

But, cloud platforms were emerging and organizations started moving towards cloud and microservices architecture for managed and cost effective solution or whatever might be the reason for the organization to choos the architecture. So, the entire application becomes “SUM of Services”. In a microservice architecture, it is not uncommon for an application to have:

  • Login service is at exmaple.com/login
  • User service is at exmaple.com/user
  • Cart service is at example.com/cart
  • Payment service is at example.com/checkout
  • and dozens of others

But to deploy or expose the application in Kubernetes each Service requires one dedicated load balancer because there was no other way in K8s.

What should have been a simple architecture like:

    flowchart TD
    U(["👤 User"]) --> LB{{"Load Balancer"}}
    LB --> S1["Service 1"]
    LB --> S2["Service 2"]
    LB --> S3["Service 3"]
  

Suddenly becomes:

    flowchart TD
    U(["👤 User"]) --> LB{{"Load Balancer"}}
    LB --> LB1{{"LoadBalancer1"}}
    LB --> LB2{{"LoadBalancer2"}}
    LB --> LB3{{"LoadBalancer3"}}
    LB1 --> S1["Service 1"]
    LB2 --> S2["Service 2"]
    LB3 --> S3["Service 3"]
  

The problem is less visible at user’s end but still latency can spike as more hops are involved. The problem was more at management side or for organization. One thing needed to be understood is that the problem was not load balancer themselves but K8s Service requiring dedicated load balancer instead was the problem:

  • As number of microservices grows, number of load balancers grows
  • You have to pay for each load balancer and it’s public IP and other costs
  • You have to manage all those load balancers
  • You can’t even write all the supported features of Load Balancer in Service spec so you should configure in load balancer seperately whenever those features required

Emerge of Ingress

The solution might have been hard to build for Kubernetes but it was easy to think or is easy to think now. It’s straightforward: “It would have been great if we could route traffic to each Service from one place — one load balancer”. And indeed after that K8s introduced Ingress but even before that there was a problem to solve.

Kubernetes could create a new resource namely Ingress but in order to integrate with all popular load balancing solutions that time such as: NGINX, AWS ALB, HA Proxy, L5, Envoy and others — it was practically impossible for K8s to code that integration (which K8s calls as Ingress Controller) for all those providers. Community should grow together. Hence, Kubernetes came up with a proposal:

Hey K8s stakeholders! We understood limitations of K8s Service and we are purposing a solution. But you people also have a job to do. At our end, we will introduce something called ‘Ingress Resource’ where you could write sepecifications to perform path/host based routing, TLS offloading, etc. but you guys should write code as ‘Ingress Controller’ in order to integrate these specifications with your load balancer. Let’s grow together.

Important

With the above proposal what you need to understand is that ‘Ingress Resource or Manifest’ is a no-op (has no effect) without ‘Ingress Controller’.

The General Workflow

Ingress Controller is not natively available in K8s Cluster, it needed to be installed depending which Ingress Controller you are going to use such as AWS Ingress Controller, etc. The Ingress Controller watches for the Ingress Manifest and essentially allocates a proxy or load balancer which can then perform host/path based routing and other things. So, the major focus is on:

  • Choosing which Ingress Controller to use
  • Installing Ingress Controllor
  • Writing Ingress Manifest for that Ingress Controller

And rest of the things Ingress Controller does by itself.

    flowchart TD
    User(["👤 User"])
    Ingress["Ingress Manifest"]
    Controller["Ingress Controller"]
    LB{{"Proxy / Load Balancer"}}
    Service1["Login Service"]
    Service2["Payment Service"]

    Controller -. Watches .-> Ingress
    Controller -->|Configures / Allocates| LB
    User -->|HTTP / HTTPS| LB
    LB -- example.com/login --> Service1
    LB -- example.com/checkout --> Service2
  

Flow:

  • The Ingress Controller watches for Ingress manifests.
  • When an Ingress resource is created, the controller configures or provisions the proxy/load balancer, depending on the implementation.
  • The user accesses the application through the load balancer, which routes requests to the appropriate Kubernetes Service.

Note

An Ingress Controller doesn’t always allocate a new load balancer for each Ingress manifest. It often configures a shared proxy or load balancer to handle multiple Ingress resources.

Important Considerations

  • Ingress is no longer being developed and Kubernetes recommends using Gateway API instead. This doesn’t mean you can’t use Ingress and is no longer used. Ingress has some limitations which we will explore later.

  • Ingress doesn’t expose arbitary protocols. In K8s words:

    An Ingress does not expose arbitrary ports or protocols. Exposing services other than HTTP and HTTPS to the internet typically uses a service of type Service.Type=NodePort or Service.Type=LoadBalancer.

Last updated on