Multi-instance deployment improves application stability but can leave resources idle and increase cluster costs. Manual scaling is labor-intensive and slow to respond. You can use Nginx Ingress to apply horizontal pod autoscaling (HPA) across multiple applications, dynamically adjusting Pod replicas based on load to maintain stability and responsiveness while optimizing resource use and cost. This topic explains how to implement multi-application HPA with Nginx Ingress.
Prerequisites
Ensure that you have:
Alibaba Cloud Prometheus is deployed in your cluster.
The ack-alibaba-cloud-metrics-adapter add-on is deployed with
prometheus.urlconfigured.Apache Benchmark (
ab) is installed.
How it works
An Ingress forwards external requests to a Service, which routes them to the matching pod. The NGINX Ingress Controller records per-service request counts in the nginx_ingress_controller_requests Prometheus metric.
HPA cannot consume Prometheus metrics directly — it requires metrics exposed through the Kubernetes external metrics API. The ack-alibaba-cloud-metrics-adapter bridges this gap by processing nginx_ingress_controller_requests in four steps:
Step | Field | Role | Value used here |
1. Discovery |
| Identifies which Prometheus metric to expose |
|
2. Association |
| Specifies whether this metric is namespace-scoped |
|
3. Naming |
| Renames the metric in the external API | Strips |
4. Querying |
| Defines the PromQL template used to compute the value |
|
The adapter fills these metricsQuery template variables at query time:
<<.Series>>— replaced with the matched Prometheus series name (nginx_ingress_controller_requests)<<.LabelMatchers>>— replaced with the label selectors from the HPA spec (for example,service="sample-app")
Each HPA uses selector.matchLabels.service to filter this metric to a single application, keeping sample-app and test-app scaling independent.
Step 1: Create applications and services
Create two Deployments and their corresponding Services.
Create
nginx1.yamlwith the following content:Apply the manifest:
kubectl apply -f nginx1.yamlCreate
nginx2.yamlwith the following content:Apply the manifest:
kubectl apply -f nginx2.yaml
Step 2: Create an Ingress
Create
ingress.yamlwith the following content:Field
Description
hostThe domain name for Service access. Set to
test.example.comin this example.pathThe URL path. Requests matching this path route to the corresponding Service.
backendThe target Service name and port for each path.
/routes tosample-app;/homeroutes totest-app.Apply the manifest:
kubectl apply -f ingress.yamlVerify the Ingress is running:
kubectl get ingress -o wideExpected output:
NAME CLASS HOSTS ADDRESS PORTS AGE test-ingress nginx test.example.com 10.XX.XX.10 80 55sThe NGINX Ingress Controller routes
test.example.com/tosample-appandtest.example.com/hometotest-app. Both request streams are recorded in thenginx_ingress_controller_requestsPrometheus metric, labeled by service name.
Step 3: Convert Prometheus metrics to HPA-compatible metrics
HPA reads metrics from the Kubernetes external metrics API, not from Prometheus directly. The ack-alibaba-cloud-metrics-adapter translates Prometheus metrics into this format via rules in the adapter-config ConfigMap.
Modify the adapter-config file
Log on to the ACK console. In the left navigation pane, click Clusters.
On the Clusters page, find the cluster you want and click its name. In the left-side pane, choose Applications > Helm.
On the Helm page, click ack-alibaba-cloud-metrics-adapter. In the Resource section, click adapter-config, then click Edit YAML in the upper-right corner.
Replace the existing rules with the following, then click OK:
All adapter configuration options are documented in Horizontal pod autoscaling based on Alibaba Cloud Prometheus metrics.
rules: - metricsQuery: sum(rate(<<.Series>>{<<.LabelMatchers>>}[2m])) name: as: ${1}_per_second matches: ^(.*)_requests resources: namespaced: false seriesQuery: nginx_ingress_controller_requests
Verify the metric is available
Confirm that the adapter serves the converted metric:
kubectl get --raw "/apis/external.metrics.k8s.io/v1beta1/namespaces/*/nginx_ingress_controller_per_second" | jq .Expected output:
{
"kind": "ExternalMetricValueList",
"apiVersion": "external.metrics.k8s.io/v1beta1",
"metadata": {},
"items": [
{
"metricName": "nginx_ingress_controller_per_second",
"metricLabels": {},
"timestamp": "2025-07-25T07:56:04Z",
"value": "0"
}
]
}A value of "0" is expected — there is no traffic yet.
Step 4: Create HPAs
Both HPAs use the same nginx_ingress_controller_per_second external metric but filter it to their respective service using selector.matchLabels.service. The adapter passes these labels as <<.LabelMatchers>> into the PromQL query, keeping each application's scaling decisions independent.
Create
hpa.yamlwith the following content:Apply the manifest:
kubectl apply -f hpa.yamlVerify both HPAs are ready:
kubectl get hpaExpected output:
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE sample-hpa Deployment/sample-app 0/30 (avg) 1 10 1 74s test-hpa Deployment/test-app 0/30 (avg) 1 10 1 59mBoth HPAs show
0/30 (avg), meaning the current request rate is below the scale-out threshold of 30 requests per second per pod.
Step 5: Verify autoscaling
Use Apache Benchmark to generate traffic and watch each HPA respond independently.
Send 5,000 requests to the
/homepath (routed totest-app):ab -c 50 -n 5000 test.example.com/homeWatch HPA status in real time:
kubectl get hpa --watchAfter the load,
test-hpascales out whilesample-hpastays at one replica:NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE sample-hpa Deployment/sample-app 0/30 (avg) 1 10 1 22m test-hpa Deployment/test-app 22096m/30 (avg) 1 10 3 80mPress Ctrl+C to stop watching.
Send 5,000 requests to the root path (routed to
sample-app):ab -c 50 -n 5000 test.example.com/Check the HPA status again:
kubectl get hpaExpected output:
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE sample-hpa Deployment/sample-app 27778m/30 (avg) 1 10 2 38m test-hpa Deployment/test-app 0/30 (avg) 1 10 1 96msample-hpascaled out under load, whiletest-hpascaled back in after/hometraffic stopped. Each application scaled independently based on its own traffic.
Next steps
Multi-zone autoscaling: Scale across multiple zones simultaneously for data-intensive, high-availability services.
Custom OS images for elastic scaling: Simplify autoscaling in environments with specific node requirements using custom images.