Website Logo. Upload to /source/logo.png ; disable in /source/_includes/logo.html

Zuzur’s wobleg

technology, Internet, and a bit of this and that (with some dyslexia inside)

Déployer Rancher Sur Une Seule Machine

| Comments

Rancher est un clickodrome assez sympa pour gérer un cluster de machines qui font fonctionner un démon docker et administrer les conteneurs qu’on aura déployés dessus. Le logiciel est assez abouti, et j’ai déjà quelques clients qui s’en servent en production et sont très contents.

Ayant subi une panne sur l’ancienne machine qui hébergeait mon blog et les autres services dont j’avais besoin, je me suis dit que j’allais essayer de tout passer dans des conteneurs juste pour le fun… je commence donc une petite série de billets sur ce que j’ai fait et comment tout ça fonctionne… et en premier lieu, puisque je n’ai pas les moyens ni l’envie de gérer un cluster avec plein de machines juste pour ça, “comment installer rancher sur une seule machine”.

Mais qu’est-ce que c’est ‘docker’ ?

Décrire et expliquer docker n’est pas le propos de cet article, mais je vais essayer en quelques mots…

Ceux qui connaissent les jails, qui existent depuis des années sous *BSD, ne devraient pas être trop dépaysés par le concept. A la base, le principe est de faire fonctionner un (ou plusieurs) processus dans un espace totalement isolé du reste du système, ce qui permet de bien compartimenter et d’interdire à un processus d’accèder à des données qu’il n’est pas sensé lire/écrire, etc… Par exemple, on ne voit pas bien pourquoi un programme CGI lancé par apache pourrait vouloir modifier /etc/passwd. Sur un système bien configuré, il ne pourrait bien sûr pas le faire, mais comme on ne sait jamais, c’est bien que l’environnement d’exécution dispose d’une copie complètement indépendante de ce fichier, et ne puisse pas, quoiqu’il fasse, perturber le fonctionnement des autres programmes qui fonctionnent sur cette machine… Ainsi, un attaquant qui arriverait à défoncer votre serveur HTTP ne pourrait pas faire grand chose à part péter ce qu’il y a sur la jail, c’est à dire le minimum pour faire fonctionner le serveur HTTP, donc c’est juste chouette™ parceque ça va largement compliquer la tâche des malandrins ! Et d’ailleurs, la philosophie, c’est que dans un conteneur bien fait, on n’a pas même besoin du shell, donc on ne l’installe pas, et un attaquant serait bien emmerdé avec son shell-code qui essaye exploite un débordement pour faire éxécuter /bin/sh à apache ou nginx ;-)

Docker, c’est une couche au dessus de ce genre de système sous Linux, qui, en plus de gérer les montages, le chroot des processes comme les jails, s’occupe de la connexion au réseau, et de la gestion des images des conteneurs - et, pour moi, c’est là que c’est assez révolutionnaire. Il se base sur un système de fichiers “union mount” qui fonctionne par couches. On démarre avec une image de base, par exemple un debian/jessie tout frais, juste installé, on ajoute les couches qui permettent de fairce ce qu’on veut dans notre conteneur - par exemple, en installant apache ou nginx, des fichiers statiques, on commite la nouvelle image, en la poussant dans docker, en l’appelant erwan/debian_httpd, et à chaque fois qu’on va lancer un conteneur erwan/debian_http, c’est cette image exacte qui sera utilisée comme base. Le démon docker permet de publier et récupérer les images publiques, via le docker hub - le composant logiciel s’appelle le registry, on peut même publier sa propre image si on le souhaite, sur le docker hub, ou sur un registry privé (une purge ultra pénible à mettre en place - ce sera peut-être l’objet d’un prochain article).

Installation de docker

C’est plutôt facile et bien documenté. Il vaut mieux utiliser une version récente de linux avec un kernel 4.4, qui contient toutes les extensions (namespaces, etc…) dont docker a besoin: Install Docker

Comme on lit toujours bien la documentation, assez touffue, avant de faire des trucs sérieusement avec un nouveau logiciel, on constate qu’il y est déconseillé d’utiliser aufs en production pour le stockage, pour des raisons de performances avec de gros volumes. Pour mon petit serveur, je pense que ça aurait été largement suffisant, mais j’avais aussi envie de me familiariser avec docker en production, donc je suis parti sur une utilisation du driver de stockage device-mapper et des LVM avec les thin-pool. Pour ce faire, sur mon ubuntu-xenial, il faut donc, avant d’installer docker préparer les choses suivantes :

  • installer les packages lvm2 et thin-provisioning-tools
  • préparer un LV en mode thin pour stocker les images docker

J’ai plus ou moins suivi la doc qu’on trouve là https://docs.docker.com/engine/userguide/storagedriver/device-mapper-driver/. Mon volume logique s’appelle space/docker-thin-pool et j’ai donc créé /etc/lvm/profile/space-docker--thinpool.profile qui contient ça:

1
2
3
4
activation {
  thin_pool_autoextend_threshold=80
  thin_pool_autoextend_percent=20
}

et donc, dans /etc/docker/daemon.json:

1
2
3
4
5
6
7
8
9
10
11
12
13
{
  "storage-driver": "devicemapper",
   "storage-opts": [
     "dm.thinpooldev=/dev/mapper/space-docker--thinpool",
     "dm.use_deferred_removal=true",
     "dm.use_deferred_deletion=true"
   ],
  "log-driver": "json-file",
  "log-opts": {
    "max-file": "2",
    "max-size": "10m"
  }
}

la dernière partie sur les logs est importante, sinon docker garde les logs de vos containers dans des sous-répertoires de /var/lib/docker, qui peut grossir assez vite pour peu que le conteneur logge beaucoup, on lui demande donc de ne garder que 20m de log par conteneur au maximum (on peut changer ces paramètres pour chaque conteneur quand on le lance)

Quand j’aurais le temps, je vais aussi essayer d’utiliser le driver ZFS, puisque ZFS a l’air d’être la panacée du stockage et que je n’y connais strictement rien, ça me permettra de me familiariser avec un cas d’utilisation concret…

Une fois que le stockage est préparé, il suffit de lancer le démon docker (sudo service docker start) et si tout est bien configuré, on devrait pouvoir lancer un tout petit conteneur hello-world qui vient du hub :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
$ docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
78445dd45222: Pull complete
Digest: sha256:c5515758d4c5e1e838e9cd307f6c6a0d620b5e07e6f927b07d05f6d12a1ac8d7
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://cloud.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/engine/userguide/

Et donc, tout fonctionne correctement, c’est magique, on peut passer à l’installation de rancher.

Installation du serveur rancher

Le serveur rancher est plutôt facile à installer. C’est un conteneur qui contient tout ce qu’il faut pour faire fonctionner le service (serveur HTTP pour exposer l’api, serveur base de données pour stocker les utilisateurs et les informations sur votre cluster…)

Dans mon cas, je le lance comme ça:

docker run -d --restart=unless-stopped --name rancher-server -p 8080:8080 -v /opt/rancher/mysql:/var/lib/mysql rancher/server:stable

  • -d detache le process du shell - si on ne donne pas cette options et qu’on fait ^C, on tue le serveur, c’est dommage
  • --restart=unless-stopped si le service docker redémarre, il va, par défaut redémarrer tous les conteneur définis au moment où il a été arrêté. Cette option permet de redémarrer ce conteneur uniquement si il n’a pas été stoppé (docker stop) explicitement auparavant.
  • --name rancher-server on va appeler notre conteneur rancher-server pour pouvoir s’y référer facilement quand on aura à le retrouver, sinon docker lui attribue un id sous forme d’un hash de 64 caractères comme c3be5bdc3b3f302dea3a840618293321ec38280e77450e5ac213f145134ab04a - ces hash fonctionnent comme les “tree-ish” de git, on peut ne spécifier que le début, ce qui est bien pratique, mais c’est toujours mieux de donner un nom explicite aux services, à mon avis…
  • -p 8080:8080 expose le port 8080 sur la machine locale en le mappant sur le port 8080 du conteneur. C’est à dire qu’après lancement, on peut faire une connexion TCP sur le port 8080, et c’est, si tout va bien, le serveur lancé par le conteneur qui va répondre…
  • -v /opt/rancher/mysql:/var/lib/mysql. Une option très importante pour un déploiement en production. Docker va monter le répertoire local /opt/rancher/mysql sur le répertoire /var/lib/mysql du conteneur. Ce qui veut dire que toutes les données écrites par le mysql du conteneur resteront sur notre serveur. Si on ne fait pas ça, on perd toute la base de données à chaque redémarrage du serveur rancher
  • rancher-server:stable le nom du conteneur à lancer, avec le tag stable - on peut jouer avec le feu et essayer le tag latest, mais comme mon besoin est juste de lancer quelques conteneur, je reste prudent.

Je vous invite à lire la documentation docker pour plus d’information sur la pléthore d’options qu’il fournit pour lancer un conteneur, et aussi celle de rancher (https://docs.rancher.com/rancher/v1.5/en/installing-rancher/installing-server/) - elle discute des différentes options d’installation, des règles de pare-feu à mettre en place, etc…

Un bonus intéressant de docker, là, c’est que comme le service docker est lancé automatiquement, si la machine redémarre, le conteneur rancher-server sera relancé (avec les données mysql stockées dans /opt/rancher/mysql) et donc, on n’a pas besoin de définir un service ou un script de démarrage. Le serveur rancher sera démarré en même temps que le système - quand on y pense, c’est quand même pas mal pour les gros feignants comme moi :-P

Une fois que le serveur est démarré, a fait les migrations, le service est prêt et écoute sur le port 8080. Si vous vous connectez à la machine, vous verrez une jolie interface utilisateur avec… quasiment rien. Le premier truc à faire est de créer un utilisateur admin - en choisissant un système pour l’authentification des utilisateurs - plusieurs sont proposés - moi, j’ai choisi d’utiliser la base locale - je n’ai pas encore expérimenté avec les autres…

Le serveur rancher n’est qu’une partie de ce qui est nécessaire pour que rancher puisse contrôler votre cluster, il faut ajouter des machines (qui font fonctionner un agent rancher - qui est, évidemment, un container !) à votre cluster. Notez bien que toutes ces opérations peuvent aussi, et heureusement ! se faire en dehors du clickodrome, avec des scripts.

L’environnement “Default” utilise le système de cluster développé par Rancher - Cattle - mais peut contrôler un cluster swarm ou kubernetes. Ce qui rend ce logiciel encore plus intéressant de mon point de vue - on peut tester la même stack dans différent environnements… pour l’instant, je me contente de Cattle par défaut puisque ça fonctionne très bien.

C’est assez simple, dans l’environnement “Default” (on peut en créer plusieurs), on peut “Add a host” en haut à droite - ou suivre le menu “Infrastructure -> Hosts -> Add a Host”. On est alors devant un autre dialogue, qui permet d’ajouter des serveurs au cluster de l’environnement “default”.

En gros, pour lancer un agent rancher et qu’il puisse être utilisé dans votre cluster rancher, il suffit qu’il ait une installation d’un docker supporté et puisse communiquer par le réseau avec - il utilise de l’IPSEC sur ipv4 donc il faut laisser passer le port TCP 500 pour ISAKMP et le port UDP 4500 pour de ipsec/UDP.

On note qu’on peut spécifier une adresse IP publique (item 4.) pour la machine - c’est important dans certains environnement d’écouter sur la bonne adresse, mais généralement, il se débrouille très bien.

Enfin, cette page vous donne la commande à lancer (item 5.) sur la machine cible pour lancer votre agent… vous la copiez et l’éxécutez sur votre serveur et c’est tout :)

Vous noterez que le conteneur lancé est privilégié. C’est normal, c’est un super-conteneur qui en lance d’autres, qui les tue, qui les redémarre, qui change les configurations, il doit donc avoir accès au démon docker de la machine comme si c’était un utilisateur docker de la même machine, c’est pour ça qu’il requiert l’accès à /var/run/docker.sock, d’où les options --privileged -v /var/run/docker.sock:/var/run/docker.sock utilisées.

une fois que vous avez démarré la commande et fermé la fenêtre, une nouvelle machine devrait apparaître dans la liste des machines… et on voit que l’agent démarre quelques services indispensables au fonctionnement de rancher, dans des stacks systèmes :

  • metadata : comme dans tout cluster qui se respecte, un service qui permet de récupérer simplement, par HTTP l’état du cluster. Les conteneurs déployés, leurs noms, des informations techniques, les volumes, les certificats, etc
  • network-manager : comme son nom l’indique, c’est le service qui assure l’établissement des connexions réseau au sein du cluster. Chaque conteneur se voit attribué une ou plusieurs addresses IP internes (dans la plage 10.42.0.0/16) et c’est ce service qui les gère
  • ipsec : assure le routage au sein du cluster. Ainsi on peut communiquer par IP entre deux conteneurs déployés sur des machines différentes de façon transparente.
  • scheduler: le service qui lance les conteneur et s’assure que les règles d’échelle sont respectées (“déployer 3 conteneurs nginx et 1 conteneur base de données…”)
  • healthcheck: un service qui surveille les conteneurs en appelant leur healthcheck - qu’on peut paramètrer en définissant le conteneur…

Vous avez maintenant un cluster prêt à démarrer des conteneurs. Je vous invite à en déployer quelques une pour essayer - il y a pas mal de templates prédéfinies, plus ou moins bien faites pour déployer toute sorte de services sur votre cluster. Moi j’aime bien la stack “Prometheus”, c’est super joli, et ça montre bien les possibilités du bazar - attention tout de même, il faut une jolie bécane, c’est une belle usine à gaz ;)

Il reste à faire un peu de réseau pour que les ports exposés par les conteneurs soient accessibles depuis l’extérieur. Ce que je fais généralement, c’est utiliser un conteneur haproxy qui expose uniquement les port HTTP et HTTPS, et le brancher aux services que j’ai déployés. Justement, les développeurs de rancher ont prévu le coup avec leur image rancher/lb-service-haproxy. Ca sera l’objet d’un prochain article - où je discuterai aussi de la gestion et du renouvellement des certificats avec rancher et letsencrypt.

Comments