Comment Jamshake est déployé

Jean-Baptiste Dusseaut
Arpinum
Published in
7 min readDec 20, 2015

Un problème non négligeable de tout produit est comment il est déployé et exploité. Quand en plus, on parle d’une startup, il faut prendre en compte plusieurs contraintes :

  • L’équipe de développement est petite, souvent une seule personne. Pour optimiser le temps de développement, il faut choisir une solution qui permette d’y passer le moins de temps possible
  • Le seul développeur n’est souvent pas du tout un expert de l’exploitation, et encore moins un DevOps, mieux vaut donc capitaliser sur une solution permettant d’abstraire un maximum de choses
  • Le cash : il y a de bien belles solutions en ligne, mais l’argent étant le nerf de la guerre, il faut trouver le bon ratio coût / temps / scalabilité
  • Pouvoir changer d’avis facilement : on ne sait pas de quoi demain sera fait, toute technologie de déploiement imposant des contraintes fait des choix qui pourront s’avérer dangereux pour l’avenir. Il faut donc garder toutes les options ouvertes pour la suite.

Jamshake n’échappe pas à la règle : j’ai eu droit à pas mal de coups de mains des arpinumiens, mais j’étais globalement tout seul, je ne suis pas un devops, et chaque minute qui n’est pas passée à améliorer le produit est une minute que je ne pourrai jamais rattraper.

Disclaimer

Le but de cet article n’est pas de vous présenter Google Container Engine et Kubernetes, vous trouverez de biens meilleurs articles pour ça sur le net. J’ai plutôt envi de vous dire pourquoi nous avons trouvé que c’était la solution que nous voulions, et quelles sont les quelques astuces que nous avons du mettre en oeuvre.

Migration vers Kubernetes et Google Container Engine

Jamshake a d’abord tourné sur des instances EC2 configurées et gérées via des scripts Ansible. Même si Docker a pas mal simplifié la quantité de choses à installer, j’étais assez instisfait d’avoir du configurer les serveurs moi même, plus ou moins bien. Mes compétences limitées pour gérer le réseau m’avaient également obligées à exposer publiquement quelques apis que j’aurai préféré cacher. Mais, j’avais parié que l’univers Docker allant très vite, quelqu’un, quelque part, ferait enfin une solution de Container As A Service digne de ce nom. Nous avons scruté attentivement le marché, et Kubernetes était dans notre liste d’attente.

Donc oui, Kubernetes résoud pas mal de nos soucis :

  • C’est une couche d’abstraction par dessus les machines physiques : je ne gère plus des machines, mais un cluster de containers
  • Sa notion de service me permet incroyabement facilement de décider quelques apis j’expose au reste du monde, tout en permettant aux membres de mon cluster de se découvrir et de se parler
  • Et le bonus : je n’ai pas besoin d’installer moi même mon cluster Kubernetes et de faire en sorte que son master ne tombe jamais : Google Container Engine s’en charge pour moi

Sans vouloir balancer la concurrence, après des années à avoir essayé à peu prêt tout ce qu’il est possible d’essayer dans le domaine du déploiement facile, Container Engine gagne sur toute la ligne :

  • Il garde toutes mes options ouvertes: il fait tourner du Docker, donc tout ce qui me passe par la tête (adieu les Buildpacks)
  • Il me permet de ne pas passer une seule minute à configurer des machines ou du réseau
  • Il gère nativement les rolling updates, le scaling des containers, etc : pour accepter plus de charge, il m’a suffit d’ajouter un noeud, et de dire à mes replicas controllers de faire tourner plus de pods.

J’ai fait le choix de ne pas vous bassiner avec la base de Kubernetes, comme je le disais le net est rempli de bon articiles sur la question. En revanche je peux vous parler des queqlues astuces que nous avons mis en oeuvres qui nous sont propres.

Un nginx pour les gouverner tous

Même si Kubernetes est tout à fait d’accord pour exposer plusieurs services, il va vous en coûter un loadbalancer à chaque fois. De plus, vu que nous voulions gérer du https et que le load balancer google à ce moment là ne le gérait pas, nous avons décidé de déployer un Nginx dans notre cluster, qui serait le point d’entrée pour toutes nos apis et services.

La contrainte bien sûr c’est que nous avons du fabriquer une image docker spécifique à nos besoins, mais surtout, il impose un ordre de déploiement sur notre cluster. En effet, NGinx ne veut pas démarrer s’il ne trouve pas tous les upstreams que nous avons défini. Nous devons donc d’abord nous assurer que tous les autres services sont déployées avant de lancer notre service Nginx.

Clairement une amélioration pour la suite est de réussir à ne plus avoir besoin de fabriquer une image spécifique. La piste est surement d’utiliser les volumes Kubernetes, notamment les volumes git, qui nous permettraient de mettre toute la configutation dans un entrepôt, et juste démarrer une image nginx générique. Les nouveaux Ingress controllers ceci dit ont l’air également d’être la nouvelle bonne solution.

Le cas ZooKeeper

Au coeur de Jamshake, il y a Jamstudio. Ce que voit surtout les utilisateurs, c’est un séquenceur dans le navigateur acceptant des modifictions concurrentes en temps réel (un peu comme un google doc du séquenceur on va dire). Mais c’est mignon de vouloir accepter les modifications de N utilisateurs simultanés sur la même séquence, mais comment mettre ça en oeuvre côté serveur sans tomber dans l’enfer des transactions ? Cela mériterait surement une article à part entière, mais pour la faire courte, chaque séquence est un acteur, et nous utilisons ZooKeeper pour que nos différentes instances élisent qui va traiter telle ou telle séquence.

Il faut théoriquement 3 membres dans un cluster ZooKeeper pour être sûr que nous n’allons perdre aucune information en cas de souci. En terme de configuration en revanche, il faut dire à chaque instance zookeeper qui sont ses voisins, ils ne sont pas capables de se découvrir tout seul. Mais du point de vue client de zookeeper, on doit théoriquement ne pas connaître les instances spécifiques, mais laisser nos demandes se loadbalancer tranquilement entre les 3. Avec un replica controller, nous pouvons facilement monter 3 instances de zookeeper, mais comment les faire discuter entre elles ? Pas le choix pour le moment, comme il n’y a pas moyen de pointer sur un pod en particulier lancé par un replica controlller, nous avons du :

  • Faire un replica controller par instance de zookeeper
  • Faire un service pour chacun, pour qu’ils se parlent
  • Faire un service pour les clients, envoyant les demandes aux trois instances en round robin
  • Utiliser une image custom de zookeeper, permettant de définir par variable d’environnement comment parler à ses pairs.

La taille compte

Nous avons du bien entendu pas mal tatonné pour trouver le bon type d’instance pour les membres de notre cluster Kubernetes. C’est assez logique, mais une fois que vous avez créé votre cluster, vous ne pourrez ajouter que des machines du même type. De plus, container engine vous facture le master quand vous arrivez à 6 noeuds. Donc il nous fallait trouver le bon équilibre : des machines pas trop grosses pour qu’elles ne nous coûtent pas un bras, pas trop petites pour que tout de même, ça tourne (nous avons essayer des micros, il n’y a même pas ce qu’il faut pour faire tourner les containers nécessaires à kubernetes). Combien de vcpu également par machine ? Kubernetes, si on ne dit rien, considère qu’un pod prend 0.1 vcpu. C’est bien entendu surchargeable, mais c’est comme ça qu’il va décider ou faire tourner les pods. Avoir deux nouveaux CPU par nouvelle instance serait donc appréciable, mais il faut également faire attention à la mémoire dont nos pods ont besoins… Il faut prendre en compte aussi ce qu’il se passe si un noeud tombe… Mieux vaut avoir toujours un noeud de marge ou moins occupé pour qu’il absorbe la charge si un autre noeud tombe (ce qui arrive par exemple, quand vous mettez à jour la version de kubernetes).

Nous n’avons pas été très scientifiques dans nos tests, et par tatonnement nous sommes partis sur des g1-small. Nous en avons désormais 5 d’ailleurs.

MongoDB il est ou ?

Nous avons essayé également de mettre dans un premier temps MongoDB dans le cluster, mais finalement il tourne sur ses propres instances à part. Il était mine de rien assez gourmand, en plus pour plus de sécurité, nous préférions vraiment que notre replica set ait la garantie de tourner sur des instances différentes : si le master et le slave tournent sur le même noeud de kubernetes, quand il tombe, nous n’avons plus de mongo…

Ceci dit, au sein d’un projet GCP, il est assez facile même depuis un pod kubernetes d’aller parler à des instances du projet. Nous n’avons pas pu non plus choisir d’utiliser compose.io ou mongolab, étant donné qu’ils ne proposent pas GCP comme cible de déploiement, ou alors pas dans la bonne zone.

Conclusion

J’espère que cet article vous a donné un avant goût de pourquoi nous aimons Kubernetes et container engine, et des réflexions que nous avons du avoir pour déployer Jamshake. Il nous reste pas mal de chemin à parcourir, notamment au niveau du monitoring et de la configuration de nos pods, mais en tout cas, rarement une technologie nous aura donné un telle impression d’être le plus simple qui fonctionne (pas simpliste) sans pour autant nous fermer des options. Kubernetes évolue également assez vite, ainsi que GCE. Il faut bien sûr surveiller qu’on ne tombe pas dans la surcomplexité des concurrents, mais pour le moment, ça reste le meilleur ratio coût/service du marché à nos yeux.

Originally published at tech.arpinum.fr on December 20, 2015.

--

--