Déployer Magento 2 sur AWS (Amazon Web Services)

CONTEXTE

J’ai récemment travaillé sur un projet magento 2 qui avait des besoins de scalabilité pour absorber les montées en charge. Nous avons donc opté pour AWS qui présente ces fonctionnalités et qui reste une valeur sure dans le domaine de l’hébergement. Le client ayant un budget hébergement approprié, nous avons pu exploré la piste.

J’ai trouvé peu d’informations sur comment déployer Magento 2 sur AWS donc je voulais vous présenter mon travail.

Ce post n’a pas comme ambition d’être un tutoriel de mise en place mais plutôt de présenter les différentes étapes par lesquelles je suis passé pour arriver à mon objectif.

MON OBJECTIF

Mon objectif était de proposer un environnement de recette iso-production avec un peu de magie. C’est à dire qu’on clique sur un bouton déployer et le code se déploie partout sans downtime.

Mon but est que l’infra doit vivre toute seule et on ne doit pas avoir à se connecter au serveur ou autre pour déployer.

POINT DE DÉPART

L’équipe AWS a réalisé un projet https://github.com/aws-quickstart/quickstart-magento qui est un “getting started” d’une infrastructure magento 2 sécurisée par de la redondance, autoscaling…

Voici l’infra :

magento-with-aurora-architecture

En gros le principe est le suivant :

- On a un BASTION qui est le point d’entrée de notre infra (avec une ip publique donc accessible à partir de ma machine) et à partir de la, elle appartient aux sous réseaux privés ou se trouvent nos serveurs web. Cela présente une bonne option de sécurité car on peut facilement filtrer par ip qui peut accéder au bastion en SSH donc j’ai configuré mon ip fixe et celle du client. Toutes les autres seront interdites.

- On a un groupe d’autoscaling basé sur le pourcentage d’utilisation du processeur qui se charge de démarrer de nouvelles machines et de les éteindre lorsque le traffic diminue. On peut bien sur gérer les seuil et les limites afin de ne pas faire exploser les coûts.

- Redis (pour les sessions et le cache magento) + Mysql pour la base de donnée. Il est essentiel d’utiliser un serveur redis pour les sessions sinon on pourrait la perdre en cours de route selon si le load balancer nous envoie sur tel ou tel serveur web.

- Un disque réseau EFS monté sur tous les serveurs web afin de partager les medias (si on change une image produit, on veut que tous les serveurs web présentent la nouvelle)

LANCEMENT DE CE TEMPLATE

Alors le lancement devait se faire tout seul, on clique sur un bouton, on choisit ses configuration et hop!!!! on attend 30 minutes et tout est en place, la réalité a été tout autre.

Déjà je souhaitais déployer l’infrastructure sur les serveurs de Paris mais ce n’est pas possible car tous les services nécessaires, taille d’instance (EFS par exemple) n’existent pas, j’ai donc eu de nombreux échecs avant de comprendre cela.

J’ai donc ensuite choisi l’Irlande et là erreur mystérieuse vers la fin, au moment de l’installation de magento 2. Je me suis dis que cela venait peut être du fait que je tentais d’installer magento 2.2 alors que le template était prévu pour magento 2.1 et j’avais raison, c’était bien la source de l’erreur….

J’ai donc fini par arriver à installer l’infra en Irlande avec magento 2.1

!!! Attention, ce template crée une infra aux alentours de 750$ par mois !!!

Le template est lancé dans CloudFormation de aws. Si tout va bien, vous obtiendrez :

CloudFormation

 

PERSONNALISATION

Déploiement

Maintenant j’ai un magento 2 vierge qui fonctionne sur une infra aws. Chic !!! Mais les nouvelles questions arrivent :

  • Comment je déploie sur toutes les instances démarrées par l’autoscaling group
  • Comment je mets à jour ces instances quand j’ai une nouvelle version de code
  • J’utilise github, comment lier le projet sur mon infra aws

Première piste : Capistrano

Travaillant beaucoup avec Ruby on rails, je suis habitué à mettre en place Capistrano pour le déploiement de mes projets et en particulier https://github.com/davidalger/capistrano-magento2 sur mes projets magento 2.

Les ips tournent dans le cloud donc je ne peux pas les saisir en dur dans capistrano, j’avais donc trouvé la solution https://github.com/marcoschicote/capistrano3-autoscaling-deploy  qui me semblait bien.

Mais comment je fais si de nouvelles machines démarrent et quelle version de code vont-elle prendre. Et bien en fait nos web serveur sont démarrés à partir d’une image AMIS (en gros un snapshot) cela signifie qu’à chaque déploiement, je dois faire une nouvelle AMIS à partir d’une instance sur laquelle j’ai correctement déployé => Trop lourd

Dans un environnement de préproduction Capistrano peut rester viable mais va s’avérer limité. Je devais trouver autre chose, il fallait que je cherche parmi les resources AWS.

Seconde piste : CodeDeploy

A force de lire sur internet, j’ai compris que amazon avait créé ses propres services d’intégration continue dont CodeDeploy qui est responsable de déployer une application web sur un auto scaling group par exemple. Pour se faire, il suffit de créer un fichier appspec.yml et d’orchestrer le déploiement par rapport au besoin de notre application.

Voici mes fichiers : appspec.yml

version: 0.0
os: linux
files:
  - source: /
    destination: /var/www/html/current
permissions:
  - object: /
    owner: ec2-user
    group: nginx
hooks:
  ApplicationStop:
    - location: scripts/stop_server.sh
      timeout: 300
      runas: root
  BeforeInstall:
    - location: scripts/magento/prepare.sh
      timeout: 300
      runas: ec2-user
  AfterInstall:
    - location: scripts/magento/symlinks.sh
      timeout: 300
      runas: ec2-user
    - location: scripts/magento/composer.sh
      timeout: 300
      runas: ec2-user
    - location: scripts/magento/permissions.sh
      timeout: 300
      runas: ec2-user
    - location: scripts/magento/compile.sh
      timeout: 800
      runas: ec2-user
    - location: scripts/magento/database.sh
      timeout: 300
      runas: ec2-user
    - location: scripts/start_server.sh
      timeout: 300
      runas: root

scripts/start_server.sh

#!/bin/bash
service nginx start

scripts/stop_server.sh

#!/bin/bash
isExistApp=`pgrep nginx`
if [[ -n  $isExistApp ]]; then
    service nginx stop
fi

scripts/magento/compile.sh

#!/bin/bash

#########################
# Go to right directory
#########################
m_path=/var/www/html/current
cd $m_path

#########################
# Set environment
#########################
php -f bin/magento -- deploy:mode:set production --skip-compilation

#########################
# Static assets
#########################
touch pub/static/deployed_version.txt
php -f bin/magento -- setup:static-content:deploy -f en_US -t VENDOR/THEME -t Magento/backend
php -f bin/magento -- setup:static-content:deploy -f fr_FR -t VENDOR/THEME -t Magento/backend
php -f bin/magento -- setup:static-content:deploy -f ja_JP -t VENDOR/THEME -t Magento/backend
php -f bin/magento -- setup:di:compile --no-ansi

#########################
# Composer
#########################
composer dump-autoload --no-dev --no-interaction --optimize 2>&1

scripts/magento/composer.sh

#!/bin/bash

#########################
# Go to right directory
#########################
m_path=/var/www/html/current
cd $m_path

#########################
# Composer
#########################
composer install --no-dev --prefer-dist --no-interaction --optimize-autoloader 2>&1

scripts/magento/database.sh

#!/bin/bash

#########################
# Go to right directory
#########################
m_path=/var/www/html/current
cd $m_path

#########################
# db update
#########################
php -f bin/magento -- setup:db-schema:upgrade
php -f bin/magento -- setup:db-data:upgrade

scripts/magento/permissions.sh

#!/bin/bash

#########################
# Go to right directory
#########################
m_path=/var/www/html/current
cd $m_path

#########################
# Permissions
#########################
find ./ -type d ! -perm 2775 -exec chmod 2775 {} +
find ./ -type f ! -perm 755 -exec chmod 755 {} +
chmod +x bin/magento

scripts/magento/prepare.sh

#!/bin/bash

#########################
# Keep last release
#########################
m_path=/var/www/html/current
rsync -a $m_path /var/www/html/last_deploy
rm -rf $m_path

scripts/magento/symlinks.sh

#!/bin/bash

#########################
# Go to right directory
#########################
m_path=/var/www/html/current
cd $m_path

#########################
# Symlinks
#########################
mkdir -p pub var
rm -rf pub/media
ln -s /var/www/html/shared/pub/media $m_path/pub/media
ln -s /var/www/html/shared/pub/sitemaps $m_path/pub/sitemaps
ln -s /var/www/html/shared/var/backups $m_path/var/backups
ln -s /var/www/html/shared/var/composer_home $m_path/var/composer_home
ln -s /var/www/html/shared/var/importexport $m_path/var/importexport
ln -s /var/www/html/shared/var/import_history $m_path/var/import_history
ln -s /var/www/html/shared/var/log $m_path/var/log
ln -s /var/www/html/shared/var/report $m_path/var/report
ln -s /var/www/html/shared/var/session $m_path/var/session
ln -s /var/www/html/shared/var/tmp $m_path/var/tmp

ln -s /var/www/html/shared/app/etc/env.php $m_path/app/etc/env.php
ln -s /var/www/html/shared/nginx.conf $m_path/nginx.conf
ln -s /var/www/html/shared/app/etc/config.local.php $m_path/app/etc/config.local.php
ln -s /var/www/html/shared/var/.setup_cronjob_status $m_path/var/.setup_cronjob_status
ln -s /var/www/html/shared/var/.update_cronjob_status $m_path/var/.update_cronjob_status

Les symlinks permettent de faire le lien entre nos répertoire locaux et le disque  NFS monté sur toutes les instances ou on va stocker tous les medias afin de les partager entre les instances. Le template magento 2 a déjà créé ce disque dans le service EFS de AWS

Une fois cela tout bien configuré, CodeDeploy est capable de déployer l’application sur nos machines et même de le faire en blue/green déploiement.

Mais késako !!! ?

Et bien en fait voici les étapes d’un blue/green déploiement :

  • Lancement de nouvelle(s) machine(s)
  • Installation de l’application sur ces machines
  • Routage du traffic sur ces machines si elles sont (healthy, c’est à dire elles répondent des 200)
  • Suppression des anciennes machines

Avantage : Pas de downtime et ca ne publiera la nouvelle version que si l’application a été correctement déployée.

Désavantage : On se retrouve pendant 30min/1h avec le double de machines allumées donc on paie 1h de machines pour chaque déploiement (coût minime)

CodeDeploy

Conclusion

CodeDeploy est la bonne solution à utiliser avec AWS sans aucun doute. Le suivi des déploiements est bien fait, et on peut aussi relier des notifications slack, sms ou autre pour être notifié à chaque déploiement.

Déploiement automatique

J’ai l’habitude en travaillant avec Travis en intégration continue d’automatiser les déploiement en me basant sur git flow :

  • Un commit sur develop déploie la version en preproduction
  • Un commit sur master déploie la version en production

J’ai donc cherché comment faire cela avec AWS et j’ai trouvé : CodePipeline 

En fait on relie le repository github associé au projet et on crée des “écouteurs” qui se déclenchent quand on commit sur telle ou telle branche. On peut alors lancé des commandes d’intégration continue (type tests rspec…) ou bien juste déployer en utilisant…CodeDeploy.

Avec ce composant je suis donc parvenu à la logique désirée : Un commit GITHUB sur la branche develop ou master déclenche un déploiement CodeDeploy sur l’environnement désirén respectivement staging ou production !

CodePipeline

Nom de domaine

Le template magento 2 crée un LoadBalancer avec un DNS amazon, il suffit de créer un record CNAME sur votre domaine vers cette url pour  accéder à votre site internet mais aussi de changer dans la table `core_config_data` de magento 2 l’url de votre site car magento 2 la stocke en base de donnée.

Par exemple :

www CNAME staging-MagentoStack-1R4POUP98-SecurityGroupStack-1X6QS47X-ELT6PU9G

Si votre nom de domaine est sebfie.com, alors l’adresse de votre site internet sera www.sebfie.com. C’est cette valeur qu’il faut mettre comme base_url dans la table `core_config_data` de magento 2.

Domaine DNS

 

Notifications de déploiement

Pour chaque déploiement, je souhaitais mettre en place des notifications slack qui m’indiquaient l’état du déploiement… pour être averti des erreurs mais aussi notifier le client lorsque le code se déploie.

Il suffit d’utiliser le service lambda de AWS et voici un tutoriel qui résume bien la procédure : http://4eva.net/posts/577

Capture_d’écran_2018-09-26_à_09_56_10

 

Et voici un peu un récapitulatif de ce que je suis arrivé à mettre en place. J’espère que cela donnera des idées à certains et si vous avez des besoins, n’hésitez pas à me contacter !