Kubernetes Ingress with Nginx

How to install and secure Nginx Ingress

Magsther
FAUN — Developer Community 🐾

--

Introduction

In the previous post, we looked at CertManager and Let’s Encrypt.

The installation of cert-manager was needed and will do the work with Kubernetes to request a certificate and respond to the challenge to validate it.

What to expect

In this guide, we will:

  • Create a demo application and deploy it to our Kubernetes cluster.
  • Install a Nginx Ingress controller
  • Create Nginx ingress resources
  • Provision certificates for our domain so that we can encrypt the traffic to our application.

Prerequisite

To follow along, you will need:

  • Have a Kubernetes cluster deployed as well as kubectl and helm
  • Deployed CertManager and Let’s encrypt (see previous post)

Demo application

This example uses manifests to create and expose a sample service called whoami.

Whoami is a Tiny Go webserver that prints OS information and HTTP request to output.

The application exposes a service on port 80.

Create the deployment with kubectl apply -f whoami.yaml

deployment.apps/whoami-deployment created
service/whoami-service created

In a little while, this service will receive external traffic using Nginx ingress.

Verify that the service is up and running

If you run kubectl get svc, you will see that the whoami-service is using type ClusterIP which is the internal IP on which the Service is exposed.

whoami-service  ClusterIP  80/TCP

This means it cannot be accessible from outside the Kubernetes cluster.

We don’t want to expose this service as LoadBalancer, since we will use Nginx Ingress Controller for that.

We can still test the application to see that it’s working. For that, we can use Port Forwarding.

Port Forwarding is a method to access applications in a cluster.

kubectl port-forward service/whoami-service 8080:80

Now you can access the application by using curl or open a browser and go to http://127.0.0.1:8080/

curl localhost:8080

Hostname: whoami-deployment-57fb67548c-s69g7
IP: 127.0.0.1
IP: ::1
RemoteAddr: 127.0.0.1:57370
GET / HTTP/1.1
Host: localhost:8080
User-Agent: curl/7.79.1
Accept: */*

What is a Nginx Ingress Controller?

A kubernetes ingress controller is designed to be the access point for HTTP and HTTPS traffic to the software running within your cluster. The ingress-nginx-controller does this by providing an HTTP proxy service supported by your cloud provider's load balancer.

This means that it is exposed to the outside, but also has access to the services inside the Kubernetes cluster.

The deployment of the Ingress controller creates a LoadBalancer service

When deployed, the Ingress Controllers watches for ingress resources, which is basically a list of rules where it should route traffic too.

In our case, any HTTP traffic that arrives on port 80 on host whoami.example.comwill be directed to the (backend) whoami-service.

An Ingress allows you to route traffic from outside to services inside of your Kubernetes cluster.

There are many other Ingress controllers on the market, another popular one is Traefik, which we wrote about here.

Deploying Nginx Ingress Controller

There are multiple ways to install the NGINX ingress controller. In the installation guide, we can see that we can deploy it using Helm.

Helm helps you manage Kubernetes applications

To deploy the ingress controller run the following command:

helm upgrade --install ingress-nginx ingress-nginx \
--repo https://kubernetes.github.io/ingress-nginx \
--namespace ingress-nginx --create-namespace

This will install the controller in the ingress-nginx namespace, creating that namespace if it doesn't already exist.

Once deployed, you can verify that pods are now starting in the ingress-nginx namespace:

kubectl get pods --namespace=ingress-nginx

After a while, all pods should be running.

If you now run kubectl get svc -n ingress-nginx you can see that Load Balancer was created and you should see the external IP address of the Load Balancer.

This Load Balancer will now receive traffic on port 80 and port 443 and forward it to the to the ingress pods.

Creating the Nginx Ingress Resource

An Ingress resource is what Kubernetes uses to expose the example service outside the cluster. By creating an Ingress resource, we can route traffic to a corresponding backend service.

When you deployed the Nginx Ingress Controller using Helm, it outputted an example Ingress that makes use of the controller.

An example Ingress that makes use of the controller:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example
namespace: foo
spec:
ingressClassName: nginx
rules:
- host: www.example.com
http:
paths:
- pathType: Prefix
backend:
service:
name: exampleService
port:
number: 80
path: /
# This section is only required if TLS is to be enabled for the Ingress
tls:
- hosts:
- www.example.com
secretName: example-tls

We can use this as a template for our resource.

DNS

The external IP that is allocated to the ingress-controller is the IP to which all incoming traffic should be routed. I’ll use a test domain called example.com.

This test domain needs to be pointed to the external IP address of the LoadBalancer.

Since I’m testing locally, I’ll just add an entry to my /etc/hosts file as such:

<EXTERNAL_IP> whoami.example.com

We will now create a rule that will route traffic from whoami.example.com to our backend service called whoami-service service.

Creating the (Nginx) Ingress rule

Create a new file called ingress-rule.yamland paste in the following:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: whoami-ingress
spec:
ingressClassName: nginx
rules:
- host: whoami.example.com
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: whoami-service
port:
number: 80

Apply the ingress rule with kubectl apply -f ingress-rule.yaml

ingress.networking.k8s.io/whoami-ingress created

If you now type kubectl get ing, you should see the following output:

NAME           CLASS   HOSTS                PORTS
whoami-ingress nginx whoami.example.com 80

Testing

Open a browser and go to http://whoami.example.com or use the curl command:

curl whoami.example.com

Hostname: whoami-deployment-57fb67548c-44dwf
GET / HTTP/1.1
Host: whoami.example.com
...

Getting a Certificate

To issue a new certificate for our application, we need to update our ingress-rule.yaml file with the following lines

annotations:
cert-manager.io/cluster-issuer: "letsencrypt-staging"
tls:
- hosts:
- whoami.example.com
secretName: whoami-tls

As you noticed, we need to annotate the yaml file with the ClusterIssuer we created in the previous post. By that, it will automatically create and issue certificates for the domains specified in the Ingress manifest.

The full file should look like this:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: whoami-ingress
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-staging"
spec:
ingressClassName: nginx
rules:
- host: whoami.example.com
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: whoami-service
port:
number: 80
tls:
- hosts:
- whoami.example.com
secretName: whoami-tls

In the annotations field we added cert-manager/io/cluster-issuer which will set the cert-manager ClusterIssuer to letsencrypt-staging

The tls block to specifies the hosts for which we want to acquire certificates, and specify a secretName. This secret will contain the TLS private key and issued certificate.

Run kubectl apply -f ingress-rule.yaml

If you type kubectl get ingress, you see that port 443 is added.

NAME             CLASS   HOSTS                ADDRESS   PORTS
whoami-ingress nginx whoami.example.com x.x.x.x 80, 443

If you now run kubectl describe ingress, you should see the following output:

Name:             whoami-ingress
Labels: <none>
Namespace: default
Address: x.x.x.x
Ingress Class: nginx
Default backend: <default>
TLS:
whoami-tls terminates whoami.example.com
Rules:
Host Path Backends
---- ---- --------
whoami.example.com
/ whoami-service:80 (10.244.0.14:80,10.244.0.15:80)
Annotations: cert-manager.io/cluster-issuer: letsencrypt-staging
...

Once the certificate has been successfully created, you can see more details about the certificate with kubectl describe certificate

In the output in the Events section, you can see that the TLS certificate was successfully issued and HTTPS encryption is now active for our domain.

To view all the certificates that are created

kubectl get certificate --all-namespaces

Get more information about a certificate

kubectl describe certificate whoami-tls

View ClusterIssuers available in your cluster.

kubectl get clusterissuer

NAME READY AGE
letsencrypt-staging True 89m

We have now configured HTTPS using a Let’s Encrypt certificate for our Nginx Ingress.

We can now test sending request to our whoami service to see if HTTPS works.

Test it

Open a browser and go to https://whoami.example.com/ and you will see that it works. You will noticed that the certificate cannot be verified (remember we are using the staging server from Let’s Encrypt), but we can see that HTTPS is working.

Now when this is working, you could do the same using the production server.

Conclusion

In this post, created a demo application and deployed it to our Kubernetes cluster. After that we installed Nginx Ingress controller and created Nginx ingress resources. Lastly, we provisioned certificates for our domain so that we can encrypt the traffic to our application.

If you find this helpful, please click the clap 👏 button below a few times to show your support for the author 👇

🚀Join FAUN & get similar stories in your inbox each week

--

--