Le Labo #20 | A la découverte de Nomad
1/Dec 2016
Pour commencer
A un mois de Nöel…ca fait un peu plus de 4 mois que je n’avais rien publié…et je n’avais pas le temps de préparer d’article (ni d’inspiration).
C’est alors que l’on m’a proposé de travailler sur de l’orchestration de conteneurs.
Et…j’ai pensé à vous.
Petit Historique de Nomad
Le développement de Nomad a débuté en Juin 2015, en compagnie de Otto (projet qui fut simplement abandonné quelques mois avant la release de Nomad).
Cet outil devait devenir le concurrent de Kubernetes, Swarm et Mesos (pour ne citer que les plus connus).
La release initiale date de Juin ou Juillet, moment auquel il fut mis à disposition du public.
Le cas concret
Dans le cas d’utilisation qui va servir de démonstration, je vais déployer plusieurs conteneurs Docker sur 6 hôtes différents avec du réseau de conteneurs et du monitoring.
Ce qu’il nous faut
Dans ce labo, je vais avoir besoin de plusieurs serveurs :
- 6 serveurs Minions sur lesquels seront installés Docker et Nomad
- 1 serveur Master sur lequel Nomad sera installé
- 1 serveur Nexus pour le Docker Private Registry
- 1 Serveur Monitor sur lequel sera installé et configuré Consul
Vu que chaque serveur sera déployé à l’aide de terraform, j’ai aussi prévu les scripts de provisionning et de déploiement (afin d’eviter de passer par Salt ou Ansible…ce qui s’avèrerait plus long).
La configuration de la partie Private Registry de Nexus étant disponible sur le site de Sonatype, je ne la détaillerais pas ici.
Configuration des serveurs Master et Minions
La configuration de ces serveurs fut assez longue (environ 2 semaines), le plus dur fut de trouver les bonnes infos (qui sont assez éparpillées). Mais une fois le tout rassemblé, tout va très vite.
On va commencer par le plus simple : Docker
La configuration de Docker
Dans mon cas, j’ai deux détails à intégrer dans mon fichier docker.conf : le proxy (parce que oui…je me suis compliqué la tâche) et le Private Registry configuré sur le serveur Nexus.
Le fichier docker.conf devra se trouver dans le dossier /etc/systemd/system/docker.service.d (si vous etes sur un server linux équipé de systemd comme CentOS 7 ou Ubuntu 16.04/16.10) et ajouter les infos ci dessous :
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd --insecure-registry=mon.docker.registry:port
Environment="HTTP_PROXY=http://mon.adresse.proxy:port"
Environment="HTTPS_PROXY=https://mon.adresse.proxy:port"
Environment="NO_PROXY=localhost,127.0.0.1,mon.docker.registry"
N’oubliez surtout pas d’ajouter insecure-registry
, surtout si votre private registry est configuré en http et non en https (sinon docker aura du mal à le contacter).
Ensuite, exécutez la commande suivante : systemctl daemon-reload && systemctl restart docker
(si vous créez le fichier docker.conf avant d’installer docker, vous n’aurez pas besoin d’exécuter a commande précédente).
La configuration du système
Vu que l’infra sur laquelle j’ai travaille comportait un proxy, il fallait faire en sorte que docker l’évite pour accéder au *Private Registry”.
Donc, j’ai exécuté la commande suivante pour compléter les variables d’environnement:
export NO_PROXY="localhost,127.0.0.1,mon.docker.registry"
Une fis cette étape effectuée, j’ai pu m’authentifier sur le Private Registry avec un docker login
La configuration de Nomad
La configuration de Nomad (master et client) est relativement simple, il faut surtout déclarer les éléments suivants :
- log_level
- bind_addr
- addresses
- advertise
- data_dir
- server
- ports
Les clients ont des keywords particulier, comme datacenters qui permet de définir l’endroit exact où la ressource sera déployée.
Le master
log_level = "DEBUG"
bind_addr = "adresse.nomad.master"
addresses {
rpc = "adresse.nomad.master"
http = "adresse.nomad.master"
}
advertise {
rpc = "adresse.nomad.master:4647"
http = "adresse.nomad.master:4646"
}
data_dir = "/tmp/server"
server {
enabled = true
bootstrap_expect = 1
}
ports {
rpc = 4647
http = 4646
}
Le client
log_level = "DEBUG"
bind_addr = "adresse.nomad.client"
datacenter "dc[x]"
addresses {
rpc = "adresse.nomad.client"
http = "adresse.nomad.client"
}
advertise {
rpc = "adresse.nomad.client:4647"
http = "adresse.nomad.client:4646"
}
data_dir = "/tmp/server"
client {
enabled = true
options = {
"docker.auth.config" = "/root/.docker/config.json"
"driver.raw_exec.enabled" = "1"
}
server = ["adresse.nomad.master:4647"]
}
ports {
rpc = 4647
http = 4646
}
Démarrage des services Master et Client
La commande est très simple, pour démarrer chaque service, il faut excuter la commande suivante : nomad agent -config /chemin/vers/fichier/config.hcl &
Pour vérifier que tout s’est bien lancé, exécutons la commande suivante sur le serveur Master : nomad node-status -address=http://ip.serveur.master:4646
Ce qui suite va s’afficher :
ID DC Name Class Drain False
45deb5e dc1 host1 <none> false ready
98f55d7 dc2 host2 <none> false ready
4cf0da1 dc3 host3 <none> false ready
65ah75e dc4 host4 <none> false ready
Les jobs
Sur le site de Nomad, tout est expliqué au sujet des jobs specifications, mais ce qui va suivre n’est expliqué nulle part, donc faites bien attention à ce qui suit :
- Vous pouvez définir un seul job avec un seul group et inclure de nombreuses task, mais vous n’aurez aucun contrôle sur l’endroit ou seront déployées ces task.
- En utilisant les constraints, vous pouvez restreindre en fonction d’un certain pattern l’exécution des jobs/groups/tasks.
Je vous invite à consulter la documentation officielle.
De mon côté, j’ai déployé 3 conteneurs zookeeper et 3 conteneurs tomcat de cette manière :
Les conteneurs Zookeeper
job "zookeeper" {
datacenter = ["dc1", "dc2", "dc3"]
count = 3
group "deploy" {
task "zookeeper-deploy" {
image = "mon.docker.registry:port/my-zookeeper"
auth {
username = "mylogin"
password = "mypassword"
}
port_map {
zk-1 = 2181
zk-2 = 2888
zk-3 = 3888
}
tty = true
interactive = true
hostname = "zookeeper"
}
resources {
cpu = 90
memory = 10
network {
mbits = 10
port zk-1 {}
port zk-2 {}
port zk-3 {}
}
}
}
}
Les conteneurs Tomcat
job "tomcat" {
datacenter = ["dc1", "dc3", "dc4"]
count = 3
group "deploy" {
task "tomcat-deploy" {
image = "mon.docker.registry:port/my-tomcat"
auth {
username = "mylogin"
password = "mypassword"
}
port_map {
tc-1 = 8080
tc-2 = 8081
tc-3 = 8090
}
tty = true
interactive = true
hostname = "tomcat"
}
resources {
cpu = 90
memory = 10
network {
mbits = 10
port tc-1 {}
port tc-2 {}
port tc-3 {}
}
}
}
}
Le monitoring
J’ai déjà fais un tour du côté de Consul lors d’un précédent use-case…je vous invite à le lire pour avoir plus d’infos concernant son installation/configuration.
En ce qui concerne la partie monitoring de conteneurs, il faut ajouter la section service à chaque task, voir ci-dessous :
service {
tags = ["zookeeper"]
port = "zk-1"
check {
type = tcp
port = "zk-1"
interval = "10s"
timeout "2s"
}
}
Ou bien, créer un script ad-hoc et le spécifier dans le type avec la cammande pour l’exécuter.
Conclusion
Malgré la relative jeunesse de Nomad et le fait que très peu de retours d’expérience soient encore disponible sur Internet, c’est un outils relativement complet et simple à prendre en main, bénéficiant de connexions assez bien pensées avec le reste de l’écosystème Hashicorp (notamment avec Terraform, Vagrant et Consul).
De mon côté, après avoir effectué de nombreux tests (qui se sont révélés etre de gros echecs) avec Kubernetes et Swarm, j’ai adopté Nomad en production et je continue à travailler dessus pour du fine tunning afin que le projet de déploiement soit parfait…ou presque (il y a un serveur Salt qui tourne derrière…voila pourquoi je continue dessus).