Voici un nouvel article sur Nomad qui fait suite à Une infra avec Nomad, Consul et Tailscale.
En plus du type service
que l’on a vu précédemment, Nomad propose le type batch qui est idéal pour lancer un tâche régulièrement via un cron intégré.
La dernière fois j’utilisais le driver docker, mais c’est complètement overkill pour lancer une simple tâche batch. Or Nomad propose le driver exec, et exec c’est un bon vieux chroot ! Léger et rapide donc. On va ici faire quelque chose d’utile, programmer une tâche qui fait un dump régulier d’une base de données qui tourne dans un conteneur Docker quelque part sur un noeud Nomad.
Pour cela je me suis fortement appuyé sur cette doc : How to backup Postgres database with Nomad, que j’ai simplifié car je dump simplement dans un répertoire du host et que je n’utilise pas encore vault (gruik!), mais c’est prévu. De plus il utilise le driver raw_exec plutôt que exec
sans doute pour pouvoir appeler Docker.
Comme exemple je vais utiliser Semaphore UI, qui est une superbe alternative à AWX, écrit en Go bien sûr (whatelse ?).
Voici le fichier hcl que j’utilise pour instancier Semaphore avec sa base Postgresql
semaphore.hcl
job "semaphore" {
datacenters = ["dc1"]
type = "service"
group "app" {
count = 1
network {
mode = "bridge"
port "http" {
to = 3000 # container port the app runs on
static = 80 # host port to expose
}
port "postgresql" {
to = 5432 # container port the app runs on
}
}
task "web" {
driver = "docker"
constraint {
attribute = "${attr.unique.hostname}"
value = "vm-semaphore"
}
env {
SEMAPHORE_DB_USER = "semaphore"
SEMAPHORE_DB_PASS = "PASS"
SEMAPHORE_DB_HOST = "127.0.0.1"
SEMAPHORE_DB_PORT = "5432"
SEMAPHORE_DB_DIALECT = "postgres"
SEMAPHORE_DB = "semaphore"
SEMAPHORE_PLAYBOOK_PATH = "/tmp/semaphore/"
SEMAPHORE_ADMIN_PASSWORD = "PASS"
SEMAPHORE_ADMIN_NAME = "admin"
SEMAPHORE_ADMIN_EMAIL = "admin@localhost"
SEMAPHORE_ADMIN = "admin"
SEMAPHORE_ACCESS_KEY_ENCRYPTION = "TOKEN"
SEMAPHORE_TELEGRAM_ALERT = true
SEMAPHORE_TELEGRAM_CHAT = "CHATID"
SEMAPHORE_TELEGRAM_TOKEN = "TOKEN"
http_proxy = "http://user:PASS@IP_TAILSCALE_NODE1:3128"
https_proxy = "http://user:PASS@IP_TAILSCALE_NODE1:3128"
}
config {
image = "semaphoreui/semaphore:latest"
ports = ["http"]
}
resources {
cpu = 1000
memory = 2000
}
service {
name = "semaphore"
tags = ["global", "app"]
provider = "consul"
port = "http"
check {
type = "http"
name = "app_health"
path = "/"
interval = "20s"
timeout = "10s"
}
}
}
task "postgresql" {
driver = "docker"
constraint {
attribute = "${attr.unique.hostname}"
value = "vm-semaphore"
}
env {
POSTGRES_USER = "semaphore"
POSTGRES_PASSWORD = "PASS"
POSTGRES_DB = "semaphore"
}
config {
image = "postgres:16"
mounts = [
{
type = "volume"
target = "/var/lib/postgresql/data"
source = "semaphore-postgresql"
},
{
type = "bind"
target = "/dump"
source = "/volume/dump/semaphore"
readonly = false
bind_options = {
propagation = "rshared"
}
}
]
ports = ["postgresql"]
}
resources {
cpu = 1000
memory = 1000
}
service {
name = "semaphore-postgresql"
provider = "consul"
port = "postgresql"
tags = ["alloc=${NOMAD_ALLOC_ID}"]
}
}
}
}
On a donc 2 conteneurs positionnés sur la même VM vm-semaphore
. A noter que via la directive mode = "bridge"
semaphore communique avec sa base de donnée sur l’interface localhost. Il y a dans ce hcl 3 paramètres importants pour que le script batch puisse fonctionner :
name = "semaphore-postgresql"
tags = ["alloc=${NOMAD_ALLOC_ID}"]
Le service s’enregiste auprès de Consul, ce qui va permettre au serveur Nomad de l’interroger pour récupérer le contenu du tag associé au service semaphore-postgresql
. Le tag contient un identifiant unique créé par Nomad lors de la création du conteneur et grâce à cet identifiant Nomad va pouvoir lancer le dump directement dans le conteneur Postgresql.
semaphore-db-backup.hcl
job "semaphore-db-backup" {
datacenters = ["dc1"]
type = "batch"
periodic {
crons = ["0 20 * * *"]
time_zone = "Europe/Paris"
prohibit_overlap = true
}
group "db-backup" {
task "postgres-backup" {
driver = "exec"
config {
command = "/bin/bash"
args = ["local/script.sh"]
}
template {
data = <<EOH
set -e
DATE_BIN=$(command -v date)
DATE=`${DATE_BIN} +%d-%m-%Y---%H-%M-%S`
nomad alloc exec -task postgresql $DB_ALLOC_ID \
/bin/bash -c "PGPASSWORD=PASS PGUSER=semaphore PGDATABASE=semaphore pg_dump --compress=4 > /dump/semaphore_${DATE}.dump.gz && /bin/chown 1001:1001 /dump/semaphore_${DATE}.dump.gz"
EOH
destination = "local/script.sh"
}
template {
data = <<EOH
#### GRUIK ! ######
NOMAD_TOKEN="TOKEN"
###################
# as service 'semaphore-postgresql' is registered in Consul
# we want to grab its 'alloc' tag
{{- range $tag, $services := service "semaphore-postgresql" | byTag -}}
{{if $tag | contains "alloc"}}
{{$allocId := index ($tag | split "=") 1}}
DB_ALLOC_ID="{{ $allocId }}"
{{end}}
{{end}}
EOH
destination = "secrets/file.env"
env = true
}
resources {
cpu = 200
memory = 200
}
}
}
}
Et voilà le script qui sera instancié comme d’habitude par un nomad job run semaphore-db-backup.hcl
La section periodic
est évidente, elle permet de le lancer tous les jours à 20h00.
On spécifie ensuite le driver exec
et ce qu’il doit lancer, ici bash avec un script.sh
dans le répertoire local
du chroot. Le script.sh n’existe pas encore, il est décrit ensuite dans une première section template. C’est un simple bash qui fait un pg_dump
suivit par un chown
pour attribuer le fichier à l’utilisateur ansible sur mon host. En effet c’est une tâche ansible lancé par la suite par Semaphore qui va envoyer le dump.gz quelque part.
La partie importante est nomad alloc exec -task postgresql $DB_ALLOC_ID
car c’est cette commande qui va instancier la commande bash dans le conteneur Postgresql et ce où que soit le conteneur dans la grappe Nomad.
Le deuxième template permet d’initialiser les variables d’environnement, interroge Consul pour extraire du service semaphore-postgresql
le tag qui contient alloc
afin d’extraire le NOMAD_ALLOC_ID
et de créer la variable $DB_ALLOC_ID
. Tout cela est stocké dans un fichier secrets/file.env
dans le chroot et sera utilisé par le local/script.sh
.
Si vous n’utilisez pas les ACL il n’y a pas la NOMAD_TOKEN
, sans doute dans un prochain article je montrerai comment l’initialiser avec vault.
Si tout va bien vous devriez avoir tous les soirs un précieux semaphore_${DATE}.dump.gz
à sauvegarder précieusement car il contient tous vos inventaires, tâches et planifications.
(Ce texte a été écrit avec Ghostwriter)