Customizing Error Pages (Nginx Ingress Controller)
Last updated
The Docker Image Server Approach
The most common way to do it, which is part of the offical solution is to create a Docker image server capable of responding to any request with 404 content, except /healthz
and /metrics.
This could be an Nginx instance.
/healthz
should return 200
/metrics
is optional, but it should return data that is readable by Prometheus in case you are using it for k8s metrics.
Note: Nginx can provide some basic data that Prometheus can read.
/returns
a 404 with your custom HTML content.
Thus, the Dockerfile
looks like this:
FROM nginx:alpine
# Remove default NGINX Config
# Take care of Nginx logging
RUN rm /etc/nginx/conf.d/default.conf && \
ln -sf /dev/stdout /var/log/nginx/access.log && \
ln -sf /dev/stderr /var/log/nginx/error.log
# NGINX Config
COPY ./default.conf /etc/nginx/conf.d/default.conf
# Resources
COPY content/ /var/www/html/
CMD ["nginx", "-g", "daemon off;"]
In the same folder where Dockerfile is located, create this default.conf
Nginx configuration file:
server {
root /var/www/html;
index 404.html;
location / {
}
location /healthz {
access_log off;
return 200 "healthy\n";
}
location /metrics {
# This creates a readable and somewhat useful response for Prometheus
stub_status on;
}
error_page 404 /404.html;
location = /404.html {
internal;
}
}
At last, provide a content/404.html
file with HTML/CSS to your own liking.
Now build the Docker image with:
docker build -t custom-default-backend .
Tag this image so that it is ready to be pushed into DockerHub (or your own private docker registry):
docker tag custom-default-backend:latest <your_dockerhub_username>/custom-default-backend
Push the image to a DockerHub repository:
docker push <your_dockerhub_username>/custom-default-backend
Create k8s resource file (custom_default_backend.yaml)
Integrate this custom-default-backend image into the Helm installation.
---
apiVersion: v1
kind: Service
metadata:
name: custom-default-backend
namespace: ingress-nginx
labels:
app.kubernetes.io/name: custom-default-backend
app.kubernetes.io/part-of: ingress-nginx
spec:
selector:
app.kubernetes.io/name: custom-default-backend
app.kubernetes.io/part-of: ingress-nginx
ports:
- port: 80
targetPort: 80
name: http
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: custom-default-backend
namespace: ingress-nginx
labels:
app.kubernetes.io/name: custom-default-backend
app.kubernetes.io/part-of: ingress-nginx
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: custom-default-backend
app.kubernetes.io/part-of: ingress-nginx
template:
metadata:
labels:
app.kubernetes.io/name: custom-default-backend
app.kubernetes.io/part-of: ingress-nginx
spec:
containers:
- name: custom-default-backend
# Don't forget to edit the line below
image: <your_dockerhub_username>/custom-default-backend:latest
imagePullPolicy: Always
ports:
- containerPort: 80
Create k8s namespace ingress-nginx
if not created already. Then proceed to create these two resources.
kubectl apply -f custom_default_backend.yaml
In order to tie the Nginx Ingress Controller with the new service, you can either just edit the deployment of the Ingress Controller or remove it completely via Helm:
helm delete nginx-ingress -n ingress-nginx
And install it again with the command below
Note: make sure you have the --set
flag with proper arguments included
helm install nginx-ingress --namespace ingress-nginx stable/nginx-ingress --set defaultBackend.enabled=false,controller.defaultBackendService=ingress-nginx/custom-default-backend
Following the steps will ensure the successful implementation of your custom default backend.
However, the newest version of ingress-nginx allows the user to only specify the docker image to pull - no need for other k8s resource files (i.e. service and deployment).
The current values.yaml
for the nginx ingress controller will allow a custom default backend:
defaultBackend:
enabled: true
name: custom-default-backend
image:
repository: <ABCD>/custom-default-backend
tag: "latest"
pullPolicy: Always
port: 8080
extraVolumeMounts:
- name: tmp
mountPath: /tmp
extraVolumes:
- name: tmp
emptyDir: {}
The Template Modification
There is another way to provide the custom error page in ingress-nginx, by just modifying the template of ingress-Nginx.(/etc/nginx/template).
volumeMounts:
- name: custom-errors
mountPath: /usr/local/nginx/html/
readOnly: true
- name: nginx-ingress-template-volume
mountPath: /etc/nginx/template
readOnly: true
In the above YAML example, use path (/usr/local/nginx/html) for mounting the custom error pages. Below is a preview of nginx template default server.
# backend for when default-backend-service is not configured or it does not have endpoints
server {
listen default_server reuseport backlog=;
listen [::]: default_server reuseport backlog=;
set $proxy_upstream_name "internal";
access_log off;
root /usr/local/nginx/html/;
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
location / {
return 404;
}
location = /404.html {
internal;
}
location = /50x.html {
internal;
}
}
Provide a custom 404.html and 50xhtml page inside the root (/usr/local/nginx/html/).
To mount the voulme with custom pages use,
volumes:
- name: custom-errors
configMap:
# Provide the name of the ConfigMap you want to mount.
name: custom-ingress-pages
items:
- key: "404.html"
path: "404.html"
- key: "50x.html"
path: "50x.html"
- key: "index.html"
path: "index.html"
Benefits of this solution
This solution doesn’t require you to spawn another service or pod (container) of any kind to work. This will be taken care of from the ingress-nginx controller pod deployment(or daemonset). You also don’t need to warm up your cluster for extra service, you just need to do it for custom error messages (or pages).
This approach will save you resources and serve you custom error pages from the ingress-nginx controller itself.
Looking for an end-to-end incident alerting, on-call scheduling and response orchestration platform?
Sign up for a 14-day free trial of Zenduty. No CC required. Implement modern incident response and SRE best practices within your production operations and provide industry-leading SLAs to your customers
Shubham Bhaskar Sharma
Time travelling through entropy