Note : Everything here applies to Amazon Elastic Kubernetes Service (EKS) . If you are running on another cloud, on-prem, with minikube, or something else, these will be slightly different.
What is an Ingress?
An Ingress in Kubernetes provides external access to your Kubernetes services. You configure access by creating a collection of routing rules that define which requests reach which services.
This lets you consolidate your routing rules into a single resource. For example, you might want to send requests to mydomain.com/ api1 to the api1 service, and requests to mydomain.com/ api2 to the api2 service. With an Ingress, you can easily set this up without creating a bunch of LoadBalancers or exposing each service on the Node.
An Ingress provides the following:
Externally reachable URLs for applications deployed in Kubernetes clusters
Name-based virtual host and URI-based routing support
Load Balancing rules and traffic, as well as SSL termination
Kubernetes Service types – an overview
ClusterIP
A ClusterIP service is the default service. It gives you a service inside your cluster that other apps inside your cluster can access. There is no external access.
The YAML for ClusterIP service looks like this:
apiVersion : v1 kind : Service metadata : name : nginx-service namesapce : default spec : selector : app : nginx type : ClusterIP ports : - name : http port : 80 targetPort : 80 protocol : TCP
Create the service:
$ kubectl apply -f nginx-svc.yaml service/nginx-service created
Check it:
$ kubectl get svc nginx-serice NAME TYPE CLUSTER-IP EXTERNAL-IP PORT ( S ) AGE nginx-service ClusterIP 172.20.54.138 <none> 80/TCP 38s
NodePort
A NodePort service is the most primitive way to get external traffic directly to your service. NodePort, as the name implies, opens a specific port on all the Nodes (the VMs), and any traffic that is sent to this port is forwarded to the service.
When we create a Service of type NodePort, Kubernetes gives us a nodePort value. Then the Service is accessible by using the IP address of any node along with the nodePort value. In other words, when a user sets the Service type field to NodePort, the Kubernetes master allocates a static port from a range, and each Node will proxy that port (the same port number on every Node) into our Service.
The YAML for NodePort service looks like this:
apiVersion: v1 kind: Service metadata: name: nginx-service namespace: default spec: selector: app: nginx type: NodePort ports: - name: http port: 80 targetPort: 80 nodePort: 30036 protocol: TCP
The nodePort parameter here is optional, added here just for an example. Without it, Kubernetes will allocate a port from the 30000-32767 ports range.
Create the service:
$ kubectl apply -f nginx-svc.yaml service/nginx-service created
Check it:
$ kubectl get svc nginx-service NAME TYPE CLUSTER-IP EXTERNAL-IP PORT ( S ) AGE nginx-service NodePort 172.20.54.138 <none> 80:30036/TCP 40s
Please keep in mind that it will work as long as the node is reachable via its IP addresses. In most cases, the worker nodes reside in our private network(like office networks or private VPC). In such cases, we can not access the NodePort service from the Internet.
LoadBalancer
A LoadBalancer service is the standard way to expose a service to the internet.
In case of AWS – will create an AWS Load Balancer, by default Classic type, which will proxy traffic to all ЕС2 instances of the TargetGroup tied to this Load Balancer, and then via NodePort Service – to all the pods.
LoadBalancer type provides a Public IP address or DNS name to which the external users can connect. The traffic flows from the LoadBalancer to a mapped service on a designated port, which eventually forwards it to the healthy pods. Note that LoadBalancers doesn’t have a direct mapping to the pods.
The YAML for LoadBalancer service looks this:
apiVersion : v1 kind : Service metadata : name : nginx-service namespace : default spec : selector : app : nginx type : LoadBalancer ports : - name : http port : 80
Apply it:
$ kubectl apply -f nginx-svc.yaml service/nginx-service created
Check:
$ kubectl get svc nginx-service NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE nginx-service LoadBalancer 172.20.54.138 ac8415de24f6c4db9b5019f789792e45-443260761.ap-south-1.elb.amazonaws.com 80:30968/TCP 1h
So, the LoadBalancer service type:
will provide external access to pods
will provide a basic load-balancing to pods on different EC2
will give an ability to terminate SSL/TLS sessions
doesn’t support level-7 routing
The big downside is that each service you expose with a LoadBalancer will get its own IP address, and you have to pay for a LoadBalancer per exposed service, which can get expensive!
Ingress
Unlike all the above examples, Ingress is actually NOT a dedicated Service. Instead, it sits in front of multiple services and act as a “smart router” or entrypoint into your cluster.
It just describes a set of rules for the Kubernetes Ingress Controller to create a Load Balancer, its Listeners, and routing rules for them.
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 .
image source: https://kubernetes.io/docs/concepts/services-networking/ingress/
In order for the Ingress resource to work, the cluster must have an ingress controller running. Only creating an Ingress resource has no effect.
An Ingress controller is responsible for fulfilling the Ingress, usually with a load balancer, though it may also configure your edge router or additional frontends to help handle the traffic.
In this example, we’ll use the Nginx Ingress Controller:
Install:
$ kubectl apply -f <https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.3.1/deploy/static/provider/cloud/deploy.yaml>
Output:
namespace/ingress-nginx created serviceaccount/ingress-nginx created clusterrole.rbac.authorization.k8s.io/ingress-nginx created clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx created role.rbac.authorization.k8s.io/ingress-nginx created rolebinding.rbac.authorization.k8s.io/ingress-nginx created service/ingress-nginx-controller-admission created service/ingress-nginx-controller created deployment.apps/ingress-nginx-controller created validatingwebhookconfiguration.admissionregistration.k8s.io/ingress-nginx-admission created serviceaccount/ingress-nginx-admission created clusterrole.rbac.authorization.k8s.io/ingress-nginx-admission created role.rbac.authorization.k8s.io/ingress-nginx-admission created rolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created job.batch/ingress-nginx-admission-create created job.batch/ingress-nginx-admission-patch created
Pre-flight check
A few pods should start in the ingress-nginx namespace:
$ kubectl get pods --namespace = ingress-nginx
After a while, they should all be running. The following command will wait for the ingress controller pod to be up, running, and ready:
$ kubectl wait --namespace ingress-nginx \ --for = condition = ready pod \ --selector = app.kubernetes.io/component = controller \ --timeout = 120s
This has set up the Nginx Ingress Controller. Now, we can create Ingress resources in our Kubernetes cluster and route external requests to our services. Let’s do that.
Creating a Kubernetes Ingress
First, let’s create two services to demonstrate how the Ingress routes our request. We’ll run two web applications that output a slightly different response.
apiVersion : v1 kind : Pod metadata : name : apple-app labels : app : apple spec : containers : - name : apple-app image : hashicorp/http-echo args : - "-text=apple" --- apiVersion : v1 kind : Service metadata : name : apple-service spec : selector : app : apple type : NodePort ports : - port : 5678 # Default port for image
apiVersion : v1 kind : Pod metadata : name : banana-app labels : app : banana spec : containers : - name : banana-app image : hashicorp/http-echo args : - "-text=banana" --- apiVersion : v1 kind : Service metadata : name : banana-service spec : selector : app : banana type : NodePort ports : - port : 5678 # Default port for image
Create the resources
$ kubectl apply -f apple.yaml $ kubectl apply -f banana.yaml
Now, declare an Ingress to route requests to /apple to the first service, and requests to /banana to second service.
apiVersion : networking.k8s.io/v1 kind : Ingress metadata : name : example-ingress annotations : nginx.ingress.kubernetes.io/rewrite-target : / spec : rules : - http : paths : - path : /apple pathType : Prefix backend : service : name : apple-service port : number : 5678 - path : /banana pathType : Prefix backend : service : name : banana-service port : number : 5678
Here we set two rules: if URI == /apple or /banana – then send the traffic to the apple-service or banana-service accordingly.
Deploy it:
$ kubectl apply -f ingress.yaml
Another example is the hostname-based routing.
Update the manifest:
apiVersion : networking.k8s.io/v1 kind : Ingress metadata : name : example-ingress annotations : nginx.ingress.kubernetes.io/rewrite-target : / spec : rules : - host : apple.banana.com http : paths : - path : /apple pathType : Prefix backend : service : name : apple-service port : number : 5678 - path : /banana pathType : Prefix backend : service : name : banana-service port : number : 5678
The Ingress controller provisions an implementation-specific load balancer that satisfies the Ingress, as long as the Services ( apple-service , banana-service ) exist. When it has done so, you can see the address of the load balancer at the Address field.
Name based virtual hosting
Name-based virtual hosts support routing HTTP traffic to multiple host names at the same IP address.
image source: https://kubernetes.io/docs/concepts/services-networking/ingress/#name-based-virtual-hosting
The following Ingress tells the backing load balancer to route requests based on the Host header.
apiVersion : networking.k8s.io/v1 kind : Ingress metadata : name : example-ingress spec : rules : - host : apple.service.com http : paths : - pathType : Prefix path : "/" backend : service : name : apple-service port : number : 5678 - host : banana.service.com http : paths : - pathType : Prefix path : "/" backend : service : name : banana-service port : number : 5678
Here we left Services without changes, but in the Rules, we set that a request to the apple.service.com must be sent to the apple-service and banana.service.com to the banana-service.
Summary
A Kubernetes Ingress is a robust way to expose your services outside the cluster. It lets you consolidate your routing rules to a single resource, and gives you powerful options for configuring these rules.
“That’s all folks!”

