Ingress Overview
Layer 7 Load Balancing Requirements
When clients access Kubernetes services, Layer 4 load balancers cannot terminate SSL sessions. This creates two significant issues: clients must establish direct SSL connections with backend pods, and if a request is routed to a different server after the SSL session is established, the session must be renegotiated.
For internal networks, we can safely terminate SSL at the load balancer and use plain HTTP internally. However, Kubernetes iptables and IPVS-based Services operate at Layer 4, meaning applications requiring HTTPS must terminate SSL in each pod.
To address this, Kubernetes employs a specialized architecture:
- Backend pods serve plaintext HTTP
- A dedicated proxy pod (running nginx, haproxy, or similar) terminates SSL
- The proxy pod communicates directly with backend pods
- Traffic flow:
Client → LB → NodePort → Service → SSL Termination Pod → Backend Pod
Network Namespace Sharing
Containers can share the host's network namespace by setting hostNetwork: true. This allows the pod to bind to host ports directly, making it accessible without port mapping. However, this limits the pod to one instance per node.
DaemonSet for High Availability
Using a DaemonSet controller ensures the SSL termination proxy runs on every node. This provides:
- Automatic failover if a node fails
- Traffic routing to any available node
- Resource efficiency through node selection (using taints and tolerasions)
For large clusters, designate specific nodes for ingress traffic by applying taints and using node affinity.
Ingress Controller Options
Ingress Controllers are standalone applications providing Layer 7 proxy capabilities. Common options include:
| Controller | Characteristics |
|---|---|
| nginx | Modified for Kubernetes, widely adopted |
| Traefik | Designed for microservices, dynamic configuration |
| Envoy | Popular in service mesh architectures |
| HAProxy | Less commonly used |
How Ingress Works
When multiple services need routing through a single proxy:
- Each service uses a selector to group backend pods
- Ingress defines routing rules (virtual hosts or URL paths)
- Ingress Controller watches API server for changes
- Configuration updates are injected into the proxy without manual reloads
The key difference from traditional nginx is that Ingress monitors pod changes and dynamically updates upstream configuration. Traefik and Envoy handle this natively, while nginx requires configuration reloads on changes.
Ingress Resource Definition
Ingress is a standard Kubernetes resource with the following structure:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: example-ingress
spec:
backend: # Optional default backend
serviceName: default-service
servicePort: 80
rules: # Routing rules (host-based or path-based)
- host: example.com
http:
paths:
- path: /
backend:
serviceName: frontend
servicePort: 80
tls: # TLS configuration
- hosts:
- secure.example.com
secretName: tls-secret
Key fields:
spec.backend: Default backend for unmatched requestsspec.rules: Host and path-based routing rulesspec.tls: TLS termination configurationbackend.serviceName: References a Service for pod discoverybackend.servicePort: Target port on the backend service
Deploying Ingress-Nginx
Component Overview
The deployment consists of several YAML files:
| File | Purpose |
|---|---|
| namespace.yaml | Creates ingress-nginx namespace |
| configmap.yaml | Stores nginx configuration variables |
| rbac.yaml | Defines RBAC permissions for the controller |
| default-backend.yaml | Handles 404 for unmatched requests |
| mandatory.yaml | Core ingress-controller deployment |
Deployment Steps
Download the deployment files:
# Latest version
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/mandatory.yaml
# Specific version
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.20.0/deploy/mandatory.yaml
Download all required files:
for file in namespace.yaml configmap.yaml rbac.yaml tcp-services-configmap.yaml \
udp-services-configmap.yaml default-backend.yaml mandatory.yaml; do
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.17.1/deploy/$file
done
Apply all configurations:
kubectl apply -f ./
Verify deployment:
kubectl get pods -n ingress-nginx
Exposing Ingress Controller
For external access, create a NodePort Service:
apiVersion: v1
kind: Service
metadata:
name: ingress-nginx
namespace: ingress-nginx
spec:
type: NodePort
ports:
- name: http
port: 80
targetPort: 80
protocol: TCP
nodePort: 30080
- name: https
port: 443
targetPort: 443
protocol: TCP
nodePort: 30443
selector:
app: ingress-nginx
Apply and verify:
kubectl apply -f service-nodeport.yaml
Test by accessing a non-existent path:
curl http://<node-ip>:30080/
Expected response: default backend - 404
Example Application with Ingress
Create Backend Deployment
apiVersion: v1
kind: Service
metadata:
name: demo-app
namespace: default
spec:
selector:
app: demo
version: stable
ports:
- name: http
targetPort: 80
port: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-app-deploy
namespace: default
spec:
replicas: 3
selector:
matchLabels:
app: demo
version: stable
template:
metadata:
labels:
app: demo
version: stable
spec:
containers:
- name: demo-app
image: ikubernetes/myapp:v2
ports:
- name: http
containerPort: 80
Define Ingress Rules
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: demo-ingress
namespace: default
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host: demo.example.com
http:
paths:
- path: /
backend:
serviceName: demo-app
servicePort: 80
Deploy and verify:
kubectl apply -f demo-app.yaml
kubectl apply -f ingress-demo.yaml
kubectl get ingress -n default
kubectl describe ingress demo-ingress -n default
Add the host entry to /etc/hosts:
<node-ip> demo.example.com
Access the application:
curl http://demo.example.com:30080/
Configuring HTTPS with Ingress
Create Backend Services
Deploy Tomcat pods with associated Service:
apiVersion: v1
kind: Service
metadata:
name: webapp
namespace: default
spec:
selector:
app: webapp
environment: production
ports:
- name: http
targetPort: 8080
port: 8080
- name: ajp
targetPort: 8009
port: 8009
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: webapp-deploy
namespace: default
spec:
replicas: 3
selector:
matchLabels:
app: webapp
environment: production
template:
metadata:
labels:
app: webapp
environment: production
spec:
containers:
- name: webapp
image: tomcat:8.5.32-jre8-alpine
ports:
- name: http
containerPort: 8080
- name: ajp
containerPort: 8009
Define Ingres with Host-Based Routing
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: webapp-ingress
namespace: default
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host: webapp.example.com
http:
paths:
- path: /
backend:
serviceName: webapp
servicePort: 8080
Deploy:
kubectl apply -f webapp-deploy.yaml
kubectl apply -f ingress-webapp.yaml
Generate TLS Certificate
Create private key and certificate:
openssl genrsa -out server.key 2048
openssl req -new -x509 -key server.key -out server.crt \
-subj /C=US/ST=California/L=SanFrancisco/O=DevOps/CN=webapp.example.com
Create Kubernetes Secret:
kubectl create secret tls webapp-tls-secret \
--cert=server.crt --key=server.key
kubectl get secret
Configure TLS in Ingress
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: webapp-ingress-tls
namespace: default
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
tls:
- hosts:
- webapp.example.com
secretName: webapp-tls-secret
rules:
- host: webapp.example.com
http:
paths:
- path: /
backend:
serviceName: webapp
servicePort: 8080
Deploy and test:
kubectl apply -f ingress-tls.yaml
curl https://webapp.example.com:30443/ -k
Alternative Deployment: Host Network Mode
For direct host binding without NodePort:
- Modify deployment to use DaemonSet
- Enable host network:
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: ingress-nginx
namespace: ingress-nginx
spec:
selector:
matchLabels:
app: ingress-nginx
template:
metadata:
labels:
app: ingress-nginx
spec:
hostNetwork: true
containers:
- name: nginx-ingress-controller
image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.20.0
securityContext:
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
args:
- /nginx-ingress-controller
- --configmap=$(POD_NAMESPACE)/nginx-configuration
This allows direct access to ingress on host ports (80 and 443) without Service exposure.
Mirror Repository Reference
For environments with limited access to official registries:
| Original Image | Mirror |
|---|---|
| quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.24.1 | linuxhub/nginx-ingress-controller:0.24.1 |
| k8s.gcr.io/kubernetes-dashboard-amd64:v1.8.3 | linuxhub/kubernetes-dashboard-amd64:v1.8.3 |
| gcr.io/kubernetes-helm/tiller:v2.11.0 | linuxhub/tiller:v2.11.0 |
| k8s.gcr.io/elasticsearch:v5.6.4 | linuxhub/elasticsearch:v5.6.4 |
| k8s.gcr.io/coredns:1.0.6 | linuxhub/coredns:1.0.6 |
Replace images in YAML files before deployment to ensure successful installation.