Chez DoorDash, fournir un service logistique rapide et à la demande ne serait pas possible sans une infrastructure informatique robuste pour alimenter nos systèmes dorsaux. Après tout, la qualité de nos algorithmes et de notre code d'application importe peu si nous n'avons pas la capacité de serveur nécessaire pour les exécuter. Récemment, nous avons réalisé que notre infrastructure existante basée sur Heroku ne répondait plus à nos besoins et que nous devions passer à Amazon Web Services. Avec l'aide d'une technologie en plein essor appelée Docker, nous avons pu effectuer cette transition en beaucoup moins de temps et d'efforts qu'il n'aurait été possible de le faire autrement.
DoorDash a d'abord été hébergé sur Heroku parce que c'était un moyen simple et pratique de lancer notre application. Au lieu de nous préoccuper des complexités de bas niveau de l'infrastructure du serveur, nous pouvions nous concentrer sur le développement des fonctionnalités du produit. Cependant, l'utilisation d'une 'Äúplatform-as-a-service'Äù n'était pas sans contrepartie, et au fur et à mesure que notre trafic augmentait, nous avons commencé à rencontrer de sérieux problèmes et limitations avec Heroku.
- Performance: Le problème le plus urgent était la performance médiocre des instances de serveur Heroku (alias 'Äúdynos'Äù). Chaque dyno est extrêmement limité en termes de performances CPU et de ressources mémoire (ce qui n'est pas surprenant si l'on considère qu'Heroku fait tourner plusieurs dynos sur une seule instance Amazon EC2). Même après un réglage approfondi des performances de notre application Django, nous étions toujours obligés de faire tourner beaucoup plus de dynos que nous l'aurions souhaité et nous ne voyions pas comment cela pourrait continuer à évoluer.
- Rentabilité: Les dynos Heroku étaient très chers pour les ressources informatiques que nous obtenions. Pour à peu près le même prix qu'un dyno Heroku 'Äú2x'Äù avec 1 Go de RAM, nous aurions pu louer une instance Amazon c3.large EC2 avec 3,75 Go de RAM.
- Fiabilité: De manière surprenante, nous avons constaté que Heroku était en proie à des problèmes de fiabilité. Une panne de l'API de déploiement d'Heroku semblait survenir toutes les semaines ou toutes les deux semaines. Un incident mémorable d'une demi-journée nous a empêchés de déployer un correctif critique et a définitivement érodé notre confiance dans la plateforme.
- Contrôle: Avec Heroku, vous perdez le contrôle fin et la visibilité sur vos serveurs. L'installation de logiciels personnalisés dans votre pile de serveurs est loin d'être simple, et il n'est pas possible de se connecter en SSH à un serveur pour déboguer un problème de CPU ou de mémoire.
Pour résoudre ces problèmes, nous savions que nous devions quitter Heroku et trouver un nouveau fournisseur d'hébergement. Le choix logique de mise à niveau s'est porté sur Amazon Web Services et son service Elastic Compute Cloud (EC2). Les instances de serveurs virtuels Amazon EC2 sont disponibles dans une grande variété de configurations de CPU et de mémoire, disposent d'un accès racine complet et offrent de bien meilleures performances par dollar qu'Heroku.
Alors qu'AWS semblait être une solution attrayante, la migration à partir d'un fournisseur de plateforme gérée de haut niveau comme Heroku peut s'avérer une tâche décourageante. En effet, l'administration d'un cluster de serveurs et la mise en place d'un déploiement continu de code nécessitent une quantité de travail non négligeable. Pour automatiser le processus de configuration de nos serveurs, vous devriez normalement installer un logiciel de gestion de configuration tel que Chef ou Puppet. Cependant, ces outils ont tendance à être encombrants et nécessitent l'apprentissage d'un langage spécifique au domaine. Nous devrions écrire des scripts pour effectuer des tâches telles que l'installation de toutes les dépendances tierces de notre application ou la configuration de tous les logiciels de notre pile de serveurs. Et pour tester le fonctionnement de ce code, nous devions mettre en place un environnement de développement local à l'aide de Vagrant. Tout cela représentait une complexité importante qui n'était pas très attrayante.
Docker
Nous nous demandions s'il existait un moyen plus simple d'accéder à AWS, et nous avions beaucoup entendu parler de cette technologie de virtualisation relativement nouvelle appelée Docker(www.docker.com). Docker vous permet d'empaqueter une application, avec toutes ses dépendances, dans un environnement 'Äúcontainer'Äù qui peut fonctionner dans n'importe quel système Linux. Comme les conteneurs Docker ne sont que des instantanés d'un état connu et fonctionnel du système, il est enfin possible de "construire une fois, exécuter partout".
Les conteneurs Docker encapsulent un système de fichiers Linux virtuel, offrant une grande partie de la portabilité et de l'isolation offertes par une machine virtuelle. La grande différence réside dans l'empreinte des ressources. Alors qu'une machine virtuelle doit exécuter un système d'exploitation complet, les conteneurs partagent le noyau Linux de la machine hôte et ne doivent virtualiser que l'application et les bibliothèques qui en dépendent. Par conséquent, les conteneurs sont très légers et démarrent en quelques secondes au lieu de quelques minutes. Pour une explication plus technique sur les conteneurs, consultez le document suivant article de RightScale.Mise en œuvre
Après avoir appris ce dont Docker était capable, nous savions qu'il pourrait jouer un rôle clé dans l'accélération de notre migration vers AWS. Nous avions prévu de déployer des conteneurs Docker exécutant notre application Django sur des instances Amazon EC2. Au lieu de consacrer des efforts à la configuration des hôtes EC2 pour exécuter notre application, nous pouvions transférer cette complexité dans l'environnement de conteneurs Docker.
Construire l'image Docker
La première étape consistait à définir l'image Docker qui accueillerait notre application. (Pour clarifier certains termes, une 'Äúimage' Docker est essentiellement un modèle pour les conteneurs, et un conteneur est une instance en cours d'exécution d'une image). Cela implique principalement l'écriture d'un simple fichier de configuration appelé Dockerfile. Contrairement aux scripts Chef ou Puppet complexes écrits dans un DSL personnalisé, un Dockerfile ressemble beaucoup à une série de commandes Bash et est facile à comprendre.
Les images Docker sont composées en couches et peuvent être basées sur d'autres images. Pour commencer, vous utiliserez généralement une version stable de Docker d'une distribution Linux populaire telle qu'Ubuntu ou CentOS. Ces "images de base" sont hébergées sur Docker Hub, un référentiel public d'images Docker prédéfinies.
Dans le cas de notre application Django, la majeure partie du travail a consisté à trouver comment installer les dépendances Python tierces (en particulier celles avec des extensions C) et à configurer les composants logiciels plus complexes de notre pile (tels que notre serveur web ou un pooler de connexion à la base de données). En temps normal, ce processus peut être fastidieux et parsemé d'essais et d'erreurs, mais le fait de pouvoir tout tester dans notre environnement local a été une véritable aubaine. Le démarrage d'un nouveau conteneur Docker ne prend pratiquement pas de temps, ce qui permet un rythme de développement très rapide.
Préparation de l'environnement Docker
La deuxième étape consistait à mettre en place un environnement d'exécution Docker sur EC2. Pour gagner du temps, nous avons décidé d'utiliser AWS Opsworks, un service qui intègre une couche Chef pour faciliter la gestion des instances EC2. Bien que nous n'ayons pas pu éviter Chef, nous n'avons pas eu à passer autant de temps à le manipuler car nous avions déjà défini la grande majorité de la configuration du système à l'intérieur de notre image Docker.
Notre flux de déploiement de code était simple, et consistait principalement à construire une image Docker à partir de notre dernière base de code et à la distribuer à nos instances de serveur, avec l'aide d'un serveur d'images Docker. Nous n'avons eu besoin que d'écrire quelques courts scripts Chef pour télécharger la dernière image Docker et démarrer un nouveau conteneur Docker.
Notre flux de déploiement basé sur les conteneurs Docker.Résultats
De la conception à l'achèvement, notre migration de Heroku vers AWS a pris environ un mois à deux ingénieurs. Il s'agissait notamment d'apprendre Docker, AWS et Chef, d'intégrer le tout et de tester (et encore tester). Ce que nous avons le plus apprécié avec Docker, c'est qu'une fois qu'un conteneur fonctionnait localement, nous étions sûrs qu'il fonctionnerait également dans un environnement de production. C'est pourquoi nous avons pu passer à l'étape suivante sans aucun problème.
Sur notre nouvel environnement Docker+AWS, nous avons pu réaliser un gain de performance de plus de 2 fois par rapport à notre environnement Heroku. Le temps de réponse moyen de l'API de DoorDash est passé d'environ 220 ms à moins de 100 ms, et nos temps d'exécution des tâches d'arrière-plan ont également été divisés par deux. Avec un matériel EC2 plus robuste, nous n'avons eu besoin que de la moitié du nombre de serveurs, ce qui a considérablement réduit notre facture d'hébergement. Le degré supplémentaire de contrôle sur notre pile logicielle s'est également avéré utile, car il nous a permis d'installer Nginx en tant que proxy inverse devant nos serveurs d'application et d'améliorer le débit de notre API web.
Réflexions finales
Inutile de dire que nous étions très heureux de notre décision de passer à AWS, et Docker a été l'une des principales raisons pour lesquelles nous avons pu le faire rapidement et facilement. Nous avons été en mesure d'améliorer considérablement les performances de nos serveurs et d'avoir un meilleur contrôle sur notre pile logicielle, sans avoir à supporter une tonne de frais généraux d'administration. Pouvoir développer et peaufiner un conteneur Docker localement tout en sachant qu'il fonctionnera exactement de la même manière dans n'importe quel environnement est un énorme avantage. Un autre grand avantage est la portabilité : au lieu d'être trop liés à notre fournisseur d'hébergement actuel, nous pouvons facilement nous déployer dans n'importe quelle autre infrastructure en nuage à l'avenir.
Il est clair que Docker est une technologie puissante qui devrait permettre aux développeurs de prendre le contrôle de la façon dont ils déploient leurs applications, tout en réduisant l'avantage marginal que les solutions PaaS gérées comme Heroku fournissent. Nous sommes impatients de voir ce que nous pourrons faire de plus avec cette technologie.
- Alvin Chow