La création d’un Container
La base de l’orchestration passe par la création d’un container. Souvent directement intégré, et utilisant régulièrement des images de provenances diverses et variées, il est primordial de faire preuve d’une hygiène de déploiement stricte afin d’en assurer le déploiement.
Chaque organisation doit dépendre d’une ligne directrice forte et rigoureuse lorsqu’il s’agit de la création des containers, et adopter un ensemble de bonnes pratiques et de règles nécessaires pour maintenir la qualité exigée.
Le rapport annuel de snyk ( https://snyk.io/open-source-security/ )est souvent cité en référence. Il nous montre que le nombre de vulnérabilités reste important même sur des images officielles à l’état critique. Ces dernières années, la tendance est plutôt positive. Malgré tout, il est essentiel de prendre toutes les précautions nécessaires.
Des besoins organisationnels
Il y a de nombreux débats sur de multiples points très spécifiques, qui ne sont pas uniquement techniques, mais avant tout organisationnels.
Par exemple, nous pouvons parler des différences au sujet de la mise à jour des packages dans le build.
RUN yum -y update && yum clean all
Cette méthodologie va dépendre de la sécurité, du besoin de build reproductible, de la manière de gérer les packages, de la stratégie de Tag…
En simplifiant, nous pouvons résumer ceci aux deux positions suivantes :
- Les « pro upgrade » pour qui le fait que la sécurité soit critique, demande de prendre en compte cet aspect e à chaque instant, sans intervention du coté Developpeur.
- Les « pro reproductibilité », qui arguent que les images sont non reproductibles, et qu’il peut y avoir des différences entre deux builds censés être identiques. Pour eux, il est plus pertinent de faire une nouvelle image avec des versions fixes, et retester correctement l’image.
Il reste qu’un certain nombre de pratiques doivent être mises en place, indépendamment des débats :
- Les images doivent être scannées : Cela permet de comprendre les risques.
- Les images doivent être légères : Dans un contexte de généralisation de l’usage du Cloud, cela permet de limiter la bande passante nécessaire (c’est d’autant plus vrai pour la CI/CD).
- Les images doivent avoir des healtchecks : C’est le minimum pour s’assurer du bon fonctionnement de l’image à tout instant.
- Ne pas avoir des images différentes en fonction des environnements : Nous retombons dans un classique « cela marche chez moi » pouvant être très agaçant 🙂 .
- Gérer correctement les secrets et les environnements.
Cas concret
Parfois une image qui semble simple peu cacher des problèmes plus profonds.
Extrêmement simple, cette image semble être plutôt bien étudiée et ne suscite pas de méfiance particulière (pas de layer, pas de healthcheck, pas de set -e, cela reste classique). Et pourtant :
Cette image pèse 950Mo, cela représente ~0.08$ par pull dans AWS. Nous parlons d’une image qui doit potentiellement être scalable.
En ce qui concerne les éventuelles vulnérabilités :
Nous constatons qu’elles viennent d’ailleurs principalement de l’image de base :
Si nous nous intéressons plus en détails à la construction de l’image, et plus spécifiquement aux différentes couches, voici ce qui en ressort :
Analysons rapidement ce résultat.
- L’image de base (debian:buster) pèse 114MB
- L’ ajout de l’image buildpack-deps :buster installe de nombreux outils pour le développement :
- Git
- Subversion
- Curl
- Mercurial (146Mo rien que pour cette dépendance)
- Un ensemble d’outils de compilation et de développement (510Mo).
L’image officielle est conçue pour tester de manière locale le développement de locust ( https://locust.io/ ), or, pour l’utilisateur final, elle dispose de plusieurs défauts :
- Elle est relativement lourde. Lors d’un déploiement qui doit avoir une certaine scalabilité, l’image sera téléchargée pour chaque nœud.
- Elle reconstruit / compile le code à chaque fois.
- Elle n’est pas à jour au niveau sécurité.
Le principal défaut vient de l’image debian/python 3.8. Elle dispose de nombreux outils pour télécharger les différents packages python nécessaires à l’utilisation et la compilation de l’application. L’image de base (debian) est « relativement » légère, 114MB.
Dans les bonnes pratiques et pour réduire la taille des images, nous utilisons principalement ces méthodes :
- L’utilisation de .dockerignore (Non utilisé ici).
- L’utilisation des builds multi-stage (Non utilisé ici).
- L’agrégation des commandes RUN.
- L’utilisation d’une image de base simple.
- La suppression des packages interdépendants inutiles.
En reprenant des images de base simples, il est possible d’optimiser de manière assez importante le résultat, soit en partant sur la même base debian, soit en prenant une base alpine ou redhat.
Vous remarquerez ce qui semble être des lignes de commande trop longues, que nous retrouvons couramment dans de nombreuses images. Si pour le développement, il peut être pratique de séparer les différentes couches comme dans l’exemple suivant :
Il reste important dans un contexte de finalisation, de regrouper les layers en utilisant ce format :
La différence entre ces deux écritures est assez équivoque. La première va créer une image plus de deux fois plus importante que la seconde (383MB VS 132MB), à cause des layers intermédiaires. De nouveau, dans un contexte cloud, et avec la possibilité d’instancier plusieurs containers, c’est un gain de temps et de trafic qui se traduit par une réduction de coûts.
A titre de comparaison, au niveau de la taille :
Au niveau des vulnérabilités, c’est également très équivoque :
Nous remarquerons le nombre de dépendances, qui est nettement plus faible.
Si nous prenons la base debian (base utilisée par le projet d’origine) :
Il reste un certain nombre de problèmes, mais nous avons réduit la surface d’attaque. Il reste 110 dépendances uniquement (contre 431 pour l’image officielle) et de problèmes de sécurité (72 contre 335)
Il est intéressant de vérifier régulièrement les images utilisées, puisqu’il est possible d’avoir des gains substantiels :
P.S. Il est à noter que les images mentionnées et les dockerfiles correspondants ne sont pas prêts pour la production. Il est nécessaire, par exemple, d’avoir non pas des images latest, mais bien des images correspondant au tag de l’application, et donc de restreinte à la construction, l’utilisation du tag adéquat.
De même, nous parlons d’un outil qui s’utilise assez simplement, et qui ressemble plus à un packaging d’applications, qu’un service spécifique, sans que cela n’ôte l’intérêt de l’ajout de healthcheck et de liveprobe.
Conclusion
En conclusion, nous pouvons constater que la réduction de la taille de l’image, ainsi que sa relative sécurisation via la limitation des packages intégrés est relativement simple à mettre en place, ne serait-ce que, comme dans le cas présent, en revoyant très légèrement le process de construction, et ce, sans modifier le résultat pour l’utilisateur.
Découvrez nos services et nos pratiques Software Product Engineering.