Skip to content

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

bash
docker run -d \
  --name optimizer \
  -p 8015:8015 \
  -v $PWD/config.ini:/app/optimizer/config.ini:ro \
  ghcr.io/productifyfw/optimizer:latest

Docker Compose

yaml
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

hcl
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

bash
# 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-scaler

Alternatively, 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

hcl
# 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:

hcl
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

bash
nomad-autoscaler agent \
  -config=/etc/nomad-autoscaler/autoscaler.hcl \
  -plugin-dir=/opt/nomad-autoscaler/plugins

Complete Stack Deployment

Docker Compose

yaml
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-stopped

Nomad 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:

hcl
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 from
  • token - Shared token required on /optimize requests
  • loglevel / api_loglevel - Logging levels
  • only_test_data - Use synthetic test data instead of Prometheus
  • enable_test_metrics - Serve a synthetic test-metrics endpoint (port set by metrics_port, default: 8017)

Plugin Configuration

See Scaling Policies for details.

Key settings:

  • optimizer_url - Optimizer service URL
  • optimizer_token - Token for authenticating with the Optimizer
  • min / max - Replica bounds
  • metric_app_name - Application name used in metric lookups
  • cache_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

bash
curl http://localhost:8015/health

Nomad Autoscaler Status

bash
curl http://localhost:8080/v1/health

Policy Status

bash
nomad scaling policy list

Monitoring

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:

bash
curl http://localhost:8080/v1/metrics?format=prometheus

Troubleshooting

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

See Also