Journal Yocto+Docker: Les containers personnalisé

Posté par (page perso) . Licence CC by-sa
20
5
avr.
2016

Sommaire

Bonjour 'nal… c'est comme ça qu'on dit n'est pas?

Vous connaissez certainement Docker, la solution permettant de d'automatiser le déploiement d'application sous forme de containers LXC.

Docker fournit un grand nombre d'images prédéfinies par d'autre mais, question: Comment peut-on faire pour créer notre propre image et la déployer?

Je travail avec Yocto qui est conçu pour créer des images aux petits oignions pour des systèmes embarqués. Pourquoi donc ne pas utiliser cet outil pour fabriquer une image qui sera ensuite utilisée dans un container Docker?

Ce journal est écrit pour faire partager mes expériences prend la forme d'un tutoriel expliquant étape par étape et comment créer et exécuter cette image.

Installons de Yocto

Pour débuter avec Yocto, il suffit de se référer au tutoriel Yocto Project Quick Start mais ne vous inquiétez pas je détail toutes les commandes ci-dessous.

Note: Dans mon exemple, je vais utiliser Fedora Core 23 (qui n'ai pas supporté par Yocto-project mais qui fonctionne).
Pourquoi cette distrib? Tout simplement parce que j'avais envie de tester l'interface Gnome3 et que je l'avais sous la main.

Tout commence donc l'installation des outils nécessaires:

sudo dnf install gawk make wget tar bzip2 gzip python unzip perl patch diffutils diffstat \
git cpp gcc gcc-c++ glibc-devel texinfo chrpath ccache perl-Data-Dumper perl-Text-ParseWords \
perl-Thread-Queue socat findutils which SDL-devel xterm

J'ai dû rajouter un paquet manquant (et oui, on m'avait bien prévenu, FC23 n'est pas supporté…)

sudo dnf install perl-bignum

Ensuite, nous devons récupérer Yocto en lui même, et nous allons l'installer dans ~/yocto:

mkdir ~/yocto
cd ~/yocto
git clone git://git.yoctoproject.org/poky -b jethro

Générons notre image

Avant de créer notre image, nous devons d'abord créer notre environnement.
Les commandes suivantes font le travail:

cd ~/yocto/poky
source oe-init-build-env

Maintenant, nous devons définir notre machine cible. Par défaut Yocto propose d'utiliser qemu, dans notre cas nous allons utiliser une cible identique au futur hôte: genericx86-64
Pour cela éditons le fichier ~/yocto/poky/build/conf/local.conf et dé-commentons la ligne suivante:

MACHINE ?= "genericx86-64"

Nous allons aussi vouloir que notre système de fichier soit produit sous forme d'une archive tar.bz2
Ajoutons à la fin de ce même fichier la ligne suivante:

IMAGE_FSTYPES += "tar.bz2 "

Voilà, notre environnement est prêt, nous pouvons lancer la compilation et partir faire quelque chose d'intéressant pendant que le PC travail (en fonction de la puissance de votre machine, ça peut prendre pas mal de temps):

cd ~/yocto/poky/build
bitbake core-image-minimal

L'image résultant de cette compilation se trouve dans ~/yocto/poky/build/tmp/deploy/genericx86-64/

Installons docker

L'installation et le démarrage de docker se fait en deux lignes:

sudo dnf install docker
sudo service docker start

Créons notre container.

Nous allons maintenant créer l'environnement permettant le déploiement de notre image:

mkdir ~/docker
cd ~/docker

Copions l'image créée précédemment dans notre environnement de déploiement Docker:

cp ~/yocto/poky/build/tmp/deploy/genericx86-64/core-image-minimal-genericx86-64-*.rootfs.tar.bz2 docker-image-minimal-genericx86-64.tar.bz2

Nous devons d'abord créer une description de ce dernier grâce à un fichier Dockerfile
Ce fichier contient les instructions permettant la création de notre container. Voici son contenu:

FROM scratch
ADD docker-image-minimal-genericx86-64.tar.bz2 /
CMD ["sh"]

Petite explication: On part "FROM scratch", on ajoute notre système de fichier docker-image-minimal-genericx86-64.tar.bz2 à la racine \. Notre container démarre avec la commande sh.

Il faut maintenant construire et installer notre container.

sudo docker build -t mycontainer .

Cette commande va lire le fichier Dockerfile que nous venons de créer et va créer un container puis décompresser l'image créée avec Yocto.

Il nous reste plus qu'a vérifier si notre image apparaît dans la liste:

sudo docker images

Démarrons notre image

Notre image est installer, démarrons là:

docker run -it --rm mycontainer

Bingo nous sommes dans notre nouvel environnement.

Poussons l'exercice encore un peu plus loin…

En explorant un peu ce que l'on vient de créer, nous constatons tout de même que nous avons une image un peu grosse (8MB) et surtout que nous avons perdu quand même pas mal de temps à compiler le noyau Linux alors qu'avec Docker, il ne nous sert à rien.

Si nous voulons encore réduire notre image et n'inclure dedans que la minimum (busybox), nous allons devoir créer notre propre image.

Pour cela, nous allons créer notre propre layer et l'ajouter aux layers utilisés dans notre environnement Yocto.

mkdir -p ~/yocto/poky/meta-xb/conf
cp ~/yocto/poky/meta-yocto/conf/layer.conf ~/yocto/poky/meta-xb/conf/

Puis nous allons créer notre image qui ne contiendra que busybox:

mkdir -p ~/yocto/poky/meta-xb/recipes-core/images/
cat > ~/yocto/poky/meta-xb/recipes-core/images/docker-image-minimal.bb << EOF
SUMMARY = "Minimal image for docker guest - Proof of Concept"
IMAGE_FSTYPES += "tar.bz2 "
LICENSE = "CLOSED"
IMAGE_INSTALL = "busybox"
inherit image
EOF

Note: J'ai mis LICENSE = "CLOSED" pour me simplifier la vie et ne pas gérer cet aspect pendant mes expériences mais bien entendu, rien n'est fermé ici

Notre layer est prêt. Pour l'ajouter aux layers utilisés dans notre environement, il faut ajouter la ligne suivante à lafin du fichier ~/yocto/poky/build/conf/bblayer.conf

BBLAYERS += "/home/developper/yocto/poky/meta-xb "

Note: /home/developper correspond à ~

Construisons notre nouvelle image:

cd ~/yocto/poky
source oe-init-build-env
bitbake docker-image-minimal

L'image produite ne fait plus que 2MB (soit 4 fois moins que la précédente … et je pense qu'on peut encore faire mieux)

Il est alors possible de la démarrer dans docker en suivant les instructions décrites un peu plus haut.

A partir de maintenant il possible possible de rajouter des recettes Yocto pour avoir un containers "aux petits oignons"…

  • # Bien vu !

    Posté par . Évalué à 7.

    C'est une utilisation très intéressante de Yocto que tu fais là, merci pour ton retour !

  • # Des icônes ?

    Posté par (page perso) . Évalué à -3.

    « Je travail avec Yocto qui est conçu pour créer des images aux petits oignions pour des systèmes embarqués. »

    Encore un journal sur le fait religieux ! Donc il existe maintenant des outils pour consacrer des images à destination des systèmes embarqués ? Le sens de la phrase est un peu obscur. À moins d'une coquille qui aurait remplacé le nom d'une plante potagère par l'imparfait du verbe oindre ?

  • # sur debian...

    Posté par . Évalué à 2.

    mais, question: Comment peut-on faire pour créer notre propre image et la déployer?

    Sous debian et dérivées c'est relativement facile. Debootstrap, tar puis docker import. J'crois que c'est même dans la doc de docker.

  • # Ahhh je vois que je ne suis pas le seul à y avoir pensé :-)

    Posté par (page perso) . Évalué à 3.

    Ça me fait plaisir de voir ça. J'y avais pensé mais je n'ai pas pris le temps de tester :-)
    Merci pour le partage !
    Après pour optimiser l'empreinte, il est possible de ne prendre que les binaires et les librairies nécessaires (via un script d'automatisation qui cible des binaires définis). J'avais déjà fait ça pour un système de backup à chaud qui créait un ré-installeur sur USB dans un job précédent. Les scripts de bases sont présents sur le net.

  • # Docker, LXC

    Posté par . Évalué à 4.

    Vous connaissez certainement Docker, la solution permettant de d'automatiser le déploiement d'application sous forme de containers LXC

    Attention, Docker n'est plus basé sur LXC depuis un bout de temps ;)

    • [^] # Re: Docker, LXC

      Posté par (page perso) . Évalué à 3.

      Effectivement. C'est ma première expérience avec Docker et je découvre donc. En relisant plus attentivement les docs de docker je constate qu'il utilise maintenant libcontainer et plus LXC.
      Merci pour la correction.

  • # Pas convaincu

    Posté par . Évalué à 3.

    Si on veux vraiment joué à qui à la plus courte faut aller plus loin, configuré yocto pour utiliser µlibc ou même musl, strippé le tout etc…
    Mais honnêtement un debian (depuis un deboostrap où on vire les man, les timezones, les langues) ça tient en 40 mo seulement tout en ayant "apt-get". Je préfère largement ça et niveau politique de sécurité je rebuild et redéploie mes images toutes les semaines (donc j'ai un apt update qui s'exécute une fois par semaine).

    • [^] # Re: Pas convaincu

      Posté par . Évalué à 2.

      ça tient en 40 mo seulement tout en ayant "apt-get"

      Dans le cas où doit vraiment isoler chaque service, donc lancer de nombreux « petits » conteneurs, la mémoire occupée par le système (avant lancement du service principal de ce conteneur) est importante. Le temps de démarrage nécessaire à chaque conteneur (entre une Debian minimal et un TinyCore Linux, ou le genre de système « au petits oignons » dont on parle ici, ça fait une nette différence…) ça rentre en ligne de compte.

      • [^] # Re: Pas convaincu

        Posté par (page perso) . Évalué à 2.

        La taille de l'image système c'est pas forcément celle prise en mémoire. Je dois avoir 4Go rien que dans /usr mais fort heureusement mon OS ne prend pas 4Go rien que pour démarrer.

        • [^] # Re: Pas convaincu

          Posté par . Évalué à 1.

          La taille de l'image système c'est pas forcément celle prise en mémoire.

          C’est quand même souvent corrélé. Une Debian (ou autre ditrib’ généraliste) occupe plus de place ET en terme de stockage dans /usr ET en terme d’utilisation de la RAM qu’un Busybox…

          Par ailleurs, je ne connais pas bien Docker, je ne serais pas surpris que Docker charge tout l’userland (ie : le contenu du /usr du conteneur) quand il démarre le conteneur…

          • [^] # Re: Pas convaincu

            Posté par (page perso) . Évalué à 6. Dernière modification le 07/04/16 à 11:20.

            docker ps
            CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
            bd32870eedd7        java:7-jdk          "bash"              2 weeks ago         Up 9 minutes                            hopeful_meninsky
            
            docker images
            REPOSITORY            TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
            docker.io/java        7-jdk               06a1d910310d        3 weeks ago         588.2 MB
            
            docker exec -it hopeful_meninsky bash
            root@bd32870eedd7:/# du -sh /usr/
            533M    /usr/
            
            cat /sys/fs/cgroup/memory/system.slice/docker-bd32870eedd7e6079bce9779702e673a1f178dfce66d513a57854b5569340445.scope/memory.usage_in_bytes 
            4485120

            C'est fou de faire autant de suppositions sur quelque chose qu'on avoue ne pas connaître.

    • [^] # Re: Pas convaincu

      Posté par (page perso) . Évalué à 2.

      Avec Yocto, on peut aussi utiliser apt-get. Yocto pourra même créer des paquets .deb pour des programmes qui ne sont pas dans les dépôts officiels.

      Le but de cette expérience était un petit challenge que je me suis lancé dans le but de tester Docker. Pour l'instant, je n'ai pas trop réfléchi aux cas d'usages possibles. J'ai tout de même le sentiment que le fait de marier les capacités de Yocto à créer des systèmes de fichiers, des dépôts et des paquets à partir de codes sources avec la puissance de docker laisse un champs de possibilités assez vaste…

  • # Excellent journal

    Posté par . Évalué à 3.

    Ça donne envie de tester tout ça !

    Au passage juste une faute dont ce journal mériterait qu’on la corrige :

    (qui n'ai pas supporté par Yocto-project mais qui fonctionne)

    n’est pas

    C’est la seule que j’ai vu (mais elle pique)…

    En tous cas encore une fois bravo, tes explications sont limpides…

    • [^] # Re: Excellent journal

      Posté par (page perso) . Évalué à 2.

      Effectivement, il y a quelques coquilles qui se sont glissées dans ma prose:

      n'est pas? --> n'est ce pas?
      qui n'ai pas --> qui n'est pas
      oignions --> oignons
      la minimum --> le minimum
      il possible possible --> il est possible

      Et peut-être encore d'autres… c'est ça de rédiger un journal à plus de 23:00 ;-)

  • # Alpine

    Posté par . Évalué à 4.

    Je vois passer pas mal de Dockerfile basé sur du Alpine Linux http://alpinelinux.org/
    Je ne connais ni Yocto ni Alpine mais ça me semble le même esprit.

    Finalement le problème de Docker c'est qu'il ne reutilise pas les librairies dynamiques deja chargées c'est ca ? Et c'est donc pour ça que l'utilisation de mini libc contourne le problème ?
    Si c'est le cas on peut aussi se demander pourquoi ne pas faire des binaires statiques du coup…

    • [^] # Re: Alpine

      Posté par . Évalué à 4.

      Finalement le problème de Docker c'est qu'il ne reutilise pas les librairies dynamiques deja chargées c'est ca ?

      Ce n’est pas un problème… le principe d’un conteneur c’est de segmenter… c’est normal que des bibliothèques ne soient pas partagées entre les conteneurs…

      Bien sûr le gestionnaire de conteneurs peut dédoubler certains espaces mémoire mais j’imagine que ça a ses limites si on veut conserver une séparation stricte des environnements d’exécution…

      D’où effectivement la nécessitée de mettre au point un « bare system » des plus léger…

      • [^] # Re: Alpine

        Posté par (page perso) . Évalué à 3.

        Hors conteneur, tous les process se partagent la même zone mémoire pour les zones statiques de la libc, quelque soit leur origine ou UI, et c'est garanti sécurisé par le kernel.
        Après, c'est le boulot du KSM de tout merger, mais ça reste une surcharge. De cpu si c'est les même libs, de RAM si les libs sont toutes différentes

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n'en sommes pas responsables.