Autoscaler Deployment
Complete deployment guide for the Productify Autoscaler components.
Components
The Autoscaler consists of two components:
- Nomadscaler Plugin - Go-based Nomad Autoscaler strategy plugin (
productify-scaler) - Optimizer Service - Python-based MILP optimization service
Optimizer Deployment
The Optimizer is configured through a config.ini file (see Optimizer Configuration). The container reads the config path from the CONFIG_PATH environment variable and the listen port from PORT (default: 8015).
Docker
docker run -d \
--name optimizer \
-p 8015:8015 \
-v $PWD/config.ini:/app/optimizer/config.ini:ro \
ghcr.io/productifyfw/optimizer:latestDocker Compose
services:
optimizer:
image: ghcr.io/productifyfw/optimizer:latest
ports:
- "8015:8015"
environment:
CONFIG_PATH: /app/config.ini
volumes:
- ./config.ini:/app/config.ini:ro
restart: unless-stoppedNomad
job "optimizer" {
datacenters = ["dc1"]
type = "service"
group "optimizer" {
count = 2 # For high availability
network {
port "http" {
to = 8015
}
}
service {
name = "optimizer"
port = "http"
tags = ["productify", "autoscaler"]
check {
type = "http"
path = "/health"
interval = "10s"
timeout = "2s"
}
}
task "server" {
driver = "docker"
config {
image = "ghcr.io/productifyfw/optimizer:latest"
ports = ["http"]
}
env {
CONFIG_PATH = "${NOMAD_TASK_DIR}/config.ini"
PORT = "${NOMAD_PORT_http}"
}
template {
data = <<EOF
[main]
loglevel=info
api_loglevel=warning
only_test_data=false
enable_test_metrics=false
prometheus_url=http://prometheus:9090
port={{ env "NOMAD_PORT_http" }}
token=<optimizer-token>
EOF
destination = "${NOMAD_TASK_DIR}/config.ini"
}
resources {
cpu = 1000
memory = 1024
}
}
}
}Future Support
Kubernetes deployment manifests will be added in a future release.
Nomadscaler Plugin Deployment
Installation
# Build plugin (produces ./bin/productify-scaler and the productify/scaler:local Docker image)
cd nomadscaler
./build.sh
# Install to Nomad Autoscaler
sudo cp ./bin/productify-scaler /opt/nomad-autoscaler/plugins/
sudo chmod +x /opt/nomad-autoscaler/plugins/productify-scalerAlternatively, use the prebuilt image ghcr.io/productifyfw/nomadscaler:latest, which bundles the Nomad Autoscaler agent with the plugin preinstalled at /plugins/productify-scaler.
Nomad Autoscaler Configuration
# autoscaler.hcl
plugin_dir = "/opt/nomad-autoscaler/plugins"
nomad {
address = "http://localhost:4646"
}
apm "nomad" {
driver = "nomad-apm"
config = {
address = "http://localhost:4646"
}
}
strategy "productify-scaler" {
driver = "productify-scaler"
config = {
optimizer_url = "http://optimizer:8015"
optimizer_token = "<optimizer-token>"
}
}
http {
bind_address = "0.0.0.0"
bind_port = 8080
}
log {
level = "INFO"
}Scaling Policy
Scaling policies are embedded in the target job's scaling block and use the productify-scaler strategy:
group "frontend" {
count = 3
scaling {
enabled = true
min = 2
max = 20
policy {
evaluation_interval = "10s"
cooldown = "30s"
check "productify-scale-check" {
source = "nomad-apm"
query = "avg_cpu-allocated"
strategy "productify-scaler" {
min = 2
max = 20
metric_app_name = "web-app"
cache_size = 10 # Number of cached replica values (default: 10)
}
}
}
}
# ... tasks
}optimizer_url and optimizer_token can be set either in the agent-level strategy block (as above) or per check in the strategy "productify-scaler" config. See Scaling Policies for the full parameter reference.
Running Nomad Autoscaler
nomad-autoscaler agent \
-config=/etc/nomad-autoscaler/autoscaler.hcl \
-plugin-dir=/opt/nomad-autoscaler/pluginsComplete Stack Deployment
Docker Compose
services:
optimizer:
image: ghcr.io/productifyfw/optimizer:latest
ports:
- "8015:8015"
environment:
CONFIG_PATH: /app/config.ini
volumes:
- ./config.ini:/app/config.ini:ro
restart: unless-stopped
nomad-autoscaler:
image: ghcr.io/productifyfw/nomadscaler:latest
volumes:
- ./autoscaler.hcl:/etc/autoscaler.hcl:ro
command: agent -config=/etc/autoscaler.hcl
environment:
NOMAD_ADDR: http://nomad:4646
depends_on:
- optimizer
restart: unless-stoppedNomad Job (Complete)
A complete reference job that runs the Nomad Autoscaler agent, the Optimizer, and a Prometheus instance in a single group is available at autoscaler/nomadscaler/config/autoscaler.hcl. The abbreviated version:
job "autoscaler" {
datacenters = ["dc1"]
group "autoscaler" {
count = 1
network {
port "optimizer_port" {}
port "optimizer_port_prometheus" {}
}
task "autoscaler" {
driver = "docker"
config {
image = "ghcr.io/productifyfw/nomadscaler:latest"
command = "nomad-autoscaler"
args = ["agent", "-config", "${NOMAD_TASK_DIR}/config.hcl"]
}
template {
data = <<EOF
plugin_dir = "/plugins"
nomad {
address = "http://{{env "attr.unique.network.ip-address" }}:4646"
}
apm "nomad" {
driver = "nomad-apm"
config = {
address = "http://{{env "attr.unique.network.ip-address" }}:4646"
}
}
strategy "productify-scaler" {
driver = "productify-scaler"
config = {
{{with nomadVar "nomad/jobs/autoscaler/autoscaler/autoscaler" -}}
optimizer_token = "{{.TOKEN}}"
{{- end}}
optimizer_url = "http://{{env "NOMAD_IP_optimizer_port"}}:{{env "NOMAD_PORT_optimizer_port"}}"
}
}
EOF
destination = "${NOMAD_TASK_DIR}/config.hcl"
}
resources {
cpu = 200
memory = 256
}
}
task "optimizer" {
driver = "docker"
config {
image = "ghcr.io/productifyfw/optimizer:latest"
ports = ["optimizer_port"]
}
env {
CONFIG_PATH = "${NOMAD_TASK_DIR}/config.ini"
PORT = "${NOMAD_PORT_optimizer_port}"
}
template {
data = <<EOF
[main]
loglevel=info
api_loglevel=warning
only_test_data=false
enable_test_metrics=false
prometheus_url=http://{{env "NOMAD_IP_optimizer_port_prometheus"}}:{{env "NOMAD_PORT_optimizer_port_prometheus"}}
port={{env "NOMAD_PORT_optimizer_port"}}
{{ with nomadVar "nomad/jobs/autoscaler/autoscaler/optimizer" -}}
token={{.TOKEN}}
{{- end }}
EOF
destination = "${NOMAD_TASK_DIR}/config.ini"
}
resources {
cpu = 768
memory = 512
}
}
# A prometheus task also runs in this group; see
# autoscaler/nomadscaler/config/autoscaler.hcl for the full job.
}
}Configuration
Optimizer Configuration
See Optimizer Configuration for details.
Key config.ini settings ([main] section):
port- HTTP listen port (default: 8015)prometheus_url- Prometheus server to read metrics fromtoken- Shared token required on/optimizerequestsloglevel/api_loglevel- Logging levelsonly_test_data- Use synthetic test data instead of Prometheusenable_test_metrics- Serve a synthetic test-metrics endpoint (port set bymetrics_port, default: 8017)
Plugin Configuration
See Scaling Policies for details.
Key settings:
optimizer_url- Optimizer service URLoptimizer_token- Token for authenticating with the Optimizermin/max- Replica boundsmetric_app_name- Application name used in metric lookupscache_size- Number of cached replica values (default: 10)evaluation_interval- How often to evaluate (policy-level)cooldown- Minimum time between scaling actions (policy-level)
Health Checks
Optimizer Health
curl http://localhost:8015/healthNomad Autoscaler Status
curl http://localhost:8080/v1/healthPolicy Status
nomad scaling policy listMonitoring
Optimizer Test Metrics
The Optimizer does not expose its own Prometheus metrics endpoint. When enable_test_metrics=true is set in config.ini, it serves a synthetic test-metrics endpoint (default port 8017) that generates load-pattern data for development and testing.
Nomad Autoscaler Metrics
With telemetry { prometheus_metrics = true } in the agent configuration, the Nomad Autoscaler exposes Prometheus metrics on its HTTP endpoint:
curl http://localhost:8080/v1/metrics?format=prometheusTroubleshooting
Optimizer Not Responding
Check:
- Service is running
- Port 8015 (or configured port) is accessible
- Health endpoint returns 200
- Logs for errors
Plugin Not Loading
Verify:
- Plugin binary in correct directory
- Binary has execute permissions
- Plugin version matches autoscaler version
- Logs show plugin registration
No Scaling Actions
Debug:
- Policy is enabled
- Metrics are being collected
- Optimizer is reachable
- Cooldown period hasn't been triggered
- Min/max bounds allow scaling