Creating a Kubernetes Nginx Ingress Controller and create a rule to a sample application
Tags: kubernetes, node
Published: 2020-09-20 19:16:06

![cover](https://dev-to-uploads.s3.amazonaws.com/i/vhrfc839gdajrqvsc940.png)

Whenever you are creating an application that you want to expose to the outside world, it's always smart to control the flow towards the application behind it. That's why Kubernetes has something called `Kubernetes Ingress`. But what is it?

## Kubernetes Ingress

[Kubernetes Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/) allows you to expose HTTP and HTTPS routes from outside the cluster to services within the cluster. The traffic routing is then controlled by rules defined in the ingress sources.

For this article, I will explain how you can get started on creating your own `Nginx Ingress Controller`. Of course this is not the only possibility, [so feel free to check other ingress controllers such as Istio, HAProxy, Traefik, ...](https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/)

Some advantages of using an ingress controller:

* Rate limiting, Timeouts, ...
* Authentication
* Content based routing

## Sample Hello World application

Before we create our controller, let's get started on creating a simple demo application. The only thing our application will do is process the HTTP request, wait a couple of seconds and return a "Hello World" response.

### Creating our sample app

I decided to create this application in Node.js. So if you have `npm` and `node` installed, run the following commands:

```bash
npm init -y
npm i express --save
```

Whereafter you can create an `index.js` file with the following content:

```javascript
const express = require('express')
const app = express()
const port = 3000

app.get('/', async (req, res) => {
  console.log('Got request, waiting a bit');
  await delay(10 * 1000);
  res.send('Hello World!')
})

app.listen(port, () => {
  console.log(`Example app listening at http://localhost:${port}`)
})

const delay = async (timeout = 1000) => {
  return new Promise((resolve, reject) => setTimeout(resolve, 1000));
}
```

### Packaging it as a container

Since everything is created in terms of application code, we can package it all up into a Docker container by creating a Dockerfile:

**Dockerfile**

```
FROM node:latest

WORKDIR /usr/src/app

# Install deps
RUN apt-get update

# Create Certificate
RUN apt-get install ca-certificates

# Install Package.json dependendencies
COPY package.json .
RUN npm install

# Copy Source Code
ADD . /usr/src/app

CMD [ "npm", "run", "start" ]
EXPOSE 3000
```

That we can build with (choose one for your use-case):

```bash
# Local build (for local use)
# Note: when using minikube, make sure to run `eval $(minikube docker-env)` to build images in minikube context
docker build -t local/node-sample-helloworld .

# Remote build (to push to docker repository)
docker build -t thebillkidy/node-sample-helloworld .
docker push thebillkidy/node-sample-helloworld
```

### Running it on Kubernetes

Once it is build, we can now run it on our Kubernetes cluster. For that we create a Deployment YAML file:

**kubernetes.yaml**

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: d-node-sample-helloworld
spec:
  selector:
    matchLabels:
      app: node-sample-helloworld
  replicas: 1
  template:
    metadata:
      labels:
        app: node-sample-helloworld
    spec:
      containers:
      - name: main
        image: thebillkidy/node-sample-helloworld:latest # if local, utilize local/node-sample-helloworld
        imagePullPolicy: Always # if local, utilize Never
        ports:
        - containerPort: 3000
```

That we can apply with `kubectl apply -f kubernetes.yaml` and should now show the following after running `kubectl get deployments -A`:

```bash
NAME                       READY   UP-TO-DATE   AVAILABLE   AGE
d-node-sample-helloworld   1/1     1            1           37s
```












































Kubernetes is getting more popular everyday and it's no wonder why! When you are running applications on-premise or in-cloud, the possibility of having the applications in a portable way is a strong one! Removing the friction for scaling-out your application when you are ready for it, or even bursting scenarios.

## Nginx Ingress Controller

We now have a simple Hello World application running, but it's only available internally! What we could do now is expose it through Kubernetes and a LoadBalancer, but let's actually utilize our Ingress Controller here! So let's get started creating this Ingress Controller.

### Installation

The first step that we should do is create the NGINX Ingress controller. For this we can follow these steps:

> Note: before the `stable/nginx-ingress` chart was utilized. But this is now deprecated!

```bash
# ref: https://github.com/kubernetes/ingress-nginx (repo)
# ref: https://github.com/kubernetes/ingress-nginx/tree/master/charts/ingress-nginx (chart)

# 1. Create namespace
kubectl create namespace ingress-nginx

# 2. Add the repository
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx

# 3. Update the repo
helm repo update

# 4. Install nginx-ingress through Helm
helm install ingress-controller ingress-nginx/ingress-nginx --namespace ingress-nginx
```

Once we ran the above, we should now be able to access the ingress controller by loading the external IP (`kubectl -n ingress-nginx get svc`). 

> **Note:** when working with Minikube or others, we can utilize `kubectl port-forward svc/ingress-controller-ingress-nginx-controller --namespace ingress-nginx --address 0.0.0.0 8000:80` and access it on `http://localhost:8000`

We are now ready to expose our application!

### Exposing our Application

Once an ingress controller is created, we need to expose our application internally:

```bash
kubectl expose deployment d-node-sample-helloworld --name svc-node-sample-helloworld
```

and configure our Ingress controller to route traffic to it as defined in the [Kubernetes Ingress API](https://kubernetes.io/docs/concepts/services-networking/ingress/#the-ingress-resource). By creating a YAML file:

**ingress-node-sample-helloworld.yaml**

```yaml
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: ingress-node-sample-helloworld
  annotations:
    # Target URI where the traffic must be redirected
    # More info: https://github.com/kubernetes/ingress-nginx/blob/master/docs/examples/rewrite/README.md
    nginx.ingress.kubernetes.io/rewrite-target: /
    kubernetes.io/ingress.class: nginx
spec:
  rules:
    # Uncomment the below to only allow traffic from this domain and route based on it
    # - host: my-host # your domain name with A record pointing to the nginx-ingress-controller IP
    - http:
        paths:
        - path: / # Everything on this path will be redirected to the rewrite-target
          backend:
            serviceName: svc-node-sample-helloworld # the exposed svc name and port
            servicePort: 3000
```

Which we apply with `kubectl apply -f ingress-node-sample-helloworld.yaml`

Now once this is applied, we should be able to execute a cURL request to access our application! So let's try this:

```bash
# Execute a GET request with the specified host and IP
# Note: the Host should match what is written in spec.rules.host
curl -k -X "GET" -H "Host: my-host" http://YOUR_IP
```

Or we can also open it in our browser and navigate to http://YOUR_IP

> **Note:** If this is not working, check `kubectl describe ing` and make sure that the configuration is correct

## Conclusion

In this article a demonstration was made on how you can set up your own ingress controller for Kubernetes. This is of course a small step in the entire chain of use cases, where most often you want to do more such as rate limiting, or even monitoring it.

[The next article](/posts/infrastructure/kubernetes-nginx-ingress-controller-monitoring-prometheus) will explain more in-depth how you are able to start monitoring what we have just set-up through Prometheus and visualize all of it in Grafana.

> Note: feel free to check the original post at my blog [https://xaviergeerinck.com](https://xaviergeerinck.com)