Kubernetes Ingress with Nginx
How to install and secure Nginx Ingress
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.
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.com
will 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.yaml
and 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 👇