Nomad premiers pas
Configuration et déploiement de Nomad pour orchestrer vos workloads

Cet article fait suite à celui parlant de Nomad et pourquoi aujourd’hui j’essaie de me tenir loin de K8. Pour plus de context aller le lire ici.
Dans la suite, on va voir comment déployer Nomad pour la première fois, configurer de la télémétrie et voir quelques command (cli) pouvant être utiles.
Déployer Nomad
Dans la suite nous allons parler de l’architecture cible. Elle sera simple mais nous discuterons quels sont les prochaines étapes.
Cet article n’a pas pour objet de l’intégrer dans un outil d’IaC, comme Terraform. Mais il tout à fait possible de faire les même étapes, ce soit seul avec Cloud-Init ou en conjonction avec Ansible (Puppet ou Chef).
Architecture cible
Nomad est fait d’un seul binaire pouvant travailler en mode Server ou Client; le “ou” n’est pas exclusif.
Le Server a pour rôle d’Orchestrer les tâches. faisant plus de la chorégraphie que de d’orchestration, mais c’est un détail. C’est à lui qu’on doit envoyer la définition des Jobs pour les exécuter.
Le Client est en charge d’exécuté la tâche. Plusieurs drivers sont disposibles, mais nous utiliserons majoritairement les containers (Podman ou Docker).

Prérequis
Avoir 4 machines disposibles pour installer Nomad. Avoir CoreDNS déployé avec toute les machines enregistrées, exposant les DNS server1.vm, client1.vm, client2.vm et client3.vm.
Toutes les machines peuvent communiquer sur le port 4646/4647/4648 et sur les ports allons de 20000 à 32000.server1.vm est accessible depuis l’exterieur sur le DNS server.nomad.dns.com, et client1.vm est accessible de l’extérieur sur traefik.dns.vm.
Les 3 machines clients ont installé le moteur conteneur choisi (podman ou docker).
Installation et config de base.
Installation
Sur Ubuntu:
0$apt-get update && apt-get install -y nomad
Voila, c’est installé.
Créer les certificats pour sécuriser la communication avec nomad (mtls)
Nomad a besoin de certificats pour communiquer en mtls avec les autres clients. Ainsi on va créer les certificats serveur et client, en passant le dns interne et externe sur lesquels on peut joindre le serveur Nomad. On va en profiter pour créer les certificats pour que Traefik puisse utilise le Service Discovery de Nomad.
On va créer un dossier /opt/nomad/certs ou l’on va enregistrer les certificats.
0$docker run --rm -it -v /opt/nomad/certs:/home -w /home docker.io/hashicorp/nomad:1.9.5 tls ca create
1$docker run --rm -it -v /opt/nomad/certs:/home -w /home docker.io/hashicorp/nomad:1.9.5 tls cert create -server additional-dnsname server1.vm -additional-dnsname server.nomad.dns.com -days 90
2$docker run --rm -it -v /opt/nomad/certs:/home -w /home docker.io/hashicorp/nomad:1.9.5 tls cert create -client additional-dnsname server1.vm -additional-dnsname server.nomad.dns.com -days 90
3$mv global-client-nomad.pem traefik.pem
4$mv global-client-nomad-key.pem traefik-key.pem
5$docker run --rm -it -v /opt/nomad/certs:/home -w /home docker.io/hashicorp/nomad:1.9.5 tls cert create -client additional-dnsname server1.vm -additional-dnsname server.nomad.dns.com -days 90
Si vous avez pas accès à un dns interne/externe vous pouvez aussi utiliser les ips fixes, il faudra alors utiliser l’option -additional-ipaddress
.
Le flag -days 90
permet de définir le nombre de jours de validiter du certificat.
Le dossier devrait contenir les 8 fichier:
0total 9
1drwxrwxr-x 1 v4rgas v4rgas 0 févr. 18 05:03 ./
2drwxrwxr-x 1 v4rgas v4rgas 0 janv. 22 23:40 ../
3-rw------- 1 v4rgas v4rgas 227 févr. 18 11:55 global-client-nomad-key.pem
4-rw-r--r-- 1 v4rgas v4rgas 1001 févr. 18 11:55 global-client-nomad.pem
5-rw------- 1 v4rgas v4rgas 227 févr. 18 11:55 global-server-nomad-key.pem
6-rw-r--r-- 1 v4rgas v4rgas 1001 févr. 18 11:55 global-server-nomad.pem
7-rw------- 1 v4rgas v4rgas 227 févr. 18 11:55 nomad-agent-ca-key.pem
8-rw-r--r-- 1 v4rgas v4rgas 1115 févr. 18 11:55 nomad-agent-ca.pem
9-rw------- 1 v4rgas v4rgas 227 févr. 18 11:55 traefik-key.pem
10-rw-r--r-- 1 v4rgas v4rgas 1001 févr. 18 11:55 traefik.pem
Créer le fichier de config serveur
Sur la machine serveur, on va créer le fichier: /etc/nomad.d/nomad.hcl
.
0datacenter = "group-1"
1data_dir = "/opt/nomad/data"
2server {
3 enabled = true
4 bootstrap_expect = 1
5}
6advertise {
7 http = "0.0.0.0"
8}
9tls {
10 http = true
11 rpc = true
12 rpc_upgrade_mode = true
13
14 ca_file = "/opt/nomad/certs/nomad-agent-ca.pem"
15 cert_file = "/opt/nomad/certs/global-server-nomad.pem"
16 key_file = "/opt/nomad/certs/global-server-nomad-key.pem"
17
18 verify_server_hostname = true
19 verify_https_client = true
20}
21telemetry {
22 collection_interval = "15s"
23 disable_hostname = true
24 prometheus_metrics = true
25 publish_allocation_metrics = true
26 publish_node_metrics = true
27}`;
Ensuite téléverser les certificats nécessaire, dans les dossier défini plus haut.
Le bloc server
, est nécessaire pour activer le process “server” de nomad, il n’est pas exclusif (les deux bloc serveur et client peut-être mis dans le fichier de config).
Le bloc adversite
se met à écouter sur l’ip définie.
Le bloc tls
permet de configurer les certificats nécessaire pour faire tu mtls et exposer le client https.
Enfin le bloc telemetry, permet d’exposer les metrics en format prometheus. Il faudra soit les collecter en utiliser des certificats spécifiques.
Créer le fichier de config des clients
Sur les trois machines client on va aussi créer le fichier: /etc/nomad.d/nomad.hcl
.
Sur client1.vm on va définir le comme étant edge
et les deux autre machine on utilisera services
.
0datacenter = "<DC>"
1data_dir = "/opt/nomad"
2client {
3 enabled = true
4 servers = ["server1.vm:4647"]
5}
6tls {
7 http = true
8 rpc = true
9
10 ca_file = "/opt/certs/nomad/nomad-agent-ca.pem"
11 cert_file = "/opt/certs/nomad/global-client-nomad.pem"
12 key_file = "/opt/certs/nomad/global-client-nomad-key.pem"
13
14 verify_server_hostname = true
15 verify_https_client = true
16}
17plugin "docker" {
18 config {
19 volumes {
20 enabled = true
21 }
22 auth {
23 config = "/opt/scripts/docker-auth.json"
24 }
25 allow_privileged = true
26 extra_labels = ["job_name", "job_id", "task_group_name", "task_name", "namespace", "node_name", "node_id"]
27 }
28}
29telemetry {
30 collection_interval = "15s"
31 disable_hostname = true
32 prometheus_metrics = true
33 publish_allocation_metrics = true
34 publish_node_metrics = true
35}
Ensuite téléverser les certificats nécessaire, dans les dossier défini plus haut.
On voit le client client
qui permet de définir la chemin d’accès.
Le block docker
, permet de configurer le client docker utilisé par Nomad.
On défini ici un docker-auth pour se connected avec le Repository de Container en étant authentifié.
On rajoute des labels au service (pour la télémétrie), on permet l’utilisation des volumes docker et l’utilisation priviligiée.
Configurer le service systemd
Il ne reste plus qu’a démarrer nos services pour qu’ils soient accèssibles. Nous allons utiliser systemd pour orchestrer les services Nomad.
0[Unit]
1Description=Nomad
2Documentation=https://www.nomadproject.io/docs
3Wants=network-online.target
4After=network-online.target
5[Service]
6ExecReload=/bin/kill -HUP $MAINPID
7ExecStart=/usr/bin/nomad agent -config /etc/nomad.d/nomad.hcl
8KillSignal=SIGINT
9LimitNOFILE=infinity
10LimitNPROC=infinity
11Restart=on-failure
12RestartSec=2
13StartLimitBurst=3
14StartLimitInterval=10s
15TasksMax=infinity
16[Install]
17WantedBy=multi-user.target
Il suffit maintenant d’activer le service et le démarrer:
Voila, desormais Nomad tourne sur les machines.
Si vous faites un $systemctl status nomad
et vous devriez avoir la gui-cli de systemd pour nomad.
Se connecter depuis l’exterieur du cluster
Désormais vous pouvez appeler le serveur Nomad depuis l’extérieur.
0$export ENDPOINT=NOMAD_ADDR=https://server1.vm:4646
1$export NOMAD_CLIENT_CERT=NOMAD_CLIENT_CERT=/opt/certs/nomad/global-client-nomad.pem
2$export NOMAD_CLIENT_KEY=NOMAD_CLIENT_KEY=/opt/certs/nomad/global-client-nomad-key.pem
3$export NOMAD_CACERT=NOMAD_CACERT=/opt/certs/nomad/nomad-agent-ca.pem
4$nomad status
Créer le p12 pour faire du mtls depuis l’extérieur en utilisant le navigateur
Désormais on expose la GUI web Nomad, accessible sur server.nomad.dns.com, il faut créer le fichier p12 que vous enregistrerez dans votre navigateur.
0$echo "$(openssl rand -base64 30 | tr -d "=+/" | cut -c1-64)" > /tmp/user-gui.txt
1$openssl pkcs12 -export -inkey /opt/nomad/certs/global-client-nomad-key.pem -in /opt/nomad/certs/global-client-nomad.pem -out /opt/nomad/certs/nomad-gui.p12 -password file:/tmp/user-gui.txt
Et désormais si vous allez sur l’url server.nomad.dns.com
vous aurez accès à la GUI de Nomad, de manière sécurisée.
Déployer traefik pour gérer tout le flux Http(s)
Pour faire du https, il vous faudra les certificats. Une fois crées déposez le dans le dossier /opt/traefik/tls/<DNS-NAME>
.
Il vous faudra au minimum 2 pairs de certificat un simple et un en wildcard.
Pour l’exemple nous allons utiliser les dns: dns.com
et *.service.dns.com
, accessibles dans les dossiers /opt/traefik/tls/dns.com
et /opt/traefik/tls/service.dns.com
respectivement.
Nous allons aussi ajouter des certificats auto-signé pour le dns *.service.local
On les téléverse sur le client1 (ayant pour datacenter edge
)
Enfin on téléverse les fichiers /opt/certs/nomad/nomad-agent-ca.pem
, /opt/certs/nomad/traefik-key.pem
et /opt/certs/nomad/traefik.pem
. On s’en servira pour utilise le Service Discovery
de Nomad directement.
On va créer un fichier contenant les configs tls: /opt/traefik/config.yml
0tls:
1 certificates:
2 - certFile: /opt/traefik/tls/dns.com/fullchain.pem
3 keyFile: /opt/traefik/tls/dns.com/privkey.pem
4 - certFile: /opt/traefik/tls/service.dns.com/fullchain.pem
5 keyFile: /opt/traefik/tls/service.dns.com/privkey.pem
6 - certFile: /opt/traefik/tls/service.internal/fullchain.pem
7 keyFile: /opt/traefik/tls/service.internal/privkey.pem
On va créer le fichier /opt/nomad/jobs/traefik.hcl.
0job "traefik" {
1 datacenters = ["edge"]
2 type = "service"
3
4 group "traefik" {
5 count = 1
6
7 network {
8 port "http" {
9 static = 80
10 to = 80
11 }
12 port "https" {
13 static = 443
14 to = 443
15 }
16 }
17
18 service {
19 name = "traefik"
20 provider = "nomad"
21 port = "http"
22 tags = [
23 "traefik.http.routers.traefik-http.service=api@internal",
24 "traefik.http.routers.traefik-http.rule=Host(`traefik.service.dns.com`)",
25 "traefik.http.routers.traefik-http.tls=true",
26 ]
27 }
28
29 task "traefik" {
30 driver = "docker"
31 config {
32 image = "docker.io/traefik:v3.3.5"
33 network_mode = "host"
34 ports = ["admin", "http", "internal", "https"]
35 args = [
36 "--api.dashboard",
37 "--api.insecure",
38 "--serversTransport.insecureSkipVerify",
39 "--entrypoints.web.address=:80",
40 "--entrypoints.https.address=:443",
41 "--providers.file.directory=/etc/traefik/dynamic",
42 "--providers.nomad",
43 "--providers.nomad.endpoint.address=https://server1.vm:4646",
44 "--providers.nomad.stale",
45 "--providers.nomad.defaultRule=Host(`{{ .Name }}.service.dns.com`)",
46 "--providers.nomad.endpoint.tls.ca=/opt/certs/nomad/nomad-agent-ca.pem",
47 "--providers.nomad.endpoint.tls.cert=/opt/certs/nomad/traefik.pem",
48 "--providers.nomad.endpoint.tls.key=/opt/certs/nomad/traefik-key.pem",
49
50 ]
51 mounts = [
52 {
53 type = "bind"
54 target = "/opt/certs/nomad/"
55 source = "/opt/certs/nomad/"
56 readonly = true
57 }, {
58 type = "bind"
59 target = "/opt/traefik/tls"
60 source = "/opt/traefik/tls"
61 readonly = true
62 },
63 ]
64 }
65
66 resources {
67 cpu = 500
68 memory = 512
69 }
70
71 }
72 }
73}
il manque plus qu’a appliquer le job soit en passant par la GUI soit en passant par le CLI.
nomad job plan traefik.hcl
vous permettra de valider le fichier.
nomad job run traefik.hcl
vous permettra de démarrer le service avec le fichier hcl.
Voila, désormais vous trouverez la GUI de traefik à l’url traefik.service.dns.com
.
Et voila, désormais lorsque vous levez un service il suffira de mettre le provider (ligne 20) et traefik l’enregistrira automatiquement pour l’exposer sur l’url https://ServiceName.service.dns.com
.
Quelques commandes intéressantes:
Voir le status d’un job
0$nomad job status traefik
Voir le status d’une task
0$nomad status <allocation_id>
Voir les logs d’une task
0$nomad logs <allocation_id>
Exporter des variables dans des fichier
On crée le dossier /opt/nomad/variables
s’il n’existe pas.
On crée un script:
0#!/bin/bash
1set -eu -o pipefail
2paths=$(nomad var list -out table nomad/jobs | tail -n +2 | egrep -o '([a-z0-9]+/)([a-z0-9/]+)')
3echo "$paths" | while IFS= read -r line ; do nomad var get -out table nomad/jobs | sed '/Items/,$!d' | tail -n +2 | sed -r 's/[ ]+=[ ]+/=/g' > /opt/nomad/variables/$(echo $line | sed 's/\//./g'); done
On aura ainsi autant de fichier que de portées.
Importer des variables depuis un fichier
On créer un fichier /opt/nomad/variables/nomad.job
0VERSION=12
On lance le shell suivant
0$nomad var put -out json -force nomad/jobs $(cat variables/nomad.jobs | sed -r 's/\n/ /g')
Conlusion
La configuration et déploiement de base est comme vous le voyez très simple.
Il manque à ajouter les configurations pour l’opentélémétrie (metrics, logs et traces), déployer les services de télémétrie dans Nomad, faire un peu de hardening sur nomad, utiliser un service pour la gestion des secrets, et configurerles services pour faire du rolling updates.