Déployer une application PHP/MySQL dans le cloud avec Amazon EC2 - Troisième partie

Dans cette troisième partie, nous allons voir pas à pas comment configurer Amazon EC2 pour adapter automatiquement le nombre de machines virtuelles à la charge du site.

Si vous avez raté les autres articles, la première partie aborde le sujet: pourquoi migrer une application vers le cloud. Elle présente brièvement la solution Amazon EC2 et vous devriez la lire pour vous familiariser avec les termes utilisés dans ce tutorial.
La deuxième partie est un tutorial expliquant comment migrer une application PHP/MySQL vers le cloud.

Load balancing de notre application

Avant d'activer l'autoscaling, nous devons disposer d'un load balancer (répartiteur de charge). Nous allons donc commencer par créer ce répartiteur de charge. Il ne servira pas à grand chose puisque nous ne mettrons pour l'instant qu'une machine dedans.

Créer le load balancer

Pour créer un load balancer, dans la console AWS, dans la section Networking & Security, sélectionnez Load Balancers, puis cliquer sur le bouton "Create Load Balancer".

L'écran de création s'affiche:

On donne un nom à notre load balancer ("http-load-balancer"), puis on choisit sur quel critère effectuer le load-balancing. Amazon nous propose de base de répartir la charge sur Apache, et c'est exactement ce que nous souhaitons.

Configurer le health check

L'étape suivante consiste à expliquer au load balancer comment savoir si les machines vers lesquelles il route les informations sont actives ou non. En effet, si une machine a une défaillance technique, il est important qu'il stoppe le routage vers cette machine. Pour cela, le load balancer effectue des health check, c'est à dire des requêtes régulières vers nos serveurs. Par défaut, il effectue sa requête vers la page d'accueil du site. Traditionnellement, la page d'accueil d'un site génère quelques requêtes, et peut être assez lourde. Nous allons donc plutôt diriger le "health check" vers une simple page HTML qui retournera "OK". Nous appelons cette page "load_balancer_ping.html".

Nous gardons les options avancées par défaut: le load balancer effectuera une vérification toutes les 5 secondes, considérera qu'une machine est défectueuse après 2 tests successifs défaillants et considérera qu'elle fonctionne de nouveau après 10 tests réussis.

L'étape suivante consiste à rajouter des instances dans notre load-balancer.
Nous allons donc déployer dans ce load-balancer notre instance unique de serveur PHP.

La dernière étape permet de vérifier tous les paramètres avant de créer le load balancer:

Gérer les sessions

Par défaut, le load balancer d'Amazon envoie les requêtes HTTP vers le serveur le moins chargé. Cela pourrait représenter un problème pour nous. En effet, si notre application PHP utilise des sessions, et si une session est démarrée sur un serveur, nous voudrions la garder sur les autres serveurs (et la partager).

Dans les options Amazon, on peut trouver un paramètre "Session stickiness". Si on active ce paramètre, un utilisateur sera toujours routé vers le même serveur, et pourra donc garder sa session. Ce paramètre résoudrait donc notre problème si on souhaitait un simple load-balancer.

Cependant, nous avons prévu d'effectuer de l'autoscaling. Dans le cadre de l'autoscaling, un serveur peut être dynamiquement ajouté, et surtout retiré. Si un serveur est retiré, nous voudrions que les sessions de ce serveur ne soient pas perdues. Le paramètre "Session stickiness" ne nous aidera pas dans ce cas.

Nous allons donc devoir partager les sessions PHP en utilisant une autre méthode. Pour ce tutorial, nous avons choisi Memcached.

Partager les sessions avec Memcached

Memcached est un gestionnaire de cache. Nous allons mettre en place le cache sur une machine centralisée. Dans notre cas, le serveur MySQL servira aussi de serveur Memcached.

Nous nous connectons donc sur le serveur MySQL et installons Memcached.
Sous Ubuntu 10.04, il suffit de taper:

sudo apt-get install memcached libmemcache-dev

Nous nous connectons donc sur le serveur PHP et installons l'extension Memcached pour PHP:

sudo apt-get install php5-memcached

Sous d'autres OS, il faudra peut être passer par PECL pour installer le module PHP pour Memcached.

Avant de déporter les sessions vers le serveur Memcache/MySQL, nous devons autoriser les serveurs externes à accéder à Memcache. Par défaut, Memcache se comporte comme MySQL, c'est à dire qu'il n'écoute que les requêtes provenant de "localhost".
Pour corriger cela, nous allons éditer le fichier /etc/memcached.conf et commenter la ligne suivante:

#-l 127.0.0.1

Nous devons maintenant redémarrer le service Memcache pour prendre les changements en compte:

sudo /etc/init.d/memcached restart

Cependant, attention! Memcache par défaut ne propose pas de mode d'authentification. Il faut donc absolument faire attention à ne pas ouvrir le port à tout le monde. En effet, ce serait une faille de sécurité énorme puisqu'elle donnerait accès depuis l'extérieur à toutes les sessions (et donc la possibilité de voler des sessions utilisateur).

Heureusement, Memcache nous donne la possibilité de donner accès à un port uniquement aux machines d'un certain groupe. Et nous avons justement un groupe de sécurité pour les serveurs HTTP! Nous allons donc ouvrir le port Memcache du serveur MySQL (11211) mais uniquement pour les serveurs HTTP.

Nous pouvons maintenant déporter les sessions du serveur PHP.
On peut le faire facilement à partir du php.ini, en remplaçant la directive "session.save_handler":

session.save_handler = memcached
session.save_path = "10.226.2.3:11211"

Il suffit de redémarrer le serveur PHP pour prendre en compte les modifications:

sudo /etc/init.d/apache2 restart

Ne pas oublier de tester l'application pour voir si des erreurs apparaissent dans le log d'erreur lors de l'ouverture des sessions:

tail -f /var/log/apache/error.log

Si aucune erreur relative aux sessions ne s'affiche, vous pouvez passer à la section suivante: générer une image de serveur!

Générer une image de serveur

L'autoscaling consiste à rajouter dynamiquement de nouveaux serveurs à la demande. Pour cela, il faut bien sûr disposer d'une image de serveur!
Dans cette section, nous allons voir comment faire une image de serveur (AMI) qui nous permettra de dupliquer notre serveur web à volonté.

Générer une image AMI est extrêmement simple. Dans la console AWS, il suffit d'accéder à la liste des instances, sélectionner l'instance à transformer en image, puis cliquer sur Instance Actions > Create Image (EBS AMI).

Il suffit de donner un nom à l'instance, éventuellement des détails. Un clic sur "Create this image" créé l'instance instantanément.

Notez l'ID de l'instance dans la page de confirmation, nous allons en avoir besoin pour démarrer l'autoscaling.

Note: la création de l'AMI peut prendre plusieurs minutes. Pour voir l'avancée du processus, vous pouvez accéder à la page "AMIs" de la console AWS.

Activer l'autoscaling

Pour activer l'autoscaling, nous devons utiliser l'API dédiée. Lors de l'écriture de ce tutorial, la console AWS ne permet pas de faire cela depuis Internet. Nous allons donc passer en ligne de commande.

Etape 1: télécharger et installer l'API Autoscaling

La première étape consiste à télécharger l'API permettant d'effectuer l'Autoscaling.
Il s'agit d'un fichier ZIP que vous pouvez décompresser.
A l'intérieur du fichier ZIP, un fichier README.TXT contient les informations pour installer l'API sur son poste.

Note: lors de la procédure d'installation, vous allez avoir besoin d'une clef AWS pour vous connecter à votre compte. Vous pouvez trouver cette clef en vous connectant à la console AWS, puis dans la section Account / Security Credentials

Etape 2: créer une configuration de lancement

Une fois les outils installés, nous allons créer une Launch configuration.
Il s'agit d'une description de serveur qui sera créée automatiquement en cas de besoin.
Une Launch configuration se créé par la ligne de commande suivante:

as-create-launch-config httpLaunch --image-id ami-8a95bffe --instance-type m1.small --group "HTTP Servers" --region eu-west-1
 
OK-Created launch config

  • httpLaunch représente le nom que nous donnons à la configuration.
  • image-id est l'ID de l'AMI à utiliser.
  • instance-type est le type de machine virtuelle à utiliser pour l'instance.
  • group correspond au group de sécurité à utiliser (donc aux paramètres du firewall).
  • region correspond à la région utilisée. Cette région doit être la région dans laquelle l'AMI a été créée.

Etape 3: créer un groupe d'autoscaling

Nous allons maintenant créer un groupe d'autoscaling. Ces groupes décrivent le nombre de machines minimales et maximales pouvant appartenir au groupe, la zone géographique, ainsi que les load-balancers auxquels les nouvelles machines doivent s'inscrire.

as-create-auto-scaling-group httpASG --launch-configuration httpLaunch --availability-zones eu-west-1b --min-size 1 --max-size 5 --load-balancers http-load-balancer --region eu-west-1
 
OK-Created AutoScalingGroup

  • httpASG représente le nom que nous donnons au groupe.
  • launch-configuration est le nom de la configuration que nous venons juste de créer.
  • availability-zones représente les zones géographiques dans lesquelles nous déployons l'application. Dans l'exemple, nous déployons l'application dans les 2 zones européennes.
  • min-size représente le nombre de machines minimum du groupe.
  • max-size représente le nombre de machines maximum du groupe.
  • load-balancers correspond aux load-balancers auxquels les nouvelles machines doivent s'inscrire.
  • region correspond à la région utilisée. Cette région doit être la région dans laquelle l'AMI a été créée.
Attention! Le load-balancer que vous avez précedemment créé est associé à une ou plusieurs zones géographiques (en fait, si il contient 2 instances dans 2 zones géographiques, il est configuré pour ces 2 zones géographiques). Dans le groupe d'autoscaling que vous créez, vous ne devez pas indiquer de zone géographique qui ne soit pas gérée par le load-balancer, autrement les instances créées ne seront pas utilisées!

Etape 4: créer un déclencheur

Nous avons défini la taille du groupe d'autoscaling, mais nous n'avons pas encore défini sous quels critères il faut rajouter des machines, et sous quels critères nous devons en retirer.

as-create-or-update-trigger httpTrigger --auto-scaling-group httpASG --namespace "AWS/EC2" --dimensions "AutoScalingGroupName=httpASG" --measure CPUUtilization --statistic Average --period 60 --breach-duration 120 --lower-threshold 30 --lower-breach-increment"=-1" --upper-threshold 60 --upper-breach-increment 1  --region eu-west-1
 
OK-Created/Updated trigger

Voyons comment lire ces paramètres:

  • httpTrigger représente le nom que nous donnons au déclencheur.
  • auto-scaling-group représente le nom du groupe auquel appliquer le déclencheur.
  • namespace - dimensions décrivent les groupes de métriques que nous allons utiliser.
  • measure: le type de mesure qui va déclencher le trigger. Dans cet exemple nous déclenchons les triggers sur l'utilisation CPU.
  • statistic: le type d'agrégation de mesure qui va déclencher le trigger. Dans cet exemple nous effectuons la moyenne (Average) sur l'utilisation CPU.
  • period: la période sur laquelle est appliquée l'agrégation. Dans l'exemple, nous analysons une moyenne sur 60 secondes.
  • upper-threshold: La limite au dessus de laquelle on active un nouveau serveur. Ici: si la moyenne du CPU sur 60 secondes est supérieure à 60%.
  • upper-breach-increment: Le nombre de serveurs à rajouter quand le plafond se déclenche.
  • breach-duration: Le nombre de secondes durant lesquelles le plafond doit être dépassé avant d'activer un nouveau serveur. Dans cet exemple, le CPU doit rester supérieur à 60% durant 120 secondes avant d'ajouter un nouveau serveur.
  • lower-threshold: La limite basse au dessous de laquelle on enlève un serveur. Ici: si la moyenne du CPU sur 60 secondes est supérieure à 30%.
  • lower-breach-increment: Le nombre de serveurs à ajouter quand le plafond se déclenche. Il faut donc indiquer un nombre négatif pour retirer des serveurs. (notez la répartition "bizarre" des quotes dans l'exemple)
  • region correspond à la région utilisée. Cette région doit être la région dans laquelle le groupe d'autoscaling a été créé.

Optimiser!

Nous avons enfin mis en place l'autoscaling! Notre application va pouvoir se dimensionner automatiquement (au moins le coté PHP) en fonction de la charge des serveurs. Cela ne doit surtout pas nous faire oublier que nous payons les serveurs démarrés. Et donc qu'optimiser le code PHP permettra d'impacter directement la facture Amazon.

Pour commencer ces optimisations, la première action que vous devriez prendre, si vous ne l'avez déjà fait est d'installer APC sur vos serveurs. APC est un cache d'opcode, et accélère grandement le temps de réponse et la charge des serveurs lors des appels de page PHP.
Referez vous à mon tutoriel d'installation d'APC.

Effectuer la maintenance

Il y a fort à parier que tôt ou tard, vous souhaiterez déployer une mise à jour. Sur le cloud, cela peut vite devenir plus complexe que sur un serveur simple, puisqu'il y a plusieurs machines à mettre à jour.
Pour cela, 2 solutions. Soit votre application est capable de se mettre à jour automatiquement (c'est surement le scénario le moins probable), soit vous devrez redéployer tous les serveurs.

Pour redéployer les serveurs la marche à suivre est la suivante.

Première étape: vider le groupe d'autoscaling

Nous allons commencer par supprimer le groupe d'autoscaling. Mais pour cela, nous devons d'abord supprimer toutes les instances à l'intérieur. Une manière simple de le faire est de réduire sa taille à 0:

as-update-auto-scaling-group httpASG --min-size 0 --max-size 0 --region eu-west-1
 
OK-Updated AutoScalingGroup

Deuxième étape: recréer une machine virtuelle AMI

Il faut ensuite recréer une image de machine virtuelle AMI. Pour cette création, il vous suffit de reprendre les étapes indiquées plus haut dans cet article.
Une fois la machine créée, il faut aussi recréer un groupe de lancement pour cette AMI. Dans l'exemple ci-dessous, nous supposons que ce nouveau groupe de lancement s'appelle "httpLaunch2".

Troisième étape: mettre à jour le groupe d'autoscaling

Une fois la machine créée, nous allons indiquer que le groupe d'autoscaling doit prendre en paramètre cette nouvelle machine, et nous allons lui redonner sa taille initiale (entre 1 et 5 machines).

as-update-auto-scaling-group httpASG --launch-configuration httpLaunch2 --min-size 1 --max-size 5 --region eu-west-1
 
OK-Updated AutoScalingGroup

Tester!

Votre configuration est ok? Vous avez configuré l'Autoscaling aux petits oignons? Alors il est temps de tester la montée en charge!
Pour cela, je vous conseille JMeter, de la fondation Apache, qui permet d'écrire facilement des tests de montée en charge. Ce sera peut être l'objet d'un prochain billet!

Avec les tests que j'ai effectué sur mon application (30 requêtes par seconde sur la page d'accueil), je passe de plus de 2 secondes de temps de réponse au début du test à moins de 0.7 secondes en fin de test, lorsque toutes les machines ont été déployées. Et à la fin du test, les machines supplémentaires sont désactivées. Mission accomplie, les résultats sont concluants!

Si vous avez suivi le tutorial jusqu'au bout, j'espère qu'il vous aura permit de vous faire une idée précise des possibilités du cloud computing, et peut être de faire les premiers pas dans ce domaine en pleine activité. N'hésitez pas à me laisser des commentaires!