La plupart des fichiers Dockerfiles
partent d'une image parent. Lorsqu'on souhaite contrôler complètement le contenu de l'image, on peut créer une image de base. Voici la différence:
FROM
dans le fichier Dockerfile
. Chaque déclaration ultérieure dans le fichier Dockerfile
modifie cette image parente.FROM
dans son fichier Dockerfile
.Cette article présente plusieurs façons de créer une image de base. Le processus spécifique dépendra fortement de la distribution Linux que l'on souhaite empaqueter.
Pour aider à faire un choix qui correspond aux besoins, cette section passe en revue certains des critères pertinents afin de l'éclairer et présente le choix d'un OS sous l'angle de la taille sur le disque de l'image.
Il existe un certain nombre de critères communs pour choisir une image de base:
En plus des avantages des de taille évidente qu'offrent les images plus petites, l'environnement devrait être également plus facile çà maintenir et plus efficace: les petites images augmentent la sécurité tout en réduisant la taille de l'empreinte de sécurité.
Afin de produire un image docker la plus petite possible elle doit contenir uniquement les éléments de base nécessaires à l'exécution de l'application.
La liste ci dessous compile les images les plus populaires du système d'exploitation de base, basées sur la taille du fichier ainsi que quelques critères qualitatifs:
En général, il faut commencer par une machine exécutant la distribution que l'on souhaite empaqueter en tant qu’image parent, bien que cela ne soit pas nécessaire pour certains outils tels que Debian Debootstrap
, que l'on peut également utiliser pour créer des images Ubuntu.
Cela peut être aussi simple que cela pour créer une image parent Ubuntu:
$ sudo debootstrap xenial xenial > /dev/null $ sudo tar -C xenial -c . | docker import - xenial
$ docker run xenial cat /etc/lsb-release DISTRIB_ID=Ubuntu DISTRIB_RELEASE=16.04 DISTRIB_CODENAME=xenial DISTRIB_DESCRIPTION="Ubuntu 16.04 LTS"
Cette section détaille comment créer une image Arch Linux
à partir de zéro ainsi que les bonnes pratiques pour créer une image afin de connaître exactement ce qui est installé dans le conteneur.
Comme point de départ, on utilise le script mkimage-arch.sh
utilisé dans le lab:
Le script doit être exécuté en tant que root
if [ “$(id -u)” != “0” ]; then printf "This script must be run as root\n" exit 1 fi
Il y a deux paquets requis: expect
+ pacstrap
qui peut être installé avec:
$ pacman -S arch-install-scripts expect
Le script vérifie ceci comme suit:
hash pacstrap &>/dev/null || { printf "Could not find pacstrap. Run pacman -S arch-install-scripts" exit 1 } hash expect &>/dev/null || { printf "Could not find expect. Run pacman -S expect" exit 1 }
Le || est similaire à && sauf qu'il indique au shell d'évaluer uniquement l'expression après, lorsque la première expression échoue.
La commande hash affecte la manière dont l'environnement shell actuel se souvient des emplacements des utilitaires trouvés. Si exécuté sans aucun paramètre, il indique le chemin de toutes les commandes exécutées depuis la dernière réinitialisation du hachage (hash -r), par exemple.
$ hash hits command 1 /usr/bin/git 1 /usr/bin/vim 3 /usr/bin/cat 1 /usr/bin/touch 1 /usr/bin/mv 1 /usr/bin/mkdir 3 /usr/bin/man 13 /usr/bin/ls
La table de hachage est une fonctionnalité de bash qui l’empêche de chercher dans $PATH
chaque fois qu'on tape une commande en mettant en cache les résultats en mémoire. Pour ce cas d'utilisation, on l'utilise comme moyen de tester si la commande est disponible.
PACMAN_EXTRA_PKGS='' EXPECT_TIMEOUT=60 ARCH_KEYRING=archlinux DOCKER_IMAGE_NAME=archlinux
On créé un système de fichiers racine temporaire avec mktemp:
ROOTFS=$(mktemp -d /tmp/rootfs-archlinux-XXXXXXXXXX)
mktemp
crée un répertoire temporaire (ou un fichier) basé sur le modèle fourni pour randomiser le nom. Chaque valeur X est remplacée par une chaîne aléatoire.
puis on définit les autorisations
chmod 755 "$ROOTFS"
Afin de produire une image minimale, on définit une liste des packages à ne pas installer:
PKGIGNORE=( cryptsetup device-mapper dhcpcd iproute2 jfsutils linux lvm2 man-db man-pages mdadm nano netctl openresolv pciutils pcmciautils reiserfsprogs s-nail systemd-sysvcompat usbutils vi xfsprogs )
Puis on développe un tableau sans index ne donne que le premier élément.
$IFS est une variable de shell spéciale qui spécifie le séparateur interne de champ .
IFS=',' PKGIGNORE="${PKGIGNORE[*]}" unset IFS printf "%s""\nPackages not to be installed : $PKGIGNORE\n"
On définit la variable PACMAN_CONF
avec le fichier de configuration créé précédemment
PACMAN_CONF='./arch-docker-pacman.conf'
On définit la variable PACMAN_MIRRORLIST
en fournissant l'url des dépôts AUR
PACMAN_MIRRORLIST='Server = https://mirrors.kernel.org/archlinux/\$repo/os/\$arch' export PACMAN_MIRRORLIST
export LANG="C.UTF-8"
arch-chroot "$ROOTFS" /bin/sh -c "ln -s /usr/share/zoneinfo/UTC /etc/localtime"
echo 'en_US.UTF-8 UTF-8' > "$ROOTFS"/etc/locale.gen arch-chroot "$ROOTFS" locale-gen
Pour une installation unattented On utilise expect
pour répondre automatiquement à pacstrap
Expect
est un programme qui “parle” à d'autres programmes interactifs selon un script. Dans l'exemple du script ci-dessous:
pacstrap
avec les arguments -C $PACMAN_CONF -c -d -G -i $ROOTFS base haveged $PACMAN_EXTRA_PKGS --ignore $PKGIGNORE
"anyway? [Y/n] "
, "(default=all): "
ou "installation? [Y/n]"
pour répondre avec send
stdin
expect <<EOF set send_slow {1 .1} proc send {ignore arg} { sleep .1 exp_send -s -- \$arg } set timeout $EXPECT_TIMEOUT spawn pacstrap -C $PACMAN_CONF -c -d -G -i $ROOTFS base haveged $PACMAN_EXTRA_PKGS --ignore $PKGIGNORE expect { -exact "anyway? \[Y/n\] " { send -- "n\r"; exp_continue } -exact "(default=all): " { send -- "\r"; exp_continue } -exact "installation? \[Y/n\]" { send -- "y\r"; exp_continue } } EOF
arch-chroot "$ROOTFS" /bin/sh -c 'rm -r /usr/share/man/*'
On utilise haveged
pour générer des nombres aléatoires et alimenter un périphérique aléatoire Linux. On peut exécuter pacman-key -init
avant d'utiliser pacman pour la première fois; le trousseau de clés local peut ensuite être rempli avec les clés de tous les emballeurs officiels d'Arch Linux
avec pacman-key –populate archlinux
.
arch-chroot "$ROOTFS" /bin/sh -c "haveged -w 1024; pacman-key --init; pkill haveged; pacman -Rs --noconfirm haveged; pacman-key --populate $ARCH_KEYRING; pkill gpg-agent"
Puis on charge pacman mirrorlist avec le contenu de PACMAN_MIRRORLIST
arch-chroot "$ROOTFS" /bin/sh -c "echo $PACMAN_MIRRORLIST > /etc/pacman.d/mirrorlist"
udev
est un gestionnaire de périphériques pour le noyau Linux. Il gère principalement les nœuds de périphériques dans le répertoire /dev
et gère également tous les événements d'espace utilisateur générés lorsque des périphériques sont ajoutés au système ou supprimés de celui-ci, y compris le chargement du microprogramme requis par certains périphériques
udev
ne fonctionne pas dans les conteneurs, il faut donc reconstruire manuellement /dev
DEV=$ROOTFS/dev rm -rf "$DEV" mkdir -p "$DEV" mknod -m 666 "$DEV"/null c 1 3 mknod -m 666 "$DEV"/zero c 1 5 mknod -m 666 "$DEV"/random c 1 8 mknod -m 666 "$DEV"/urandom c 1 9 mkdir -m 755 "$DEV"/pts mkdir -m 1777 "$DEV"/shm mknod -m 666 "$DEV"/tty c 5 0 mknod -m 600 "$DEV"/console c 5 1 mknod -m 666 "$DEV"/tty0 c 4 0 mknod -m 666 "$DEV"/full c 1 7 mknod -m 600 "$DEV"/initctl p mknod -m 666 "$DEV"/ptmx c 5 2 ln -sf /proc/self/fd "$DEV"/fd
Chargement du système de fichiers racine dans un fichier tar et import de l' image dans Docker.
tar --numeric-owner --xattrs --acls -C "$ROOTFS" -c . | docker import - "$DOCKER_IMAGE_NAME"
Options tar utilisées:
--numeric-owner
Toujours utiliser des nombres pour les noms d'utilisateur/groupe.--xattrs
Activer le support des attributs étendus--acls
Activer la prise en charge des ACL POSIX-C
Changer de répertoire-c
Créer une nouvelle archivedocker run --rm -t $DOCKER_IMAGE_NAME echo Success
L'exécution du script devrait se terminer par
tar: ./etc/pacman.d/gnupg/S.gpg-agent: socket ignored sha256:82b0356924efb95e5483417dd4f0cef85fce7afbacb75c18632de7bc45edd796 Success
rm -rf "$ROOTFS"