Mettre à jour les images Docker : la méthode de Bearstech

le vendredi 7 juillet 2023

Bearstech | Dernière mise à jour : vendredi 7 juillet 2023

Découvrez les bonnes pratiques de Bearstech pour la gestion et la mise à jour sécurisées des images Docker.

Notre prochain webinar

Bearstech propose une série d'images Docker pour des clients les utilisant, en lien étroit avec notre métier d'hébergeur (Nginx, PHP, Nodejs, ...) : https://hub.docker.com/u/bearstech.

Ces images sont maintenues avec la même rigueur que nous apportons au reste de notre infrastructure, en particulier du point de vue de la sécurité, le tout en proposant à nos client un moyen de s'assurer que les images qu'ils utilisent sont bien à jour. Voyons quelles sont les bonnes pratiques que nous avons mis en place à ce sujet.

L'arborescence de nos images Docker

On peut définir une arborescence de ces images Docker à partir du champ "FROM" défini au début de chaque Dockerfile.

Toutes nos images dérivent donc de notre image de base (bearstech/debian), qui est disponible avec les tags de chaque version Debian encore maintenue (Buster, Bullseye et Bookworm au moment de la rédaction de cet article). Cette image n'est pas créée à partir d'un Dockerfile mais "from scratch" grace à l'outil mmdebstrap.

Afin de maintenir ses images Docker, il est important de connaître cette arborescence :

Arborescence des images Docker

La nécessité de reconstruire une image à n'importe quel niveau implique la nécessité de reconstruire toutes les images qui en dérivent (si image1 est rebuildée, alors image1.1, image1.2, image1.2.1 et image1.3 doivent être reconstruites). De même il n'est pas utile de vérifier la nécessité de reconstruire une image si son image "mère" le nécessite.

Cette arborescence peut être formalisée dans un fichier plat, par exemple en YAML, même si ce n'est pas la solution que nous avons retenue :

image1:
  image1.1:
  image1.2:
    image1.2.1:
  image1.3:
image2:
  image2.1:
[...]

Suivi des mises à jour des images Docker

Afin de suivre en interne les mises à jour de nos images, nous utilisons le système de labels des images Docker, soit dans un Dockerfile:

LABEL votre.label=valeur-du-label

On peut alors obtenir les informations d'une image via :

$ docker inspect --format='{{json .Config.Labels}}' bearstech/debian:buster | jq        
{
  "org.opencontainers.image.authors": "Bearstech",
  "org.opencontainers.image.revision": "2244cebd8a4a2226cfa409ed8d2d0adf722f9e48",
  "org.opencontainers.image.url": "https://hub.docker.com/r/bearstech/debian",
  "org.opencontainers.image.version": "bearstech/debian:buster-2023-07-01"
}

Les deux labels utiles pour nous étant:

  • org.opencontainers.image.revision contient un hash de commit et nous permet donc de savoir l'état du dépôt au moment du build,
  • org.opencontainers.image.version que nous avons formalisé en bearstech/IMAGE:TAG-DATE (ici bearstech/debian:buster-2023-07-01), ce tag n'étant pas déployé sur le hub Docker mais a son existence propre en interne.

Ce deuxième tag permet d'avoir immédiatement une information pratique sur la nature et version de l'image, alors que la plupart des images se contentent du hash qui laisse en général assez perplexe (par ex: où consulter une table de correspondance hash -> date ?).

Pourquoi Debian

Vous aurez remarqué que nous n'avons pas choisi une distribution "légère spéciale conteneur" telle que la fameuse Alpine. Il y a plusieurs raisons à ceci :

  • la principale est que notre modèle de maintenance est basée sur le principe Debian des branches stables + patches de sécurité. Le comfort pour les développeurs (environnement stable pendant 2 ans) et pour l'infogéreur (garantie de pouvoir déployer des patches de sécurité sans incidents et sans demander la permission aux développeurs) est incomparable

  • nous avons par ailleurs plus de 20 ans d'expérience, de recettes et d'outils sur les systèmes Debian, il aurait été dommage de ne pas en profiter...

  • et enfin il n'y a pas de contre-indication, car grâce au système de "layers" de Docker, l'image de base Debian est partagée par toutes nos images de notre arborescence : au final le coût de stockage des images sur les serveurs reste très modeste et n'est PAS proportionnel au nombre d'images

Quand mettre à jour les images Docker

Pour la plupart de nos images, nous appliquons la même règle de sécurité que nos serveurs en suivant les mises à jour de sécurité Debian de la pile logicielle que nous maintenons. Nous avons donc opté pour un système nous notifiant quotidiennement des mises à jour de sécurité sur les différentes images. Voici par exemple le mail reçu ayant déclenché la mise à jour de l'image bearstech/debian:buster du 7 juillet:

error: security upgrades on debian:buster:
debian-archive-keyring                           2019.1+deb10u1 ->         2019.1+deb10u2 (Debian-Security:10/oldoldstable)

Les images dérivant de bearstech/debian:buster nécessitent également d'être reconstruites au moins à cause de ces mises à jour de sécurité et ne sont donc ni testées (pour un gain de temps substentiel), ni mentionnées car leur reconstruction est automatique dans le processus de reconstruction de l'image bearstech/debian:buster.

Certaines images nécessitent des tests supplémentaires :

  • images utilisant des logiciels en "rolling release" (PHP dans une version non packagée par Debian, Node.js, etc.) via des dépôts APT hors Debian (nous appliquons alors toute mise à jour de paquet dès qu'elles sont mises à disposition)
  • logiciels spécifiques hors dépôt apt (Ruby, Gitlab, ...) pour lesquels il faut écrire des scripts "maison" pour vérifier l'existence de nouvelles mises à jour

KISS and generic

Comme souvent, le meilleur moyen garantir un niveau de maintenance satisfaisant est de garder le fonctionnement et le processus de mise à jour aussi simples que possible, et de rendre générique tout ce qui peut l'être.

Le processus de publication d'une image est chez nous toujours le même:

  • build de la nouvelle image
  • tests effectuées sur l'image
  • archivage interne et publication sur le hub Docker
  • mêmes étapes pour les images dérivant de cette image

Voici donc le résultat pour la publication de l'image bearstech/debian:buster:

$ docker-bearstech-full-build debian:buster
info:  building image debian:buster
info:  testing image debian:buster
info:  pushing image debian:buster
info:  image debian:buster published
info:  rebuilding debian:buster dependencies: php-cli:7.3
info:  building image php-cli:7.3
info:  testing image php-cli:7.3
info:  pushing image php-cli:7.3
info:  image php-cli:7.3 published
info:  rebuilding php-cli:7.3 dependencies: php-composer:7.3 php:7.3
info:  building image php-composer:7.3
info:  testing image php-composer:7.3
info:  pushing image php-composer:7.3
info:  image php-composer:7.3 published
info:  building image php:7.3
info:  testing image php:7.3
info:  pushing image php:7.3
info:  image php:7.3 published

Chaque étape est bloquante (pas de publication sur le hub Docker si les tests ne passent pas) et peut être rejouée indépendemment :

$ docker-bearstech-test debian:buster
[...]
# Résultats de tests laissés à notre discrétion
[...]
info:  image bearstech/debian:buster-2023-07-01 tested successfully

Enfin, si certaines étapes du build peuvent être rendues génériques, cela allègera les Dockerfiles et vous y gagnerez en homogénéité. Voici par exemple un Dockerfile utilisé à la fin de chaque build d'image pour y ajouter nos labels :

$ cat labels/Dockerfile 
ARG IMAGE
ARG TAG
ARG BUILD_DATE
FROM bearstech/$\{'IMAGE'}:$\{TAG}-$\{BUILD_DATE}

ARG IMAGE
ARG TAG
ARG GIT_REVISION
ARG BUILD_DATE
LABEL org.opencontainers.image.authors=Bearstech
LABEL org.opencontainers.image.revision=$\{GIT_REVISION}
LABEL org.opencontainers.image.version=bearstech/$\{IMAGE}:$\{TAG}-$\{BUILD_DATE}
LABEL org.opencontainers.image.url=https://hub.docker.com/r/bearstech/$\{IMAGE}

Sur l'image sans labels bearstech/debian:buster-2023-07-01 qui vient d'être buildée, il nous suffit de lancer cette commande (intégrée au processus de construction) pour y ajouter nos labels :

$ docker build --build-arg IMAGE=debian --build-arg TAG=buster --build-arg BUILD_DATE=$(date +%Y-%m-%d) --build-arg GIT_REVISION=$(git rev-parse HEAD) labels/

Avertir de la mise à jour d'une image

Il est de notre responsabilité de mettre à jour les images que nous proposons mais il revient à nos clients de mettre à jour leurs propres images qui en dérivent. Pour cela, nous avons intégré à notre dashboard multicloud un module affichant les mises à jour d'images Docker disponibles.

Présentation des images docker sur notre dashboard multicloud

Si de plus vous avez souscrit à notre système de workflow, il ne vous reste plus qu'un clic pour redéployer vos applications et profiter de ces mises à jour sans effort et en toute sérénité.

Conclusion

Dans un article précédent nous avions détaillé la position de bearstech vis-à-vis de Docker, en particulier son utilisation en production.

Cet article vient préciser comment nous gérons dans les faits les images dont nous avons la résponsabilité.

Comme toujours, notre approche vise plusieurs objectifs : mettre en œuvre les images les plus simples et sécurisées possible, tout en facilitant autant que faire se peut le quotidien de nos clients dans le cadre de nos prestations d'infogérance.

La maintenance et la mise jour des images Docker est une tâche complexe qui requièrent une grande rigueur et organisation.

Notre approche méthodique nous permet de relever ces défis efficacement !

Service Hébergement et Infogérance Cloud docker

Bearstech vous propose ses services Hébergement et Infogérance Cloud docker

Découvrir ce service

Partager cet article

Flux RSS

flux rss

Partager cet article :

Nos expertises technologiques