L’objectif de cet article est de vous montrer comment fabriquer un conteneur simple - un serveur HTTP pour des fichiers statiques, puis de le déployer sur le cluster rancher mis en place lors des précédents.
Fabrication du conteneur
On va partir d’un site octopress (un moteur de blog statique - celui avec lequel est fabriqué ce que vous lisez), et monter un conteneur avec les fichiers générés par Octopress.
J’ai créé un projet sur git.arzur.net que vous pouvez cloner. Pour démarrer, il vous faut un interpréteur ruby (à partir de la verson 1.9.4). J’utilise rbenv sur ma machine pour avoir plusieurs versions et environnements - brew install rbenv
. Vous pouvez donc changer les fichiers .ruby-version
et .ruby-gemset
pour les adapter à votre environnements - utiliser rbenv et les gemset est une bonne idée, car pour supprimer toutes les gems installés, il vous suffit de lancer rbenv gemset delete 2.4.1 octopress-blog
, c’est pratique une fois qu’on a fini d’expérimenter…
On commence par installer octopress
1 2 3 4 |
|
puis on génère le site…
1
|
|
ça crée un répértoire public/
avec les articles de votre blog. J’en ai ajouté un en créant le projet, pour que le blog ne soit pas complètement vide.
On peut passer à la fabrication du conteneur. J’ai ajouté un Dockerfile, on ne peut plus simple:
1 2 3 4 |
|
On copie les fichiers de public/
dans /usr/share/nginx/html
de l’image de base nginx:alpine
, et c’est tout !
1 2 3 4 5 6 7 8 9 10 11 |
|
Vous devriez avoir maintenant dans votre docker une image nommée rancher-blog
:
1 2 3 4 |
|
vous pouvez lancer le conteneur sur votre machine locale:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
Dans un navigateur, ça donne ça:
avec ça, le conteneur avec votre mini-blog est prêt sur le docker de votre poste de travail, mais il n’est pas encore accessible depuis votre serveur rancher, qui ne saurait tirer l’image que vous venez de créer depuis votre machine. Il va falloir la publier sur le Docker Hub
le docker hub
Ce service est un répertoire d’images publiques dans lequel chacun peut pousser ses images. Il est totalement gratuit pour les images publiques (que tout le monde peut importer) et payant quand on veut pousser des images privées.
j’ai poussé mon conteneur comme suit:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
On a maintenant publié notre conteneur sur le Hub et notre serveur rancher va pouvoir le télécharger et l’executer.
Première stack rancher
on commence par s’assurer qu’on a bien les paramètres dans l’environnement pour se connecter à notre serveur rancher en sourçant le petit script qui contient l’adresse du serveur et les clés. On lance à nouveau rancher ps
, pour être sûr.
dans la branche rancher-stack
(git checkout rancher-stack
) du projet, j’ai ajouté les fichiers docker-compose.yml
et rancher-compose.yml
suivants pour définir la stack rancher-blog
(attention: rancher
et rancher-compose
prennent le nom du répertoire courant comme nom de la stack si vous ne le précisez pas avec l’option -p
):
docker-compose.yml
1 2 3 4 5 6 7 8 9 |
|
- il va tirer l’image publique
zuzur/rancher-blog
(bien sûr, vous devez changer ce nom pour utiliser votre propre image !) - le conteneur expose le port 80 - si vous avez déjà un serveur HTTP en place, ça ne va pas fonctionner.
- les
labels
permettent d’ajouter des méta-données pour le serveur rancher. Ici, par exemple, on demande au serveur de toujours rafraîchir (pull
) l’image quand il démarre le conteneur. Le labelrole
est juste informatif pour le moment, mais on verra plus tard à quoi il sert.
on peut d’hors et déjà lancer le service avec rancher:
1 2 3 4 5 |
|
-d
pour détacher le terminal après la sortie, sinon la commande affiche le log de tous les services dans la stack, et reste jusqu’à ce que vous l’arrêtiez.
si on envoie un navigateur sur l’adresse de notre serveur, c’est magique…
rancher-compose.yml
Pour un service aussi simple, on aurait pu complètement se passer de cette configuration, mais j’ai choisi d’en mettre un pour illustrer un truc important avec rancher, la notion de healthcheck
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Avec cette configuration, le service healthcheck
de rancher va essayer toutes les 10 secondes (interval
) de se connecter au port 80 (port
) du conteneur, et au bout de 3 essais infructueux (unhealthy_threshold
), va supprimer le conteneur et en créer un à partir de l’image (strategy
). Pour tester il tente la requête HTTP GET / HTTP/1.0
(request_line
), laisse 1 seconde au conteneur pour démarrer (initialization_timeout
) et le déclare en bonne santé après 2 essais fructueux (healthy_threshold
)1.
Le problème, là, c’est que on voulait déployer un autre service, sur le port 80, on serait bien embêté parce qu’on ne pourrait pas le faire. Notre conteneur avec port 80 exposé par docker empêcherait un autre service d’écouter sur le même…
La solution est d’utiliser un conteneur, développé spécialement par rancher qui fournit un service de répartition de charge (load-balancer) pour les services déployés dans le cluster.
Load balancer
Dans la branche rancher-stack-haproxy
du projet (git checkout rancher-stack-haproxy
), j’ai poussé une nouvelle version des fichiers docker-compoose.yml
et rancher-compose.yml
avec le contenu suivant:
docker-compose.yml
:- le port
80
du conteneur n’est plus exposé à l’extérieur, il ne sera accessible que par un service qui fonctionne dans le cluster rancher. - on déploie un nouveau service, basé sur l’image
rancher/lb-service-haproxy:v0.7.5
- ce service écoute sur le port 80
- comme le service haproxy a besoin d’interagir avec l’API (pour lire les méta-données, retrouver les informations sur les healthchecks, etc…), il faut faire en sorte que l’agent pousse les clés afin de s’y connecter dans l’environnement au moment du démarrage du conteneur - c’est le but des étiquettes
io.rancher.container.agent.role
etio.rancher.container.create_agent
2
- le port
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
rancher-compose.yml
- on a ajouté des règles pour le service
load-balancer
pour connecter notre servicerancher-blog
- on spécifie le port source et le port destination
hostname
sert à router correctement - le load-balancer de rancher support l’hébergement de serveurs virtuels basé sur leur nom- et enfin, on branche cette règle au conteneur
rancher-blog
dans la même stack (service:
) - on peut le brancher à un service dans une autre stack avec la notation<stack>/<service>
, par exemple dans notre cas:rancher-blog/rancher-blog
- on a ajouté des règles pour le service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
- on commence par supprimer l’ancienne stack - on n’est pas obligé de le faire mais si on ne le fait pas,
rancher-compose
râle car les fichiers ne sont pas synchronisés. On peut alors lui demander plutôt de mettre à jour le service (option-u
)3 - après la création de notre stack, on voit que des accès HTTP sont régulièrement faits dans le conteneur. Exactement 2 espacés de 10s
- le service
healthcheck
de rancher vérifie toutes les 10s que notre mini-blog répond. Sans réponse, il arrête le conteneur, le supprime et en redémarre un nouveau. - le conteneur
load-balancer
lui aussi vérifie la santé du backend. Si il ne répond pas, il cesse d’envoyer des requêtes au conteneur (comme on n’en a qu’un, on aurait dans ce cas des erreurs502 - Bad Gateway
renvoyées aux clients)
- le service
répartition de la charge
Si notre service a besoin de beaucoup de resources - on peut demander très facilement à rancher de lancer plusieurs conteneurs “blog” et le load-balancer va répartir automatiquement les requêtes entre les conteneurs.
1 2 3 |
|
on a donc démarré 3 conteneurs pour notre blog. on peut le vérifier
1 2 3 4 5 |
|
On voit bien nos 3 conteneurs en bonne santé (vérifiés par healthcheck
). Si on vérifie les logs du service blog, on va voir tout d’un coup beaucoup plus d’accès:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
des accès plus fréquents, c’est normal puisque healthcheck
et haproxy
on plus de services à surveiller…
remarques
utilisation de l’interface graphique
Dans cet article, vous noterez qu’à aucun moment je n’ai mentionné l’interface d’administration du cluster rancher. On peut réaliser toutes ces opérations avec cette UI, évidemment, mais toutes ces actions ne sont alors pas répercutées dans les fichiers de configuration de la stack, et on ne peut pas en garder trace. Donc, j’ai plutôt tendance à éviter de l’utiliser, sauf pour des manipulations “one shot” que je sais répercuter facilement dans les fichiers correspondants. Le nombre de conteneurs pour un service, par exemple… Je vous invite à manipuler un peu, à voir les effets de vos commandes (upgrades, rollback, etc… dans l’interface)
système de publication
Une autre remarque d’ordre général. Pousser un nouvel article avec ce système est plutôt fastidieux.
Il faut:
- écrire un nouvel article en markdown (
bundle exec rake new_post['my new post']
) - appeler
bundler exec rake generate
dans le répertoire du blog - construire la nouvelle version du conteneur (
docker build -t rancher-blog .
) - tagger l’image générée avec
zuzur/rancher-blog:latest
- la pousser sur le docker hub
- mettre à jour le service
rancher-blog/blog
dans rancher pour qu’il utilise la nouvelle image
Quand je parlais de “canon à mouches”… C’est bien trop compliqué. Et automatiser tout cela sera l’objet d’un futur article…
-
Si vous connaissez les healthcheck de haproxy, c’est exactement le même principe et c’est normal, puisque le service
healthcheck
, c’est haproxy, on peut le voir en s’y connectant:rancher exec -it healthcheck/healtcheck /bin/sh
. On voit que c’est bien haproxy qui fonctionne (avecps ax
), et on peut retrouver le healthcheck en question dans/etc/haproxy/haproxy.cfg
…↩ -
Dans ce cas, il va lancer un nouveau conteneur, avec la nouvelle configuration, et arrêter l’ancien. Si vous vous apercevez qu’il y a un soucis avec cette config, on peut faire un ‘rollback’ (option
-r
) et dans ce cas, il va arrêter et supprimer le nouveau conteneur, et redémarrer l’ancien qu’il avait simplement arrêté. C’est parfait pour faire des upgrades de service sans interruption. Il y a de nombreuses options documentées que je vous invite à retrouver dans la doc…↩