Fonctionnellement, il s’agit donc de déployer un CMS qui gère le contenu éditorial d’un site e-Commerce. Par contenu éditorial, nous entendons les différentes images (header / footer / bannière) qui sont à afficher par le navigateur de l’utilisateur. Les images à afficher sont fonctions du contexte du navigateur, telles que marques, catégories de produits et date courante. Ces images doivent être servies à tous les utilisateurs grand public du site, elles seront donc servies par un CDN.
Pour que le front sache quelles images il doit afficher, il requête un index dont nous ne parlerons pas ici.
Avant d’aller plus loin, répondons à deux questions qui doivent vous tarauder : pourquoi ECS et pourquoi s3Fs ?
Lorsque l’on parle d’ECS, une des premières remarques qui suit est : pourquoi partir sur ECS qui est propriétaire AWS, et pas Kubernetes ?
La réponse est simple, ECS est prêt à l’emploi :
La gestion des données est peut-être plus limitée comparée à d’autres solutions mais ce n’était pas un problème pour nous puisque tous les micro-services ECS étaient stateless, les données étant dans des bases RDS+Elasticache.
L’argument de la facilité de mise en oeuvre et de maintien a été déterminant pour notre client qui nous laissait deux mois pour monter une infrastructure micro-services from-scratch.
Au final, nous ne regrettons pas notre choix puisque nous avons réussi dans le temps imparti à déployer 30 services ECS en auto-scaling 1 à 20, le tout sur un cluster lui-même en auto-scaling 1 à 20.
La bonne solution qui fonctionne et qui scale pour du stockage persistent sur ECS, est de mettre en oeuvre des montages EFS.
Dans notre cas, le sujet n'est pas tant de stocker des fichiers images de manière persistente que de les servir "massivement" à des utilisateurs. Un pré-requis est donc le CDN CloudFront. Se pose alors la question du stockage des images source, et S3 y répond bien.
Le point dur est que l'application d'administration repose sur un système de fichiers local pour le téléchargement des images. La bonne approche aurait été de s'affranchir de stockage local, en faisant un envoi direct vers S3, mais cela implique des changements au niveau de l'application que nous n'avons pas réussi à faire valider faute de temps...
Plutôt que de modifier l’application, il a été préféré de continuer en l’état et travailler au niveau de l'infrastructure.
Nous avons donc subi s3Fs plus que choisi.
Est-ce grave ?
s3Fs reposant sur s3, il y a des limites à prendre en compte :
Cela étant dit, pas de problème bloquant compte tenu des volumétries cibles :
Dans une pure approche conteneur (un processus par conteneur), nous déployons donc : un conteneur s3Fs et une Webapp (ici une image Ubuntu).
Comme indiqué sur le readme s3Fs, la réplication des points de montage entre namespaces doit être autorisée (plus de détails ici sur le mode de partage des systèmes de fichiers entre namespaces). D’où la suppression de MountFlags=slave dans le lancement du daemon Docker et l’option ‘shared’ fournie lors du montage du volume s3Fs sur les conteneurs.
Lançons le conteneur s3Fs :
[root@localhost mnt]# mkdir /mnt/tests3fs-bucket
[root@localhost mnt]# docker run --name tests3fs --detach -v /root/.s3fs:/root/.s3fs --privileged -v /mnt/tests3fs-bucket:/mnt/local:shared xueshanf/s3fs /usr/bin/s3fs -f -o allow_other -o use_cache=/tmp -o passwd_file=/root/.s3fs tests3fs-bucket /mnt/local
e8b0a9ecb48dd142c6ab8e5c740be822ff148e7eabdd5103f54ffc7725cd8d10
A ce stade, nous pouvons voir que le volume doit propager les montages (tout montage s3Fs (via FUSE) au sein du conteneur sur /mnt/local sera propagé au namespace host sur /mnt/tests3fs-bucket et vice-versa) :
[root@localhost mnt]# docker inspect tests3fs|jq '.[].Mounts[]|select(.Destination == "/mnt/local")'
{
"Source": "/mnt/tests3fs-bucket",
"Destination": "/mnt/local",
"Mode": "shared",
"RW": true,
"Propagation": "shared"
}
[root@localhost mnt]# mount|grep s3
s3fs on /mnt/tests3fs-bucket type fuse.s3fs (rw,nosuid,nodev,relatime,user_id=0,group_id=0,allow_other)
Vérifions que cela fonctionne sur le host :
[root@localhost mnt]# aws s3 ls s3://tests3fs-bucket
[root@localhost mnt]# ls -lh /mnt/tests3fs-bucket/
total 0
[root@localhost mnt]# touch /mnt/tests3fs-bucket/test-from-host
[root@localhost mnt]# aws s3 ls s3://tests3fs-bucket
2017-02-09 17:11:45 0 test-from-host
[root@localhost mnt]# aws s3 rm s3://tests3fs-bucket/test-from-host
delete: s3://tests3fs-bucket/test-from-host
[root@localhost mnt]# ls -lh /mnt/tests3fs-bucket
total 0
Vérifions que cela fonctionne depuis le conteneur webapp :
[root@localhost vagrant]# docker run --name webapp -ti --privileged -v /mnt/tests3fs-bucket:/mnt/local ubuntu
root@8b610f2467c7:/# ls -l /mnt/local
total 0
root@8b610f2467c7:/# touch /mnt/local/test-from-webapp
root@8b610f2467c7:/# exit
exit
[root@localhost vagrant]# aws s3 ls s3://tests3fs-bucket
2017-02-09 17:22:53 0 test-from-webapp
Avant de passer sur ECS, une phase d’amélioration est nécessaire :
Nous sommes maintenant prêts à définir notre tâche ECS… sauf que la définition de volumes au sein des containers ECS ne supporte que partiellement les options disponibles dans Docker !
Ainsi, là où Docker permet de gérer les options [rw|ro], [z|Z],[[r]shared|[r]slave|[r]private], and [nocopy], ECS ne supporte qu'un sous-ensemble de ces options: read-only|read-write.
Impossible de fournir l’option ‘shared’ aux volumes Docker.
A ce stade, les alternatives ont été de :
Je vous laisse deviner ce que le client a retenu …
Dans cet article, nous avons vu :