Change-Id: I6048ebc6243ebc8630caceeff787370b9725a152
15 KiB
HAProxy Unified Gateway
A Kubernetes Gateway API controller powered by HAProxy. HAProxy Unified Gateway (HUG) implements the Gateway API specification to provide advanced traffic management capabilities.
Introduction
This chart bootstraps a HAProxy Unified Gateway deployment/daemonset on a Kubernetes cluster using the Helm package manager.
Prerequisites
- Kubernetes 1.26+
- Helm 3.6+ (recommended 3.7+)
Before you begin
Setting up a Kubernetes Cluster
The quickest way to setup a Kubernetes cluster is with Azure Kubernetes Service, AWS Elastic Kubernetes Service or Google Kubernetes Engine using their respective quick-start guides.
For setting up Kubernetes on other cloud platforms or bare-metal servers refer to the Kubernetes getting started guide.
Install Helm
Get the latest Helm release.
Adding Helm chart repo
Once you have Helm installed, add the haproxytech Chart Repository as follows:
helm repo add haproxytech https://haproxytech.github.io/helm-charts
helm repo update
Installing the chart
To install the chart with Helm v3 as my-release deployment:
helm install my-release haproxytech/haproxy-unified-gateway
Installing with unique name
To auto-generate controller and its resources names when installing, use the following:
helm install haproxytech/haproxy-unified-gateway \
--generate-name
Installing from a private registry
To install the chart using a private registry for controller into a separate namespace prod.
NOTE: Helm v3 requires namespace to be precreated (eg. with kubectl create namespace prod)
helm install my-release haproxytech/haproxy-unified-gateway \
--namespace prod \
--set controller.image.tag=SOMETAG \
--set controller.imagePullSecrets[0].name=my-pull-secret
Using values from YAML file
As opposed to using many --set invocations, much simpler approach is to define value overrides in a separate YAML file and specify them when invoking Helm:
myhug.yaml:
controller:
kind: DaemonSet
service:
type: LoadBalancer
annotations:
service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true"
service.beta.kubernetes.io/aws-load-balancer-internal: 0.0.0.0/0
And invoking Helm becomes:
helm install my-release -f myhug.yaml haproxytech/haproxy-unified-gateway
Installing as DaemonSet
Default controller mode is Deployment, but it is possible to use DaemonSet as well:
helm install my-release haproxytech/haproxy-unified-gateway \
--set controller.kind=DaemonSet
Installing with host networking (DaemonSet)
When using DaemonSet mode, you can enable host networking and host ports:
helm install my-release haproxytech/haproxy-unified-gateway \
--set controller.kind=DaemonSet \
--set controller.daemonset.useHostNetwork=true \
--set controller.dnsPolicy=ClusterFirstWithHostNet
Installing with service annotations
On some environments like EKS and GKE there might be a need to pass service annotations. Syntax can become a little tedious however:
helm install my-release haproxytech/haproxy-unified-gateway \
--set controller.service.type=LoadBalancer \
--set controller.service.annotations."service\.beta\.kubernetes\.io/aws-load-balancer-internal"="0.0.0.0/0" \
--set controller.service.annotations."service\.beta\.kubernetes\.io/aws-load-balancer-cross-zone-load-balancing-enabled"="true"
NOTE: With helm --set it is needed to put quotes and escape dots in the annotation key and commas in the value string.
Installing with Horizontal Pod Autoscaler (HPA)
HPA automatically scales number of replicas in Deployment and adjusts replica count for the controller:
helm install my-release haproxytech/haproxy-unified-gateway \
--set controller.autoscaling.enabled=true
Enabling Prometheus monitoring
HUG exposes two separate metrics endpoints:
stat(port 31024) — HAProxy native metrics (haproxy_*prefix): connections, request rates, backend health, latency, error codesmetrics(port 31060) — HUG controller metrics (hug_*prefix): event batch processing, config generation, cert/map operations, HAProxy reloads
The chart supports both ServiceMonitor and PodMonitor for Prometheus Operator integration. These are mutually exclusive — enable only one. By default, both endpoints are scraped.
Note: Requires Prometheus Operator installed in the cluster. The monitoring.coreos.com/v1 API must be available.
Metrics authentication
The controller metrics endpoint (metrics port) supports three authentication modes via controller.metricsAuth:
| Mode | Default | Protocol | Description |
|---|---|---|---|
kube-rbac |
yes | HTTPS | Kubernetes TokenReview/SubjectAccessReview — Prometheus authenticates with its ServiceAccount token |
none |
HTTP | No authentication | |
basic |
HTTPS | HTTP Basic Authentication with username/password |
Default setup (kube-rbac)
By default the chart uses kube-rbac authentication. The controller serves metrics over HTTPS and validates bearer tokens via the Kubernetes API. To set it up:
Step 1. Create a ClusterRole that grants access to the /metrics endpoint and bind it to the Prometheus ServiceAccount:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: hug-metrics-reader
rules:
- nonResourceURLs: ["/metrics"]
verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: hug-metrics-reader
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: hug-metrics-reader
subjects:
- kind: ServiceAccount
name: prometheus # adjust to your Prometheus SA name
namespace: monitoring # adjust to your Prometheus namespace
Step 2. Enable the ServiceMonitor (or PodMonitor). The default endpoints are pre-configured for kube-rbac — stat uses plain HTTP, metrics uses HTTPS with the Prometheus pod's ServiceAccount token:
controller:
serviceMonitor:
enabled: true
extraLabels:
release: prometheus # match your Prometheus serviceMonitorSelector
That's it. The default values.yaml endpoints already include the correct HTTPS + bearer token configuration for the metrics port:
# Default endpoints (already set in values.yaml):
endpoints:
- port: stat
path: /metrics
scheme: http
interval: 30s
- port: metrics
path: /metrics
scheme: https
interval: 30s
tlsConfig:
insecureSkipVerify: true
bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
Using no authentication
To disable metrics authentication:
controller:
metricsAuth: none
serviceMonitor:
enabled: true
endpoints:
- port: stat
path: /metrics
scheme: http
interval: 30s
- port: metrics
path: /metrics
scheme: http
interval: 30s
Using basic authentication
controller:
metricsAuth: basic
extraArgs:
- --metrics-basic-auth-user=prometheus
- --metrics-basic-auth-password=secret
serviceMonitor:
enabled: true
endpoints:
- port: stat
path: /metrics
scheme: http
interval: 30s
- port: metrics
path: /metrics
scheme: https
interval: 30s
tlsConfig:
insecureSkipVerify: true
basicAuth:
username:
name: hug-metrics-basic-auth
key: username
password:
name: hug-metrics-basic-auth
key: password
Using PodMonitor instead of ServiceMonitor
PodMonitor scrapes pods directly without creating an extra metrics Service. Replace serviceMonitor with podMonitor in any of the examples above:
controller:
podMonitor:
enabled: true
extraLabels:
release: prometheus
Configuring HugConf
The chart creates a HugConf custom resource for controller configuration. You can customize logging, and optionally reference Global and Defaults custom resources:
hugconf:
logging:
defaultLevel: Debug
categoryLevelList:
- category: "gate"
level: "Debug"
- category: "k8s"
level: "Info"
# Reference a Global CR for HAProxy global section customization
globalRef:
group: gate.v3.haproxy.org
kind: Global
name: global
# Reference a Defaults CR for HAProxy defaults section customization
defaultsRef:
group: gate.v3.haproxy.org
kind: Defaults
name: haproxytech
The Global and Defaults CRDs are automatically installed by the CRD job. When a globalRef or defaultsRef is set, the controller uses the referenced CR to configure the HAProxy global/defaults sections. When removed, built-in defaults are restored.
Adding extra ports
By default the chart exposes four container ports: http (31080), https (31443), stat (31024) and metrics (31060). Additional ports can be added in two places:
controller.containerPort— exposes the port on the container (pod spec)controller.service.extraPorts— exposes the port on the Service
Using --set flags
To add a container port only (e.g. for a sidecar or internal use):
helm install my-release haproxytech/haproxy-unified-gateway \
--set controller.containerPort.custom=8080
To also expose it on the Service:
helm install my-release haproxytech/haproxy-unified-gateway \
--set controller.containerPort.custom=8080 \
--set controller.service.extraPorts[0].name=custom \
--set controller.service.extraPorts[0].port=8080 \
--set controller.service.extraPorts[0].targetPort=8080 \
--set controller.service.extraPorts[0].protocol=TCP
Using a values file
For multiple extra ports, a values file is cleaner:
controller:
containerPort:
http: 31080
https: 31443
stat: 31024
custom: 8080
grpc: 9090
service:
extraPorts:
- name: custom
port: 8080
targetPort: 8080
protocol: TCP
- name: grpc
port: 9090
targetPort: 9090
protocol: TCP
DaemonSet with host ports
For DaemonSet mode with host ports, also add matching entries in controller.daemonset.hostPorts:
controller:
kind: DaemonSet
containerPort:
http: 31080
https: 31443
stat: 31024
custom: 8080
daemonset:
useHostPort: true
hostPorts:
http: 80
https: 443
stat: 1024
custom: 8080
service:
extraPorts:
- name: custom
port: 8080
targetPort: 8080
protocol: TCP
Passing extra arguments
Additional controller flags can be passed via extraArgs:
controller:
extraArgs:
- --controller-name=gate.haproxy.org/hug
- --namespaces=default,production
- --leader-election-enabled
Available controller flags
| Flag | Default | Description |
|---|---|---|
--controller-name |
gate.haproxy.org/hug |
spec.controllerName GatewayClass selector |
--namespaces |
Comma-separated list of namespaces to monitor | |
--ipv4-bind-address |
IPv4 address to bind to | |
--ipv6-bind-address |
IPv6 address to bind to | |
--disable-ipv4 |
false |
Disable IPv4 support |
--disable-ipv6 |
false |
Disable IPv6 support |
--stats-port |
1024 |
Port for HAProxy stats |
--controller-port |
31060 |
Port for controller metrics (prometheus) |
--log-type |
json |
Log output type (text or json) |
--sync-period |
0 |
Period at which the controller computes HAProxy configuration (e.g. 5s, 1m) |
--startup-sync-period |
0 |
Startup period for HAProxy config computation |
--cache-resync-period |
0 |
Controller-runtime manager cache SyncPeriod (defaults to 10 hours if not set) |
--leader-election-enabled |
false |
Enable leader election |
--add-stats-port |
true |
Add stats port bind to existing stats frontend |
--metrics-auth |
none |
Metrics endpoint auth mode: none, kube-rbac, basic |
--metrics-basic-auth-user |
Basic auth username (when --metrics-auth=basic) |
|
--metrics-basic-auth-password |
Basic auth password (when --metrics-auth=basic) |
Note: The --hugconf-crd flag is set automatically by the chart via the hugconfCrd helper. The --job-check-crd and --job-gwapi flags are used internally by the CRD/Gateway API installation jobs.
Installing with KEDA autoscaling
KEDA provides event-driven autoscaling. It is mutually exclusive with HPA — when KEDA is enabled, HPA is automatically disabled even if autoscaling.enabled is set to true.
controller:
keda:
enabled: true
minReplicas: 2
maxReplicas: 20
pollingInterval: 30
cooldownPeriod: 300
restoreToOriginalReplicaCount: false
scaledObject:
annotations: {}
triggers:
- type: prometheus
metadata:
serverAddress: http://<prometheus-host>:9090
metricName: haproxy_process_idle_time_percent
threshold: '50'
query: avg(100-avg_over_time(haproxy_process_idle_time_percent{job="haproxy-unified-gateway"}[2m]))
Optional advanced configuration:
controller:
keda:
enabled: true
# ...triggers, minReplicas, maxReplicas...
fallback:
failureThreshold: 3
replicas: 5
horizontalPodAutoscalerConfig:
behavior:
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Pods
value: 1
periodSeconds: 300
Disabling CRD/Gateway API installation jobs
By default, the chart includes Helm hook jobs that install HUG CRDs and Gateway API CRDs. To disable them:
helm install my-release haproxytech/haproxy-unified-gateway \
--set crdjob.enabled=false \
--set gwapijob.enabled=false
Upgrading the chart
To upgrade the my-release deployment:
helm upgrade my-release haproxytech/haproxy-unified-gateway
Uninstalling the chart
To uninstall/delete the my-release deployment:
helm delete my-release
Debugging
It is possible to generate a set of YAML files for testing/debugging:
helm install my-release haproxytech/haproxy-unified-gateway \
--debug \
--dry-run
Contributing
We welcome all contributions. Please refer to guidelines on how to make a contribution.
