This repository has been archived on 2021-03-01. You can view files and clone it, but cannot push or open issues or pull requests.
internship-novaquark/report/R_technique.md

26 KiB

Analyse des plates-forme d'informatique en nuage

Objectifs

Les serveurs du jeu seront regroupés au sein d'une structure permettant facilement d'adapter leur nombre en fonction du besoin (fortement en corrélation avec le nombre de joueurs inscrits et connectés).

Depuis quelques années, un grand nombre de plates-forme se développent, fournissant un service de mise à l'échelle clef en main ; mais des solutions libres sont également disponibles lorsque l'on désire s'occuper du service manuellement.

Ici, l'objectif était de trouver les avantages et les inconvénients de chaque fournisseur et de contre balancer cette analyse par rapport aux solutions à mettre en place soi-même.

Alternatives possibles

Développer une solution de mise à l'échelle automatique, parfaitement adapté à nos besoins n'était pas envisageable : cela aurait réclamé une équipe dédiée au développement de cette solution alternative. Comme ce n'est pas le cœur de métier du studio, cette solution a d'emblée été écartée.

Propositions retenues

Les fournisseurs de service d'hébergement en nuage retenus sont :

  • Amazon Web Services Elastic Cloud 2 ;
  • Digital Ocean ;
  • Gandi ;
  • Google Compute Engine ;
  • Ikoula ;
  • Linode ;
  • Lunacloud ;
  • Numergy ;
  • Rackspace ;
  • Windows Azure.

\vspace{0.5em}

Les solutions libres retenues sont :

  • Ecalyptus ;
  • Libvirt.

Résultats obtenus

La solution d'Amazon a été retenue pour sa grande diversité et le support dont nous bénéficions en tant que start-up en développement.

Vous trouverez en annexe A le tableau de résultats comparatifs effectué dans le cadre de cette analyse.

Discussions complémentaires

Organisée par l'incubateur à destination des start-up intéressées, une journée nous a permis de discuter avec des architectes d'Amazon afin de connaître leur avis sur la structure que nous envisagions et comment cela pourrait s'intégrer au sein de l'offre d'Amazon.

\newpage

Analyse des outils de déploiement automatique

Objectifs

Les outils de déploiement automatique vont éviter toutes les problématiques de configuration des différents serveurs que peuvent être le manque d'harmonisation, les difficultés de réalisation des mises à jour, les besoins de tests de l'environnement avant la mise en production, le redéploiement rapide d'une version stable ou précédente.

\vspace{1em}

Il est nécessaire de configurer de nombreuses machines dans différents cadres :

  • ajout d'un nouveau serveur au sein du cluster (lui fournir le contexte et l'annoncer) ;
  • configuration d'une machine virtuelle à destination des développeurs ;
  • configuration des serveurs annexes (sites web, bases de données, monitoring, ...).

Alternatives possibles

Dans de nombreux environnements chaque machine est configurée à la main par un administrateur système ; à chaque étape, une erreur humaine peut venir interférer dans les procédures de mise à jour.

Cette solution est évidemment à écarter si l'on veut garder le contrôle de son système d'information et pouvoir faire face à tout type de problème.

Propositions retenues

Les outils de déploiment automatique retenus sont :

  • Ansible ;
  • Capistrano ;
  • CFEngine ;
  • Chef ;
  • Fabric ;
  • Puppet.

Résultats obtenus

Ansible a été la solution retenue : assez récente, elle n'impose pas l'installation préalable d'un programme sur chaque machine et est en constant développement depuis son lancement.

Vous trouverez en annexe B le tableau comparatif effectué dans le cadre de cette analyse.

\newpage

Analyse des solutions de virtualisation

Objectifs

La virtualisation peut nous être utile à plusieurs niveaux :

  • la mise en place d'un environnement de tests de plusieurs machines ;
  • l'utilisation d'un environnement standardisé pour les développeurs ;
  • le déploiement d'environnements standardisés et minimalistes.

\vspace{1em}

Si dans un contexte de production les serveurs sont utilisés à une valeur proche de leur puissance maximale, pour un environnement de test, il est tout à fait envisageable de regrouper les différentes machines nécessaires au sein de plusieurs machines virtuelles d'une même machine physique, dédiée aux tests.

\vspace{1em}

Pour les développeurs, l'installation de l'environnement de développement n'est pas toujours facile (surtout s'ils doivent alterner entre deux systèmes d'exploitation différents) et requiert une certaine standardisation (versions précises de certaines bibliothèques, paquets particuliers). La virtualisation permet de leur fournir un environnement clef en main qu'ils n'ont pas à maintenir.

\vspace{1em}

Sont apparues ces dernières années des technologies prônant le déploiement en production de machines virtuelles ou conteneur. Cela permet d'avoir une série de machines hôtes dédiées au déploiement et des machines virtuelles standardisées et minimalistes (pas d'écoute SSH par exemple).

Propositions retenues

Sous GNU/Linux, les solutions retenues sont :

  • Docker ;
  • Libvirt ;
  • Linux Kernel-based Virtual Machine (KVM) ;
  • Linux Containers (LXC) ;
  • Oracle VirtualBox.

Sur Windows, seul Oracle VirtualBox répondait aux contraintes, en particulier car le logiciel Boot2Docker s'intègre uniquement à cette solution.

Résultats obtenus

Globalement, les technologies de virtualisation de type hyperviseur n'ont plus guère d'avantages face aux conteneurs : ils permettent de garantir un niveau de sécurité et d'isolation quasi-identique, sans avoir la lourdeur de gestion d'autant de noyaux que l'on veut lancer de machines.

\vspace{1em}

Docker et LXC ont donc été retenus. La première version stable de Docker est sortie pendant mon stage, ce qui a été déterminant pour le choix de cette solution. LXC reste dans la course pour tout ce qui nécessite une configuration avancée que ne permettrait pas de faire Docker.

\newpage

Recherche d'une solution de gestion de chat ingame

Objectifs

Le chat est un élément essentiel dans un jeu en ligne massivement multijoueurs. Pour nous éviter de réinventer la roue, il a fallu faire le choix d'une technologie existante et répondant à nos besoins.

Alternatives possibles

Ajouter au sein du serveur de jeu la gestion du chat aurait pu être envisageable. Cependant, il peut être fastidieux de programmer un système complet (par exemple il faut prévoir les systèmes de modération, de répartition de charge, ...). Afin d'éviter d'ajouter inutilement du temps au développement, il a donc été préféré le choix d'une technologie existante.

Propositions retenues

Les principales solutions existantes sont les suivantes :

  • IRC ;
  • XMPP.

Résultats obtenus

XMPP a été retenu comme solution de chat. En effet, le serveur ejabberd, écrit en Erlang, permet aisément de déployer un cluster et ainsi répartir la charge d'une manière relativement transparente, comparé aux autres implémentations et des autres protocoles.

\newpage

Recherche d'une solution de gestion de l'authentification

Objectifs

L'authentification est un point crucial à ne pas négliger lorsque l'on développe un jeu massivement multijoueur. En effet, c'est elle qui permettra de contrôler l'accès aux différents services du jeu :

  • site web ;
  • forum ;
  • jeu ;
  • système de chat dans le jeu ;
  • ...

Alternatives possibles

L'implémentation d'une solution maison est encore une fois envisageable. Elle n'a pas été retenue pour éviter d'avoir à gérer les vulnérabilités qui pourraient être découvertes une fois le jeu lancé.

\vspace{1em}

D'autre part, étant donné que plusieurs services devront pouvoir s'authentifier en un point central, cela évite de fournir un travail pour rendre compatibles avec une solution maison les logiciels déjà opérationnels avec des solutions d'authentification standards.

Propositions retenues

Les solutions de centralisation d'authentification courantes sont :

  • LDAP ;
  • Kerberos, PKINIT ;
  • RADIUS.

Résultats obtenus

LDAP est une solution utilisée universellement dans le cadre de l'authentification et du stockage d'informations sur les utilisateurs (Active Directory dans les environnements Windows, LDAP directement dans les environnements Unix traditionnels ou en plugin dans de nombreuses applications demandant une authentification).

\vspace{1em}

Afin de gérer plus simplement l'authentification entre les différents services au sein du client de jeu (chat, jeu, contenus, marché) et compte tenu du fait que plusieurs serveurs vont être contactés par le client au fil de l'évolution du joueur dans le jeu, l'authentification par un serveur Kerberos puis l'échange de tickets permet de s'affranchir de trop nombreuses authentifications répétées.

\newpage

Conception d'un programme de relevé de métriques système

Objectifs

Afin de pouvoir déterminer le moment optimal pour démarrer un nouveau serveur, il faut relever un certain nombre de métriques puis déterminer dans une première phase l'indicateur le plus adapté.

Cadre imposé

Avant mon arrivée dans l'entreprise, mon maître de stage avait fait le choix de la base de données InfluxDB pour le stockage des métriques (aussi bien système que business).

Propositions retenues

La base de données retenue est toute récente ; c'est pourquoi il n'existait encore aucun programme pour faire simplement un relevé de métriques système (consommation CPU, mémoire vive disponible, bande passante, ...). Il a donc été choisi de le développer.

\vspace{1em}

Le langage retenu fut le Go : le langage compilant les binaires statiquement, il n'y aurait donc rien de plus à installer sur les machines sur lesquelles nous devions faire les relevés.

Résultats obtenus

Le projet a été terminé et mis en ligne publiquement sur GitHub sous licence CC0.

Il a ensuite été annoncé à la communauté InfluxDB qui lui a fait un bon accueil : nous avons eu quelques retours sur des fonctionnalités manquantes et des membres de la communauté ont eux-mêmes pris la peine de modifier le code pour y faire des améliorations.

\vspace{1em}

En sus de ce programme de relevé de métriques, il m'a été demandé de réaliser un tableau de bord permettant de visualiser en temps réel les données compilées. Le premier tableau de bord a été réalisé directement avec le serveur web de la base de données allié à la bibliothèque JavaScript Cubism (voir figure \ref{fig:img/cubism.png}) : il permet de visualiser en un coup d'oeil l'état des différents serveurs. Le deuxième tableau de bord utilise la bibliothèque JavaScript Graphana (voir figure \ref{fig:img/grafana-monitor0.png}) : il montre sous une autre forme les données enregistrées dans la base de données en permettant plus facilement de comparer sur le long terme l'utilisation des ressources sur les machines en les corrélant avec les événements survenus dans le jeu.

%%fimage(img/cubism.png, 0.7, "Tableau de bord réalisé avec Cubism") %%fimage(img/grafana-monitor0.png, 1, "Tableau de bord réalisé avec Graphana")

\newpage

Conception de recettes de déploiement automatique de serveurs

Objectifs

Chaque serveur, que ce soit un serveur physique ou virtuel, doit pouvoir être reconfiguré rapidement en fonction des rôles qui lui sont attribués.

Certains rôles sont réutilisés d'une recette à l'autre : par exemple la machine virtuelle de développement est un serveur de jeu sur lequel tous les types de serveur de jeu sont disponibles et elle possède également une base de donnée locale.

Résultats obtenus

Les recettes qu'il a été nécessaire de développer sont les suivantes :

  • Serveur de monitoring : chaque serveur déployé assure son rôle grâce à la mise à disposition d'une base de données InfluxDB et à des outils permettant de visualiser la charge et les événements. Afin de toujours connaître l'état en temps réel des serveurs de jeu, chaque serveur configuré est inclus au sein d'un cluster de haute-disponibilité, grâce à LVS et Keepalived.
  • Serveurs de base de données : pour l'instant un seul type de base de données est utilisé : Redis. La recette doit permettre de déployer un serveur au sein d'un cluster Redis. À terme, un second type de base de données devra être déployée afin de disposer de stockage permanent.
  • Serveurs de jeu : le serveur de jeu étant décomposé en trois parties, trois recettes différentes sont nécessaires, en fonction du type de serveur que l'on veut déployer.
  • Machine virtuelle de développement : ces machines mises à disposition des développeurs doivent leur permettre de tester le jeu avec un serveur qui leur est fournis. Elles contiennent tous les composants nécessaires au bon fonctionnement du serveur de jeu.
  • Serveur de machines virtuelles/conteneurs : il s'agit de configurer un serveur fraîchement arrivé pour lui permettre d'exécuter des conteneurs (serveur de jeu, site web, etc.). Pour cela, une étape supplémentaire est nécessaire afin de qualifier une première fois les rôles à déployer sur le serveur. Par exemple, on peut distinguer les rôles suivants :
    • proxy frontal : cette recette crée un conteneur redirigeant le trafic vers d'autres machines virtuelles exécutant un service web. En effectuant aussi peu d'action que possible, il permet d'afficher une page d'erreur lorsque les autres services sont rendus inaccessibles;
    • site web commercial : cette recette crée un conteneur permettant d'afficher le site web du jeu via un serveur web correctement configuré;
    • forum : cette recette crée un conteneur exécutant le logiciel de forum;
    • serveur de mails à usage interne aux serveurs.
  • Serveur de supervision : cette recette crée un conteneur exécutant un serveur icinga (clone de nagios). Le conteneur est préconfiguré pour superviser les serveurs connus au moment de sa construction.

\vspace{1em}

Pour chaque recette, un certain nombre d'actions communes sont effectuées comme la mise à jour des paquets et la sécurisation générale (restriction de l'authentification par SSH, ajout de règles de pare-feu sommaires et diverses règles permettant de prévenir diverses attaques comme le DNS et l'ARP-poisoning suivant les machines).

\vspace{1em}

Certaines machines disposeront de deux cartes réseau : une publique et une privée. Un plugin pour Ansible a donc été développé afin de gérer la présence de ces deux interfaces, permettant d'adapter automatiquement les configurations des logiciels en fonction de leur besoin (usage interne ou externe au cluster).

\vspace{1em}

De nombreux modules Ansible existent afin de configurer des bases de données. Malheureusement, InfluxDB étant encore relativement récente, aucun module n'était disponible. Pour nos besoins, nous avons donc dû développer un module en Go (pour profiter d'un code compilé de manière statique avec ses bibliothèques, nous évitant ainsi d'avoir des prérequis sur les machines). Une demande de fusion de branche a été passée auprès de l'équipe d'Ansible afin de permettre l'exécution de programmes natifs (précédemment limité aux scripts).

\vspace{1em}

Les premières versions du serveur de jeu utilisaient le programme de compilation sbt-native-packager, cela générait les scripts d'initialisation. Lors de la mise en place des recettes de déploiement pour cette version du serveur, nous avons observé un bug dans le script d'initialisation à destination des systèmes Debian. Nous avons donc corrigé le problème et soumis le correctif au mainteneur de la solution.

\vspace{1em}

Un peu plus tard, afin d'enregistrer les actions effectuées par le programme de déploiement, il a été nécessaire de développer un nouveau module pour Ansible afin qu'il enregistre les actions qu'il effectue ainsi que les éventuelles erreurs qu'il rencontre. Cela permet d'avoir une traçabilité des déploiements et des mises à jour de l'ensemble du cluster.

\vspace{1em}

À noter que pour le moment, aucune solution n'a été trouvée pour éviter ou simplifier l'étape de compilation de noyau sur les serveurs exécutant les conteneurs. En effet, les serveurs nous sont livrés avec un noyau standard ne disposant pas de toutes les options nécessaires au fonctionnement de LXC.

Une première étape manuelle consiste alors à compiler un noyau renforcé (par l'application du correctif GrSecurity) avec des options plus appropriées à notre usage.

\newpage

Mise en place d'un processus de livraison du serveur à destination des développeurs du client

Objectifs

Notre jeu se basant sur un serveur, chaque personne utilisant le client (développeur ou graphiste) a donc besoin de se connecter à un serveur « stable » afin de pouvoir visualiser l'impact de ses modifications dans l'environnement de jeu.

Chaque développeur, pour ses tests peut avoir besoin d'une version particulière du serveur : une version avancée pour les développeurs travaillant sur l'intégration réseau, une version stable pour les graphistes, ... Et chacun peut avoir besoin de faire des tests dans un environnement particulier.

Ainsi, chacun devrait avoir accès à un serveur de jeu dédié et à un serveur de jeu global (lorsqu'il est question de faire des tests à plusieurs).

Cadre imposé

Le temps passé au développement est primordial comparé au temps passé à tenter de configurer l'environnement à partir de la documentation. Une solution clef en main où le moins de connaissances préalables nécessaires est un plus.

Propositions retenues

Pour la mise en place de ce processus, il a été retenu la mise à disposition :

  • de machines virtuelles pré-configurées : l'équipe serveur met à disposition du reste de l'équipe des fichiers .ova : un disque virtuel au format VMDK associé à une configuration de machine virtuelle au format OVF ;
  • de conteneurs associés à des scripts d'automatisation ;
  • d'un conteneur sur une machine dédiée : l'équipe serveur s'occupe de mettre en place les conteneurs en fonction des besoins de chacun, sans avoir à gérer les problèmes liés à la configuration personnelle des machines de chacun.

Difficultés

Chacune des solutions listée ci-dessus a été testée. La première solution a souffert d'un manque de consultation des besoins des développeurs : travailler dans une machine virtuelle en ligne de commande n'est en général pas à la portée des développeurs plus habitués à Visual Studio.

Comme première contre-mesure, une interface a été développée afin de masquer et centraliser les principales actions, voir la figure \ref{fig:img/ncurses-dev.png} en \pageref{fig:img/ncurses-dev.png}.

\vspace{1em}

%%fimage(img/ncurses-dev.png, 1, "Interface Curses de contrôle du serveur")

Dans un souci d'harmonisation des technologies, Docker a été retenu dans un second temps pour répondre également aux problématiques de déploiements futurs.

\vspace{1em}

Après une semaine de test sur les machines des développeurs, de nombreux problèmes m'ont été remontés. Ces problèmes étant liés d'une part à la difficulté de configuration automatique des machines sous Windows et d'autre part à la complexité grandissante des scripts automatisant pourtant seulement le téléchargement et l'exécution d'un conteneur dans un environnement connu, il a été décidé que ces machines virtuelles ne seraient plus distribuées aux développeurs : ils disposeront d'un accès sur une machine virtuelle maintenue par l'équipe en charge de l'administration système.

Résultats obtenus

Compte tenu de la diversité des systèmes employés et de la non-maîtrise de ceux-ci par les développeurs (filtrage de ports par un anti-virus nouvellement installé, version différentes de Windows, ...), la solution utilisant des conteneurs contrôlés par l'équipe serveur sur une machine dédiée a été retenue.

\vspace{1em}

Actuellement au stade d'étude, il serait question de mettre à disposition des développeurs une interface web leur permettant de lancer à volonté des machines virtuelles et de leur permettre de contrôler certains aspects préenregistrés (vider la base de données, charger une carte prédéfinie, ...) et de consulter les journaux des différents programmes.

\newpage

Conception de l'architecture de test de montée en charge

Objectifs

Une fois les premières versions du serveur livrées, nous nous sommes rapidement intéressés à ses capacités : en particulier le nombre maximum de joueurs pouvant être accueilli.

Propositions retenues

En tant que start-up, nous avons eu un certain nombre de crédits sur Amazon Web Service afin de tester et démarrer notre activité sur leur plate-forme.

Nous avons donc utilisé un certain nombre de nos crédits afin de lancer 20 machines virtuelles exécutant chacune plusieurs clients.

Résultats obtenus

Dans un premier temps, la montée en charge montrait que le serveur ne permettait pas de dépasser 250 joueurs. Au delà, l'analyse qui s'en est suivie a montré que l'ordonnanceur de la bibliothèque d'acteurs que nous utilisions ne faisait pas correctement son travail : il en résultait alors une surcharge anormale des canaux de diffusion internes, entraînant le sabordage du serveur de jeu (s'agissant d'une situation irrécupérable).

\vspace{1em}

Ce problème a été corrigé dans la version suivante de la bibliothèque d'acteurs. La nouvelle montée en charge a permis d'atteindre cette fois plus de 800 joueurs simultanément ; à ce moment, c'était bien le CPU qui était limitant et il n'en résultait pas de plantage du serveur.

\newpage

Intégration de métriques dans le serveur

Objectifs

Les métriques relevées précédemment concernaient le système : consommation CPU, utilisation de la mémoire et de la bande passante, ... Pour des besoins statistiques (et à terme business), il est nécessaire d'intégrer au serveur de jeu l'envoi de métriques telles que le nombre de joueurs inscrits, connectés, ...

Difficultés

Le serveur étant écrit en C++, il est nécessaire d'utiliser une bibliothèque C ou C++ pour s'interfacer avec la base de données. Malheureusement, comme elle est récente, cette bibliothèque n'avait pas encore été développée.

J'ai donc pris le soin de la développer puis de la proposer aux développeurs de la base de données qui l'ont intégrée. La bibliothèque est désormais disponible sur GitHub : \url{https://github.com/influxdb/influxdb-c}.

Résultats obtenus

Le travail d'intégration de métriques a d'abord conduit à la réalisation d'un système de configuration de l'application. En effet, jusque là, les quelques paramètres nécessaires au fonctionnement du serveur étaient regroupés dans des macros de préprocesseur.

\vspace{1em}

Une fois ce travail préliminaire réalisé, il a été aisé de rajouter dans le code du serveur une série de macros envoyant à chaque événement intéressant, un message vers la base de données.

\newpage

Déploiement du site web commercial du jeu

Objectifs

Précédemment hébergé auprès d'une société d'hébergement mutualisé permettant de modifier facilement le contenu du site ; nous avons été contraint de le refaire et de l'héberger nous-même afin de pouvoir l'adapter plus facilement à nos besoins.

Résultats obtenus

Le site web est déployé par un conteneur Docker, sur un serveur dédié aux services présenté à la communauté (aucun service d'usage interne ne s'y trouve).

\vspace{1em}

Le serveur web nginx est utilisé, allié au service php-fpm. Tous deux ont été configurés afin de permettre à un millier d'utilisateurs de visionner le site en même temps.

%%fimage(img/siteweb.png, 0.95, "Page d'accueil du site web")

Prévoyant un afflux massif de joueurs dans les prochains mois, nous nous sommes intéressés aux services d'Amazon afin de bénéficier d'une structure de mise à l'échelle. Un test grandeur nature a donc été effectué en utilisant le service Amazon Elastic Beanstalk. Ce service configure seul un load-balancer et s'occupe de déployer automatiquement de nouvelles instances d'une machine spécifique afin de répondre à la demande fluctuante, selon des critères préétablis. Après ce test, compte-tenu du coût de la solution, il a été décidé d'attendre d'en avoir vraiment l'utilité.

\newpage

Réflexion sur l'architecture et le déploiement des serveurs

Objectifs

La sortie du jeu n'étant pas encore à l'ordre du jour et certains aspects du développement du serveur n'étant pas encore complètement réglés, cette étape est là pour établir une base de travail concernant l'architecture et le déploiement des serveurs de jeu, de monitoring ainsi que leur interactions.

Pistes envisagées

À l'heure actuelle, la principale piste envisagée consiste à générer l'image d'un conteneur préalablement, de l'envoyer sur un dépôt d'images (contenant toutes les dernières versions des conteneurs). Ensuite, progressivement, on coupe les serveurs de jeu, puis on lance la nouvelle image. En cas de problème lors du déploiement, l'ancienne image persiste sur les serveurs jusqu'à ce que l'on lance un nettoyage. Cela permet de relancer la version stable précédente très rapidement en cas de problème.

\vspace{1em}

Précédemment, il était plutôt question d'effectuer un déploiement plus classique : une recette Ansible aurait éteint un petit groupe de serveurs, puis aurait procédé à la migration avant de relancer les serveurs mis à jour.

Les serveurs sont mis à jour progressivement ; en cas de problème durant la procédure, il faut alors retirer du cluster les serveurs défectueux. Cette opération est plus lourde que le basculement d'une version à l'autre tel que le permet Docker.

\vspace{1em}

L'avantage des images est que l'intégralité du système est inclus dans le conteneur. Il n'y a donc aucune variation d'environnement au moment du déploiement : il est ainsi plus facile d'avoir un environnement de test proche de l'environnement de production.