NGINX Ingress Controller for Amazon EKS — Frequently Used Annotations
Table of Contents
- Introduction
- AWS Load Balancer Controller
- NGINX Ingress Controller
- Conclusion
- About the Author ✍🏻
Introduction
Amazon Elastic Kubernetes Service (Amazon EKS) is a managed service that you can use to run Kubernetes on AWS without needing to install, operate, and maintain your own Kubernetes control plane or nodes. Kubernetes is an open-source system for automating the deployment, scaling, and management of containerized applications.
— Amazon EKS User Guide
An Ingress controller is a specialized load balancer for Kubernetes (and other containerized) environments. An Ingress controller abstracts away the complexity of Kubernetes application traffic routing and provides a bridge between Kubernetes services and external ones.
— NGINX Glossary
The DevOps team here at QloudX, was recently involved in setting up an EKS cluster for one of our enterprise clients. As always, we encountered the question of which ingress controller to use for our EKS cluster.
AWS Load Balancer Controller
We first tried to use AWS Load Balancer Controller. However, the AWS LBC didn’t work out for us for a few reasons:
- We were planning to run 250+ apps from a single large cluster. Since the AWS LBC delegates many of its functionality to AWS services (like path based routing rules), we starting running into many of the limits imposed by AWS’s Application Load Balancer (ALB), like “target groups per ALB” & others described at Quotas for your Application Load Balancers.
- We didn’t want to deploy & maintain multiple ALBs (to workaround these limits) since all our apps combined could be easily served by the capacity of a single ALB.
So we decided to go with the most popular ingress controller for Kubernetes: NGINX!
NGINX Ingress Controller
Installation
Installing the NGINX ingress controller onto our cluster was fairly straight-forward using Helm:
helm upgrade --install ingress-nginx ingress-nginx \
--repo https://kubernetes.github.io/ingress-nginx \
--namespace ingress-nginx --create-namespace
The controller will create its own load balancer in AWS as soon as you install it. Unfortunately, no ingress controller except AWS LBC can create & manage an AWS ALB. The best you can get is an NLB.
However, since we really needed an ALB, we prefer to create our own ALB & configure it to route traffic to NGINX ingress controller, configured as a NodePort service inside the cluster.
In case you’re in the same boat as us, here are the Helm values we used while installing the controller:
controller:
ingressClassResource:
controllerValue: k8s.io/ingress-nginx
enabled: true
name: nginx
kind: DaemonSet
metrics:
enabled: true
port: 10254
service:
annotations: {}
nodePort: 31254
servicePort: 10254
type: NodePort
service:
enableHttp: true
enableHttps: false
nodePorts:
http: 32080
type: NodePort
Basic Usage
The simple use case for an ingress is as follows. Here is an ingress for an app:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app
spec:
ingressClassName: nginx
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app
port:
number: 80
Ingress Annotations
Advanced behavior (beyond basic usage) can be achieved by annotating ingresses.
All annotation keys & values must always be strings! Values like true
must be quoted as “true”.
All annotations always start with nginx.ingress.kubernetes.io
.
Blue/Green Deployments
If you employ blue/green deployments in your cluster, this section is for you!
Reference: https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/#canary
From the NGINX ingress controller’s perspective, blue/green deployments are essentially canary deployments that have been configured to send 100% of a particular type of incoming traffic to the green environment.
The type of requests to send to the green environment can be determined by many factors like:
- Requests that include a specific HTTP header
- Requests that include a specific value for a pre-defined HTTP header
- Requests that include a specific cookie
In each of these cases, the blue service has a standard ingress like this:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-v1
spec:
ingressClassName: nginx
rules:
- host: app-v1.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app-v1
port:
number: 80
The green service always has a separate ingress resource. How it’s configured, depends on what kind of request routing is desired.
The green ingress must always have this annotation:
nginx.ingress.kubernetes.io/canary: "true"
HTTP Header Based Request Routing
To route all requests containing a particular HTTP header to the green environment, the green ingress must be:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-v2
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-by-header: canary
spec:
ingressClassName: nginx
rules:
- host: app-v2.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app-v2
port:
number: 80
All requests containing the HTTP header canary = always will be routed to app v2. All other requests will be routed to app v1.
HTTP Header Value Based Request Routing
To route all requests containing an HTTP header app-version = v2 to app v2:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-v2
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-by-header: app-version
nginx.ingress.kubernetes.io/canary-by-header-value: v2
spec:
ingressClassName: nginx
rules:
- host: app-v2.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app-v2
port:
number: 80
HTTP Header Value Pattern Based Request Routing
To route requests containing any value for the canary header to app v2:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-v2
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-by-header: canary
nginx.ingress.kubernetes.io/canary-by-header-pattern: .*
spec:
ingressClassName: nginx
rules:
- host: app-v2.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app-v2
port:
number: 80
canary-by-header-pattern
can be any PCRE regular expression.
Cookie Based Request Routing
To route all requests containing the cookie canary = always to app v2:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-v2
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-by-cookie: canary
spec:
ingressClassName: nginx
rules:
- host: app-v2.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app-v2
port:
number: 80
Canary Deployments
To route 10% of all incoming live traffic to app v2:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-v2
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "10"
spec:
ingressClassName: nginx
rules:
- host: app-v2.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app-v2
port:
number: 80
Session Affinity / Stickiness
To ensure that users that were served by canaries, continue to be served by canaries, set this annotation on the app v1 ingress (not the canary ingress):
nginx.ingress.kubernetes.io/affinity: cookie
HTTP Response Headers
To include additional HTTP headers in all responses from canary, add this annotation to the canary ingress:
nginx.ingress.kubernetes.io/configuration-snippet: |
add_header "app-version: v2";
NOTE: This feature may not work out of the box. It was disabled due to a CVE. Click here for details.
CORS: Cross-Origin Resource Sharing
Reference: https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/#enable-cors
To enable CORS on your service, use:
nginx.ingress.kubernetes.io/enable-cors: "true"
To fine-tune CORS behavior, use:
nginx.ingress.kubernetes.io/cors-allow-origin: https://*.example.com
nginx.ingress.kubernetes.io/cors-allow-methods: OPTIONS, GET, POST
Nginx Server Configuration
For use cases not covered by annotations, it’s possible to provide Nginx server configuration to ingresses using a special annotation:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/server-snippet: |
set $agentflag 0;
if ($http_user_agent ~* "(Mobile)" ){
set $agentflag 1;
}
if ( $agentflag = 1 ) {
return 301 https://m.example.com;
}
Redirect Requests
To redirect incoming requests, use:
nginx.ingress.kubernetes.io/permanent-redirect: https://other-app.example.com
for a permanent HTTP 301 redirect, or
nginx.ingress.kubernetes.io/temporal-redirect: https://other-app.example.com
for a temporary HTTP 302 redirect.
Request Mirroring
Incoming requests can be mirrored to another backend by applying this annotation:
nginx.ingress.kubernetes.io/mirror-target: https://mirror-app.example.com/$request_uri
Responses from mirror backends are ignored.
Regular Expressions in Paths
Reference: https://kubernetes.github.io/ingress-nginx/user-guide/ingress-path-matching/
Regular expressions cannot be used in the host URL, but they can be used in the URL paths if needed, by using the use-regex
annotation:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app
annotations:
nginx.ingress.kubernetes.io/use-regex: "true"
spec:
ingressClassName: nginx
rules:
- host: app.example.com
http:
paths:
- path: /app/.*
pathType: Prefix
backend:
service:
name: app
port:
number: 80
Conclusion
In this post, we presented a curated list of commonly-used NGINX ingress annotations. Hopefully, this will serve as a handy reference for you, as it does for us. 😊
About the Author ✍🏻
Harish KM is a Principal DevOps Engineer at QloudX & a top-ranked AWS Ambassador since 2020. 👨🏻💻
With over a decade of industry experience as everything from a full-stack engineer to a cloud architect, Harish has built many world-class solutions for clients around the world! 👷🏻♂️
With over 20 certifications in cloud (AWS, Azure, GCP), containers (Kubernetes, Docker) & DevOps (Terraform, Ansible, Jenkins), Harish is an expert in a multitude of technologies. 📚
These days, his focus is on the fascinating world of DevOps & how it can transform the way we do things! 🚀