HAProxy can act as a Kubernetes Ingress controller, but it’s not a built-in solution and requires a bit of manual setup.
Let’s see HAProxy in action managing traffic for a simple web application deployed in Kubernetes.
Here’s a basic deployment and service for a hypothetical myapp:
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-deployment
spec:
replicas: 2
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp-container
image: nginxdemos/hello:plain-text
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:
selector:
app: myapp
ports:
- protocol: TCP
port: 80
targetPort: 80
Now, to expose this service using HAProxy as an Ingress controller, we need to:
- Install HAProxy: This is typically done by deploying HAProxy itself as a Pod within your Kubernetes cluster. You’d usually use a Docker image for HAProxy.
- Configure HAProxy: This is the core part. HAProxy needs to be configured to listen for incoming traffic and route it to your
myapp-service. This configuration is usually mounted into the HAProxy pod as a ConfigMap. - Expose HAProxy: The HAProxy pod needs to be accessible from outside the cluster. This is often done using a
NodePortorLoadBalancerservice pointing to the HAProxy pod.
Here’s a simplified example of a HAProxy configuration (haproxy.cfg) that routes traffic for myapp.example.com to our myapp-service:
global
log /dev/log local0
log /dev/log local1 notice
chroot /var/lib/haproxy
stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
stats timeout 30s
user haproxy
group haproxy
daemon
defaults
log global
mode http
option httplog
option dontlognull
timeout connect 5000
timeout client 50000
timeout server 50000
frontend http_frontend
bind *:80
acl host_myapp hdr(host) -i myapp.example.com
use_backend myapp_backend if host_myapp
backend myapp_backend
balance roundrobin
# This needs to resolve to the Kubernetes service IP and port
# In a real scenario, you'd use a tool to dynamically update this
# or configure it to point to the Kubernetes service DNS name.
# For demonstration, let's assume the service IP is 10.43.0.10 and port 80
server myapp_server 10.43.0.10:80 check
To make this dynamic and Kubernetes-native, you’d typically use a controller that watches Kubernetes Ingress resources and generates and updates this haproxy.cfg file on the fly. This controller would also programmatically fetch the IP addresses of the myapp-service’s pods.
Here’s how you’d deploy HAProxy and its configuration as Kubernetes resources:
1. HAProxy ConfigMap:
apiVersion: v1
kind: ConfigMap
metadata:
name: haproxy-config
data:
haproxy.cfg: |
global
log /dev/log local0
log /dev/log local1 notice
chroot /var/lib/haproxy
stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
stats timeout 30s
user haproxy
group haproxy
daemon
defaults
log global
mode http
option httplog
option dontlognull
timeout connect 5000
timeout client 50000
timeout server 50000
frontend http_frontend
bind *:80
acl host_myapp hdr(host) -i myapp.example.com
use_backend myapp_backend if host_myapp
backend myapp_backend
balance roundrobin
# This will be dynamically updated by the controller
server myapp_server 10.43.0.10:80 check
2. HAProxy Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: haproxy-ingress
spec:
replicas: 1
selector:
matchLabels:
app: haproxy-ingress
template:
metadata:
labels:
app: haproxy-ingress
spec:
containers:
- name: haproxy
image: haproxy:2.4 # Use an appropriate HAProxy image
ports:
- containerPort: 80
name: http
- containerPort: 1936 # For stats
name: stats
volumeMounts:
- name: haproxy-config-volume
mountPath: /usr/local/etc/haproxy/haproxy.cfg
subPath: haproxy.cfg
volumes:
- name: haproxy-config-volume
configMap:
name: haproxy-config
3. HAProxy Service (to expose it):
apiVersion: v1
kind: Service
metadata:
name: haproxy-ingress-service
spec:
selector:
app: haproxy-ingress
ports:
- protocol: TCP
port: 80
targetPort: 80
name: http
- protocol: TCP
port: 1936
targetPort: 1936
name: stats
type: LoadBalancer # Or NodePort
Once HAProxy is running and exposed, you’d create an Ingress resource to tell HAProxy how to route traffic:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp-ingress
spec:
rules:
- host: myapp.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp-service
port:
number: 80
The real magic of a Kubernetes Ingress controller is its ability to watch for these Ingress objects and programmatically update the HAProxy configuration and backend server lists based on them. You’re not manually editing haproxy.cfg each time you create an Ingress. Instead, a separate controller component (often running alongside HAProxy or as a separate deployment) monitors the Kubernetes API for Ingress resources and translates them into HAProxy configurations.
The most surprising true thing about using HAProxy as an Ingress controller in Kubernetes is that there isn’t a single, official "Kubernetes HAProxy Ingress Controller" provided by the Kubernetes project itself. Instead, the community has developed various solutions, with the most popular one being ingress-nginx (which uses Nginx, not HAProxy) and a dedicated HAProxy Ingress controller project. These controllers are essentially custom applications that run within your cluster, watch Kubernetes API events, and dynamically reconfigure HAProxy.
The primary challenge is ensuring the HAProxy configuration is kept up-to-date with the state of your Kubernetes services and pods. This dynamic re-configuration is handled by a controller process that monitors the Kubernetes API for changes to Ingress resources, Endpoints (which represent the IPs and ports of your service’s pods), and Services. When changes are detected, the controller regenerates the haproxy.cfg file and signals HAProxy to reload its configuration without dropping active connections.
The next concept you’ll likely encounter is how to handle TLS termination and advanced HAProxy features like sticky sessions or different load balancing algorithms within the Kubernetes Ingress framework.