Wer sich schonmal mit dem Nomad Autoscaler beschäftigt hat, der weiss, dass es sich dabei um eine Komponente handelt, die separat von Nomad läuft.
Das bedeutet, dass es verschiedene Optionen gibt, den Autoscaler auszurollen. Man kann ihn z. B. genau wie Nomad selbst, als Dienst per systemd starten. Deutlich komfortabler ist es aber, den Autoscaler als Job in Nomad zu deployen, weil er damit natürlich bei Client-Ausfällen einfach auf einem anderen Client neugestartet werden kann.
Da der Autoscaler für Nomad aber eine "externe Komponente" ist, muß er mit allerlei "Geheimnissen" ausgestattet werden, um auf das Nomad-Cluster zugreifen zu können.
Wenn man zusätzlich noch ein Plugin benutzt, um automatisiert Nodes auf der unterliegenden Infrastruktur kreieren und löschen zu können, werden dafür vermutlich auch noch "Geheimnisse" benötigt.
Damit diese sensiblen Informationen nicht direkt im Job-File auftauchen müssen, bietet Nomad die Möglichkeit an, Variablen für den jeweiligen Job im vorhinein zu befüllen.
Standardmässig hat erstmal nur der Job Zugriff auf die dort abgespeicherten Werte. Zusätzlich sollte man den Autoscaler auch in einem eigenen Namespace starten (weil damit auch die HA-Funktion realisiert wird) und mit einer passenden Policy versehen.
In meinem Fall erstreckt sich das Nomad-Cluster ja über mehrere OpenStack-Regionen. Deshalb habe ich auch für jede eine entsprechende Policy erzeugt:
namespace "autoscalerprod1" {
policy = "write"
variables {
path "*" {
capabilities = ["write", "read", "destroy"]
}
}
}
agent {
policy = "write"
}
node {
policy = "read"
}
quota {
policy = "read"
}
Die entsprechenden Namespaces werden vorher z. B. mit nomad namespace apply autoscalerprod1
angelegt. Sie können natürlich auch vorher in HCL-Dateien definiert und dann direkt beim Start von Nomad erzeugt werden.
Die oben definierte Policy wird dann mit nomad acl policy apply autoscalerprod1 autoscalerprod1-policy.hcl
angelegt.
Wenn die existiert, kann damit auch ein Token erzeugt werden, welches die Rechte im Cluster auf den jeweiligen Namespace und die festgelegte Policy festlegt: nomad acl token create -name="autoscalerprod1" -policy=autoscalerprod1 -type=client
. Dieses Token wird dann benötigt, um den Autoscaler am Nomad-Cluster anzumelden.
Es wird aber nicht nur das Token sondern es werden mindestens auch die erforderlichen Zertifikate benötigt, um auf das Cluster zugreifen zu können. Deswegen werden auch diese vorher in Nomad Variablen abgelegt: nomad var put -namespace autoscalerprod1 nomad/jobs/autoscaler-ha-prod1 cacert=@/etc/nomad/certificates/ca.pem clientcert=@/etc/nomad/certificates/cert.pem clientkey=@/etc/nomad/certificates/private_key.pem token=@autoscalerprod1.token
Um die vorher befüllten Variablen im Job-File zu "konsumieren" greift man dann wieder auf die schon bekannte go-Syntax zurück. Im Wesentlichen kann man sie dazu benutzen, Umgebungsvariablen für den Job zu setzen oder mit dem template-Block direkt Dateien zu befüllen, die dann vom Job weiterverwendet werden.
In meinem Autoscaler-Job sieht das dann z. B. so aus:
job "autoscaler-ha-prod1" {
region = "de-west"
datacenters = ["prod1"]
namespace = "autoscalerprod1"
group "autoscaler" {
network {
port "http" {}
}
task "autoscaler_agent" {
driver = "exec"
config {
command = "/usr/local/bin/nomad-autoscaler"
args = [
"agent",
"-plugin-dir=local/nomad-autoscaler/plugins",
"-config=local/nomad-autoscaler/etc",
"-policy-dir=local/nomad-autoscaler/etc/policies",
"-nomad-address=https://127.0.0.1:4646",
"-http-bind-address=${NOMAD_IP_http}",
"-http-bind-port=${NOMAD_PORT_http}",
"-nomad-ca-cert=local/nomad-autoscaler/etc/certificates/ca.pem",
"-nomad-client-cert=local/nomad-autoscaler/etc/certificates/cert.pem",
"-nomad-client-key=local/nomad-autoscaler/etc/certificates/private_key.pem",
"-nomad-region=de-west"
]
}
template {
destination = "${NOMAD_SECRETS_DIR}/env.txt"
env = true
data = <<EOT
NOMAD_TOKEN={{ with nomadVar "nomad/jobs/autoscaler-ha-prod1" }}{{ .token }}{{ end }}
EOT
}
artifact {
source = "https://github.com/jorgemarey/nomad-nova-autoscaler/releases/download/v0.6.0/nomad-nova-autoscaler-v0.6.0-linux-amd64.tar.gz"
destination = "local/nomad-autoscaler/plugins"
options {
checksum = "md5:fec29af8625842b154d30be8b8db305f"
}
}
template {
data = <<EOH
{{ with nomadVar "nomad/jobs/autoscaler-ha-prod1" }}{{ .cacert }}{{ end }}
EOH
destination = "local/nomad-autoscaler/etc/certificates/ca.pem"
}
template {
data = <<EOH
{{ with nomadVar "nomad/jobs/autoscaler-ha-prod1" }}{{ .clientcert }}{{ end }}
EOH
destination = "local/nomad-autoscaler/etc/certificates/cert.pem"
}
template {
data = <<EOH
{{ with nomadVar "nomad/jobs/autoscaler-ha-prod1" }}{{ .clientkey }}{{ end }}
EOH
destination = "local/nomad-autoscaler/etc/certificates/private_key.pem"
}
template {
data = <<EOH
#high_availability {
# enabled = true
# lock_namespace = "autoscalerprod1"
# lock_path = "ha/lock"
# lock_ttl = "30s"
# lock_delay = "15s"
#}
apm "nomad-apm" {
driver = "nomad-apm"
}
target "os-nova" {
driver = "os-nova"
config = {
auth_url = "redacted"
username = "redacted"
password = "redacted"
domain_name = "redacted"
project_id = "redacted"
region_name = "prod1"
}
}
strategy "target-value" {
driver = "target-value"
}
EOH
destination = "local/nomad-autoscaler/etc/nomad-autoscaler.hcl"
}
template {
data = <<EOH
scaling "worker_pool_policy" {
enabled = true
min = 1
max = 2
policy {
cooldown = "2m"
evaluation_interval = "1m"
check "cpu_allocated_percentage" {
source = "nomad-apm"
query = "percentage-allocated_cpu"
strategy "target-value" {
target = 70
}
}
target "os-nova" {
dry-run = false
stop_first = true
image_id = "redacted"
flavor_name = "redacted"
pool_name = "redacted"
name_prefix = "nom-"
network_id = "redacted"
security_groups = "default"
availability_zones = "az1"
tags = "nom-pool,ubuntu-minimal"
server_group_id = "redacted"
node_class = "nom-pool"
node_drain_deadline = "1h"
node_drain_ignore_system_jobs = false
node_purge = true
node_selector_strategy = "least_busy"
}
}
}
EOH
destination = "local/nomad-autoscaler/etc/policies/scaling-policy.hcl"
}
resources {
cpu = 50
memory = 128
}
}
}
}
Überall, wo da jetzt noch "redacted" steht, müssen auch noch Variablen gesetzt werden, da sich diese Werte ja in der Regel auch pro Umgebung unterscheiden.
Ich verwende hier den "Exec" Driver, weil der Nomad Autoscaler als simples Go-Binary vorliegt, welches ich mit ein paar Parametern starten kann ohne erst einen Docker-Container dafür bauen zu müssen.
Links:
Create and update Nomad Variables
Configure access control for Nomad Variables
Access Nomad Variables from within tasks
Nomad secrets consumption patterns: Nomad variables
Im folgenden Video kann man die Verwendung von Nomad Variablen und des Nomad Autoscalers sehen: