How Ingress Works in Kubernetes
By: Craig Godden-Payne
Tags: Programming, Minikube, Kubernetes, Docker, Containers

Setting Up Ingress With minikube

For this example, we are going to go through the process of setting up Ingress, using the Ingress Controller type. The implementation of the Ingress Controller is going to be nginx, but it is just as straightforward to use other ingress controllers, such as Traefik.

First of all, make sure minikube is started and working.

➜ minikube start

😄 minikube v1.14.2 on Darwin 10.15.7

✨ Using the hyperkit driver based on existing profile

👍 Starting control plane node minikube in cluster minikube

🔄 Restarting existing hyperkit VM for "minikube" ...

🐳 Preparing Kubernetes v1.19.2 on Docker 19.03.12 ...

🔎 Verifying Kubernetes components...

🌟 Enabled addons: storage-provisioner, default-storageclass

🏄 Done! kubectl is now configured to use "minikube" by default

We also need to enable ingress support for minikube, which can be done with the following command

➜ minikube addons enable ingress

🔎 Verifying ingress addon...

🌟 The 'ingress' addon is enabled

If this does not work, delete your minikube instance and run using this command.

minikube delete && minikube start --vm=true

We are going to create a namespace, as its good practice to logically separate things from each other.

➜ cat <<EOF | kubectl apply -f -

apiVersion: v1

kind: Namespace

metadata:

name: minikube-example

labels:

name: minikube-example

EOF

namespace/minikube-example created

This will create a namespace called minikube-example .

➜ kubectl get namespaces

NAME STATUS AGE

default Active 7m29s

kube-node-lease Active 7m30s

kube-public Active 7m30s

kube-system Active 7m31s

minikube-example Active 39s

Now we should deploy a pod that contains a web application. One I use a lot for testing is one of the Google samples, Hello App.

➜ cat <<EOF | kubectl apply -n minikube-example -f -

apiVersion: apps/v1

kind: Deployment

metadata:

name: test-app

labels:

app: test-app

spec:

replicas: 3

selector:

matchLabels:

app: test-app

template:

metadata:

labels:

app: test-app

spec:

containers:

- name: test-app

image: gcr.io/google-samples/hello-app:1.0

ports:

- containerPort: 8080

EOF

deployment.apps/test-app created

I can see the pods running now (I use k9s, which is an awesome tool for managing your k8s cluster: https://github.com/derailed/k9s).

Viewing the cluster in k9s

Hitting the web app after using a port forward

I manually added a port-forwarding rule to check the app works as expected.

Underneath it seems to set up a Node Port for the duration of time that k9s is open, but if you wanted to do this manually, it is possible by running a command like this:

➜ cat <<EOF | kubectl apply -n minikube-example -f -

apiVersion: v1

kind: Service

metadata:

name: test-app

labels:

name: test-app

spec:

type: NodePort

ports:

- port: 8080

name: http

selector:

app: test-app

EOF service/test-app created

🏃 Starting tunnel for service test-app.

|------------------|----------|-------------|------------------------|

| NAMESPACE | NAME | TARGET PORT | URL |

|------------------|----------|-------------|------------------------|

| minikube-example | test-app | |

|------------------|----------|-------------|------------------------|

http://127.0.0.1:53732

❗ Because you are using a Docker driver on darwin, the terminal needs to be open to run it. ➜ minikube service --url test-app -n minikube-example🏃 Starting tunnel for service test-app.|------------------|----------|-------------|------------------------|| NAMESPACE | NAME | TARGET PORT | URL ||------------------|----------|-------------|------------------------|| minikube-example | test-app | | http://127.0.0.1:53732 |------------------|----------|-------------|------------------------|❗ Because you are using a Docker driver on darwin, the terminal needs to be open to run it.

Hello, world!

Version: 1.0.0

Hostname: test-app-df4854fbc-z9zq9 ➜ curl http://127.0.0.1: 53732 / Hello, world!Version: 1.0.0Hostname: test-app-df4854fbc-z9zq9

(Note that the selector uses the label rather than the name since the deployment will create a name with a unique identifier postfixed. Also remember to delete the service afterward, as we are going to use Ingress Controller instead).

Ok cool, so now the deployment and pods exist, we can focus on the Ingress Controller.

➜ cat <<EOF | kubectl apply -n minikube-example -f -

---

apiVersion: v1

kind: Service

metadata:

name: test-app

labels:

name: test-app

spec:

type: NodePort

ports:

- port: 8080

name: http

selector:

app: test-app

---

apiVersion: networking.k8s.io/v1

kind: Ingress

metadata:

name: minikube-example-ingress

annotations:

nginx.ingress.kubernetes.io/rewrite-target: /$1

spec:

rules:

- host: minikube-example.com

http:

paths:

- path: /

pathType: Prefix

backend:

service:

name: test-app

port:

number: 8080

EOF

service/test-app created

ingress.networking.k8s.io/minikube-example-ingress created

What this will do is create an ingress pod, which will proxy all content with the host header minikube-example.com to the service with name test-app.

To check this is working as expected, run the following command. (It could take a couple of minutes for the IP to appear.)

➜ kubectl get ingress -n minikube-example

NAME CLASS HOSTS ADDRESS PORTS AGE

minikube-example-ingress <none> minikube-example.com 192.168.64.4 80 56s

You should then pass this IP into the host's file, like so:

➜ echo '192.168.64.4 minikube-example.com' | sudo tee -a /etc/hosts

Now, time to try it out!

➜ curl minikube-example.com

Hello, world!

Version: 1.0.0

Hostname: test-app-df4854fbc-6mjnw

The ingress controller hits each pod in turn

The ingress controller hits each pod in turn

The ingress controller hits each pod in turn

You now have the nginx ingress controller, running on minikube!

To change this up and test some things out, we are going to try two back ends and update the Ingress to include them both.

The easiest way to get rid of what we have done so far is to delete minikube and start it back up again, or use a different namespace.

➜ minikube delete && minikube start --vm=true && minikube addons enable ingress 🔥 Deleting "minikube" in hyperkit ...

💀 Removed all traces of the "minikube" cluster.

😄 minikube v1.14.2 on Darwin 10.15.7

✨ Automatically selected the hyperkit driver

👍 Starting control plane node minikube in cluster minikube

🔥 Creating hyperkit VM (CPUs=2, Memory=4000MB, Disk=20000MB) ...

🐳 Preparing Kubernetes v1.19.2 on Docker 19.03.12 ...

🔎 Verifying Kubernetes components...

🌟 Enabled addons: default-storageclass, storage-provisioner

🏄 Done! kubectl is now configured to use "minikube" by default

🔎 Verifying ingress addon...

🌟 The 'ingress' addon is enabled

And then run in a config, similar to this:

➜ cat <<EOF | kubectl apply -n minikube-example -f -

---

apiVersion: v1

kind: Namespace

metadata:

name: minikube-example

labels:

name: minikube-example

---

apiVersion: apps/v1

kind: Deployment

metadata:

name: hello-app

labels:

app: hello-app

spec:

replicas: 3

selector:

matchLabels:

app: hello-app

template:

metadata:

labels:

app: hello-app

spec:

containers:

- name: hello-app

image: gcr.io/google-samples/hello-app:1.0

ports:

- containerPort: 8080

---

apiVersion: apps/v1

kind: Deployment

metadata:

name: insights-app

labels:

app: insights-app

spec:

replicas: 3

selector:

matchLabels:

app: insights-app

template:

metadata:

labels:

app: insights-app

spec:

containers:

- name: insights-app

image: yeasy/simple-web:latest

ports:

- containerPort: 80

---

apiVersion: v1

kind: Service

metadata:

name: hello-app

labels:

name: hello-app

spec:

type: NodePort

ports:

- port: 8080

name: http

selector:

app: hello-app

---

apiVersion: v1

kind: Service

metadata:

name: insights-app

labels:

name: insights-app

spec:

type: NodePort

ports:

- port: 80

name: http

selector:

app: insights-app

---

apiVersion: networking.k8s.io/v1

kind: Ingress

metadata:

name: minikube-example-ingress

annotations:

nginx.ingress.kubernetes.io/rewrite-target: /$1

spec:

rules:

- host: minikube-example.com

http:

paths:

- path: /hello

pathType: Prefix

backend:

service:

name: hello-app

port:

number: 8080

- path: /insights

pathType: Prefix

backend:

service:

name: insights-app

port:

number: 80

EOF namespace/minikube-example created

deployment.apps/hello-app created

deployment.apps/insights-app created

service/hello-app created

service/insights-app created

ingress.networking.k8s.io/minikube-example-ingress created

Now wait for the pods to enter a running state.

Viewing the cluster in k9s

Wait for the ingress controller to become ready.

➜ kubectl get ingress -n minikube-example

NAME CLASS HOSTS ADDRESS PORTS AGE

minikube-example-ingress <none> minikube-example.com 192.168.64.7 80 51s

And update the host's file to reflect it.

sudo vim /etc/hosts

Now check that both paths can be reached from Ingress.

➜ curl minikube-example.com/hello

Hello, world!

Version: 1.0.0

Hostname: hello-app-5b844b975f-ddrxc

➜ curl minikube-example.com/insights

<!DOCTYPE html> <html> <body><center><h1><font color="blue" face="Georgia, Arial" size=8><em>Real</em></font> Visit Results</h1></center><p style="font-size:150%" >#2020-11-11 12:38:29: <font color="red">1</font> requests from <<font color="blue">LOCAL: 172-17-0-2.ingress-nginx-controller-admission.kube-system.svc.cluster.local</font>> to WebServer <<font color="blue">172.17.0.6</font>></p></body> </html>

Awesome, now we have the Ingress controller routing two different paths to two different back ends.