Resources attached to the Road To DevOps tutorial https://blog.noobtoroot.xyz/road-to-devops/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

25 KiB

Docker

Schema Docker Swarm

Images de conteneurs

Maxime Poullain • Christian Tritten

Qu'est-ce qu'une image de conteneur ?

Une image :

  • Est un template de conteneur en lecture seule.

  • Est composée de fichiers et de métadonnées.

  • Peut par exemple contenir un système d'exploitation Debian avec Apache et une application web pré-installés.

  • Est "instanciée" pour créer un ou plusieurs conteneurs.

Les images Docker les plus populaires en 2020

Source : https://www.docker.com/blog/docker-index-shows-continued-massive-developer-adoption-and-activity-to-build-and-share-apps-with-docker/

Images incrémentales et réutilisables

Images réutilisables

Remarque importante

Si l'image de base est modifiée (par exemple dans le cadre de l'application d'une mise à jour de sécurité), et que l'on souhaite propager cette mise à jour sur les images dérivées, il faudra reconstruire ces dernières.

Système de fichiers multi-couches

  • Les images sont composées d'une ou plusieurs couches superposées.

  • Chaque couche représente un différentiel des changements apportés par rapport à la couche inférieure.

  • Lorsque plusieurs images partagent des couches, cela permet d'optimiser l'espace disque, et les temps de transfert.

Système de fichiers multi-couches

  • La principale différence entre un conteneur et une image réside dans la couche en r/w du sommet.

  • Toutes les écritures qui ajoutent ou modifient des données dans un conteneur sont stockées sur cette couche.

  • Quand le conteneur est supprimé la couche en r/w est aussi supprimée, les couches inférieures de l'image de base restent inchangées.

  • On peut sauvegarder les modifications effectuée au sein d'un conteneur en utilisant la commande : docker commit

  • Ceci va ajouter une nouvelle couche en lecture seule au dessus de la pile composant l'image.

Lorsqu'une image est modifiée, une nouvelle couche est ajoutée au sommet de la pile :

Ajout d'une nouvelle couche

Partage d'image entre plusieurs conteneurs

Partage d'image entre plusieurs conteneurs

## Gestion des images

Outils et commandes pour la gestion des images

Images locales

docker image ls

$ docker image ls
REPOSITORY             TAG          IMAGE ID            CREATED             SIZE
composeformation_web   latest       0d2d42971538        20 hours ago        237MB
debian-formation       latest       257c415ffcc7        20 hours ago        237MB
mariadb                10.1.24      98f78d96be9c        2 weeks ago         395MB
debian                 jessie       3e83c23dba6a        3 weeks ago         124MB
hello-world            latest       48b5124b2768        4 months ago        1.84kB

Le Docker Hub

Le Docker Hub est un entrepôt d'images de conteneurs sur lequel on peut télécharger :

  • des images officielles certifiées par Docker
  • des images publiques maintenues par la communauté

Recherche mariadb sur le Docker Hub

Attention !

Docker ne garantie pas le bon fonctionnement
ni même l'absence de faille de sécurité
sur les images non officielles.

Chercher une image sur le Docker Hub

docker search IMAGE

$ docker search mariadb
NAME                      DESCRIPTION                               STARS  OFFICIAL
mariadb                   MariaDB is a community-developed for...   1354   [OK]
bitnami/mariadb           Bitnami MariaDB Docker Image              37
paintedfox/mariadb        A docker image for running MariaDB 5...   29
million12/mariadb         MariaDB 10 on CentOS-7 with UTF8 default  14
toughiq/mariadb-cluster   Dockerized Automated MariaDB Galera ...   11
webhippie/mariadb         Docker images for mariadb                 9
gists/mariadb             MariaDB on Alpine                         7
panubo/mariadb-galera     MariaDB Galera Cluster                    7
kakilangit/mariadb        Docker for MariaDB with OQGraph & To...   6
maxexcloo/mariadb         Service container with MariaDB insta...   4
tianon/mariadb            DEPRECATED; use mariadb:* -- ♪ "I ju...   4
takaomag/mariadb          docker image of archlinux (mariadb)       2
drupaldocker/mariadb      MariaDB for Drupal                        1
...

Récupérer une image sur le Docker Hub

docker pull IMAGE[:TAG]

$ docker pull mariadb:10.7.1
Using default tag: latest
latest: Pulling from library/mariadb
10a267c67f42: Pull complete
c2dcc7bb2a88: Pull complete
17e7a0445698: Pull complete
9a61839a176f: Pull complete
64675690edb1: Pull complete
3de17e251488: Pull complete
f814b22b783e: Pull complete
733ce1f03439: Pull complete
fb7b719835fd: Pull complete
8d3f82357729: Pull complete
a4f4cbdfcf7c: Pull complete
Digest: sha256:4b54358541679032f6c3a9d9fc944ad96d77ae72fecd6cb44bf18cf97743da24
Status: Downloaded newer image for mariadb:10.7.1

Visualiser l'historique d'une image

docker history IMAGE

$ docker history mariadb:10.7.1
IMAGE          CREATED       CREATED BY                                      SIZE
98f78d96be9c   2 weeks ago   /bin/sh -c #(nop)  CMD ["mysqld"]               0B
<missing>      2 weeks ago   /bin/sh -c #(nop)  EXPOSE 3306/tcp              0B
<missing>      2 weeks ago   /bin/sh -c #(nop)  ENTRYPOINT ["docker-ent...   0B
<missing>      2 weeks ago   /bin/sh -c ln -s usr/local/bin/docker-entr...   34B
<missing>      2 weeks ago   /bin/sh -c #(nop) COPY file:d559178e6a2929...   5.6kB
<missing>      2 weeks ago   /bin/sh -c #(nop)  VOLUME [/var/lib/mysql]      0B
<missing>      2 weeks ago   /bin/sh -c sed -Ei 's/^(bind-address|log)/...   5.27kB
<missing>      2 weeks ago   /bin/sh -c {   echo mariadb-server-$MARIAD...   252MB
<missing>      2 weeks ago   /bin/sh -c echo "deb https://repo.percona....   114B
<missing>      2 weeks ago   /bin/sh -c set -ex;  export GNUPGHOME="$(m...   21.1kB
<missing>      2 weeks ago   /bin/sh -c apt-get update && apt-get insta...   14.3MB
<missing>      2 weeks ago   /bin/sh -c set -x  && apt-get update && ap...   4.58MB
<missing>      2 weeks ago   /bin/sh -c groupadd -r mysql && useradd -r...   330kB
<missing>      3 weeks ago   /bin/sh -c #(nop) ADD file:f4e6551ac34ab44...   124MB

"Inspecter" une image

docker inspect IMAGE

$ docker inspect mariadb
[
    {
        "Id": "sha256:98f78d96be9c7f513f21de040d083ee7ba23d74c8f3bc499373e56e93c...",
        "RepoTags": [
            "mariadb:10.7.1"
        ],
        "RepoDigests": [
            "mariadb@sha256:4b54358541679032f6c3a9d9fc944ad96d77ae72fecd6cb44bf1..."
        ],
        "Parent": "",
        "Comment": "",
        "Created": "2017-05-09T17:28:06.071608373Z",
        "Container": "83ce76bba170200d3783bde70b7c1d06a61ed2b91bec7351a5c5a664f5...",
        "ContainerConfig": {
            "Hostname": "200591939db7",
            "Domainname": "",
            "User": "",
            "ExposedPorts": {
                "3306/tcp": {}

Supprimer une image

docker image rm IMAGE [IMAGE...]

$ docker image rm mariadb:10.7.1
Untagged: mariadb:10.7.1
Untagged: mariadb@sha256:4b54358541679032f6c3a9d9fc944ad96d77ae72fecd6cb44bf18cf...
Deleted: sha256:98f78d96be9c7f513f21de040d083ee7ba23d74c8f3bc499373e56e93c8e9ec9
Deleted: sha256:bea03b338eb87d64861847305aa63f6104212c60719168f25b54ca713db4b870
Deleted: sha256:519db73d66bef13a78573160ddf2059f9dc382e03fd2e85f354c3172ded67b90
Deleted: sha256:7f728a3fd818a51a5425306ef40f398c7698f4252ade70cb83b3d26b825bb613
Deleted: sha256:48159803f1446a31e60af329025fc5c3ae8ef07f950d8750a7e14d46d1d1191c
Deleted: sha256:ff5b1cc6d50c6f7ab9e6ee77ff89b0b037a6840f7b1f44cbe234499362221c15
Deleted: sha256:d147674f5cce42f85942e815f154c6a7ecb86359689d823a9840b28126a12f4e
Deleted: sha256:571be45150dde0fb8f6c3862abbcfa06fbad0e6a128d459a6b8ad0660f9f0660
Deleted: sha256:458271f19a2c854fc6fd3338f9151662173b96a14cfe1a2e46eca95d27e4102c
Deleted: sha256:ba779192baede4aadd009c269406b5e8fd885c653ce19719316bf40cc66a6cf3
Deleted: sha256:2302bd8bbdd530199aa432c357a4da9eab2621c3ba4c4dacb4ea0f4afecbcae7
Deleted: sha256:00771f8e1e12bdfc9d47bc52a78e3f5ce5306a1caa5dd6237731cff9ca106040
Deleted: sha256:8d4d1ab5ff74fc361fb74212fff3b6dc1e6c16d1e1f0e8b44f9a9112b00b564f

Importer / Exporter une image

  1. Export
    $ docker save -o mon-image.tar IMAGE

  2. Import
    $ docker load -i mon-image.tar

Travaux pratiques

Travaux pratiques

TP Docker Images

Dockerfile

Automatiser la construction d'une image

  • Il est possible de construire une image à la main puis de la sauvegarder avec un docker commit.

  • Toutefois ceci est fastidieux, non parfaitement reproductible et donc potentiellement source d'erreur.

  • D'autre part, comment gérer les mises à jours d'une telle image ?

  • Docker est capable de construire des images automatiquement à partir des instructions d'un Dockerfile.

  • Le Dockerfile est un fichier texte qui contient toutes les instructions permettant de construire une image Docker pour une application donnée.

https://docs.docker.com/engine/reference/builder/

Dockerfile

  • Le Dockerfile est versionnable et permet de produire une image à tout moment.

  • Ceci s'inscrit dans la philosophie Infrastructure as Code qui prône la définition d'une architecture dans des fichiers textes déclaratifs.

  • Grâce au Dockerfile on peut reconstruire périodiquement une image afin quelle intègre les dernières mises à jour applicatives et les derniers patches de sécurité.

Exemple de Dockerfile

FROM debian:bullseye
LABEL maintainer "robert@produpot.com"

# On installe Apache httpd
ENV DEBIAN_FRONTEND noninteractive
RUN apt update \
  && apt install -y apache2 \
  && rm -rf /var/lib/apt/lists/*

# Ajout d'un script d'init
ADD run.sh /run.sh
RUN chmod 755 /run.sh

# Importe l'application
RUN mkdir -p /app && rm -fr /var/www/html && ln -s /app /var/www/html
ADD homepage/ /app

# On expose le port 80
EXPOSE 80

# On indique le script qui doit être lancé au démarrage du conteneur
ENTRYPOINT ["/run.sh"]

Principales directives

Directive | Description

  •           | -
    

FROM | Spécifie l'image de départ pour la construction de la nouvelle image. LABEL | Ajoute des métadonnées à la nouvelle image. ENV / ARG | Ajoute des variables d'environnement. ADD / COPY | Copie des fichiers ou des dossiers sur le système de fichiers de l'image. VOLUME | Créé un point de montage à l'instanciation du conteneur.

Principales directives (suite)

Directive | Description

  •           | -
    

RUN | Exécute une commande et "commite" le résultat dans une nouvelle couche de l'image. USER | Spécifie l'utilisateur à utiliser pour jouer les instructions RUN et ENTRYPOINT. HEALTHCHECK | Indique une commande qui sera lancée à l'intérieur du conteneur pour vérifier que celui-ci tourne correctement. ENTRYPOINT | Définie une commande de base à exécuter dans le conteneur. CMD | Définie les paramètres par défaut de la commande de base.

LABEL

LABEL maintainer="user@example.com" \
      vendor=ACME\ Incorporated \
      com.example.version="0.0.1-beta" \
      com.example.release-date="2015-02-12"

Choisir entre ARG et ENV

  • ARG et ENV permettent de déclarer des variables qui sont utilisables à partir du moment où elles sont déclarées dans le Dockerfile.

  • Les ARG peuvent être surchargées au moment du build via l'option --build-arg.

  • Les ENV peuvent être surchargées à l'exécution via l'option -e.

  • Si une variable ARG est utilisée sans valeur par défaut et qu'aucune valeur n'est fournie via --build-arg, cela déclenche une erreur lors du build.

ARG vs ENV

https://vsupalov.com/docker-arg-env-variable-guide/

Dockerfile

ARG MY_VAR_1                  <---- expect a build-time variable
ARG MY_VAR_2=pouet            <---- set a build-time variable

RUN touch "$MY_VAR_2"

ARG A_VARIABLE                <---- expect a build-time variable
ENV another_var=$A_VARIABLE   <---- use the value to set the ENV var default

                                    if not overridden, that value of another_var
                                    will be available to your containers!

Choisir entre ADD et COPY

Les deux directives ont la même syntaxe :

COPY <src>... <dest>
ADD  <src>... <dest>

Selon le guide des bonnes pratiques Docker :

  • Utilisez COPY dans tous les cas, sauf si vous avez besoin d'extraire automatiquement le contenu d'une archive, dans ce cas précis utilisez ADD.

  • Pour récupérer des fichiers distants, préférez plutôt l'instruction RUN wget ....

CMD et ENTRYPOINT

Les commandes CMD et ENTRYPOINT permettent
de définir la commande par défaut à exécuter
à l'intérieur du conteneur.

  • ENTRYPOINT définie la commande de base pour le conteneur,

  • CMD définie les paramètres par défaut pour cette commande.

FROM debian:bullseye
RUN apt update && apt install -y cowsay
ENTRYPOINT ["/usr/games/cowsay"]
CMD ["hello"]
$ docker build -t cowsay .
...
Successfully built a27691083512
Successfully tagged cowsay:latest
$ docker run cowsay
 -------
< hello >
 -------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
$ docker run cowsay 2,21 Gigowatts ?!
 -------------------
< 2,21 Gigowatts ?! >
 -------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

HEALTHCHECK

L'exemple suivant teste toutes les 5 minutes que le conteneur est capable de servir une ressource HTTP en moins de 3 secondes :

HEALTHCHECK --interval=5m --timeout=3s \
  CMD curl -f http://localhost/ || exit 1

Le test de santé étant lancé depuis l'intérieur du conteneur, la commande curl utilisée dans l'exemple ci-dessus doit être présente dans le conteneur.

Le status healthy / unhealthy est consultable
via docker ps ou docker inspect

$ docker ps
CONTAINER ID  IMAGE     COMMAND      CREATED         STATUS
9e2ea5f59f8b  xian/web  "/start.sh"  28 minutes ago  Up 28 minutes (healthy)
20851619e1af  xian/web  "/start.sh"  23 minutes ago  Up 23 minutes (unhealthy)
$ docker inspect -f '{{json .State.Health.Status}}' conteneur
"healthy"

Il s'agit d'un simple message informatif, en effet Docker ne relance pas de lui-même un conteneur détecté comme unhealthy.

  • Toutefois, Le changement de santé d’un conteneur génère un évenement Docker que les outils de monitoring et d’orchestration peuvent intercepter.

  • Par exemple l'orchestrateur Swarm utilise cette information pour remplacer automatiquement le conteneur défectueux par une nouvelle instance.

Construire l'image à partir du Dockerfile

docker build -t IMAGE[:TAG] .

Les tags permettent de proposer
plusieurs versions d'une image.

docker build -t mon-image:v1 .

Une même image peut
être tagguée plusieurs fois :

ex : debian:10 et debian:buster

Derrière un proxy

--build-arg http_proxy=PROXY

$ docker build --build-arg http_proxy=http://my.proxy.url:3128 \
              --tag debian-formation .

Travaux pratiques

Travaux pratiques

TP Dockerfile

Rendre une image publique

  • On peut rendre une image publique en la poussant sur le Docker Hub.

  • La création d'un Docker ID est nécessaire.

  • Le Docker ID sera le nom d'utilisateur pour le Docker Hub.

  • La commande docker login permet de se connecter au Hub.

Pour pouvoir pousser une image sur le Hub, il faut la nommer avec un nom de la forme docker-id/image:tag

Ceci se fait avec la commande docker tag :

$ docker tag mon-image mon-docker-id/mon-image:1.0.0

Il est ensuite possible de pousser l'image avec la commande docker push :

$ docker push mon-docker-id/mon-image:1.0.0

A partir de là, l'image devient accessible publiquement par n'importe qui.

Dockerfile

les bonnes pratiques

Réutiliser au maximum la même image de base afin de mutualiser les couches entre vos différentes images applicatives.

Eviter d'installer tout ce qui n'est pas strictement nécessaire.

Paquets de la distribution, paquets applicatifs, ...

Alléger les images en supprimant les données inutiles, mais pas n'importe comment !

RUN apt-get update && apt-get install -y package
RUN rm -rf /var/lib/apt/lists/*

Chaque couche est commitée en readonly avant de passer à l'instruction suivante !

Il faut nettoyer dans la même couche !

RUN apt update \
  && apt install -y package \
  && rm -rf /var/lib/apt/lists/*
RUN wget archive.tar.gz \
  && tar xzvf archive.tar.gz \
  && rm archive.tar.gz

(Combiner plusieurs instructions RUN permet également de diminuer le nombre de couches constituant l'image.)

Ne pas utiliser apt-get upgrade ou apt-get dist-upgrade dans vos images spécialisées.

Ceci doit être fait dans l'image OS de base.

Chaque conteneur devrait se focaliser sur une seule tâche.

Un processus par conteneur

Utiliser une image de base légère.

Par exemple, debian:bullseye-slim pèse ~30MB tout en restant une distribution complète.

Proscrire l'utilisation du tag latest en production.

Attention !
docker pull myimage == docker pull myimage:latest

Le tag latest est utilisé par défaut si vous n'en fournissez pas un dans vos commandes Docker !

3 bonnes raisons de ne pas utiliser le tag latest

  • Votre outil de déploiement ne déploiera pas une nouvelle version de votre application taggué latest, tout simplement parce qu'il ne détectera pas la différence avec l'ancienne version elle aussi tagguée avec latest.

  • Tagguer une image avec un numéro de version unique permet d'effectuer de la tracabilité. On sait exactement quelle version est déployée.

  • En réutilisant systématiquement le tag latest, tout retour arrière à une ancienne version de l'image est impossible, car vous écrasez systématiquement la précédente version de l'image docker.

Déterminer une stratégie pour les tags

  • Lorsque on crée une image, il nous appartient de lui ajouter des tags appropriés.

  • Utiliser une stratégie pour les tags qui soit cohérente et consistante sur toutes les images produites.

  • La stratégie doit être facilement compréhensible par les utilisateurs de ces images.

L'exécution de processus avec l'utilisateur root à l'intérieur d'un conteneur est un préliminaire à de nombreux types d'attaques.

Déclarer un utilisateur dédié permet d'éviter un grand nombre d'attaques :

RUN useradd -d /home/my-app-user -m -s /bin/bash my-app-user
USER my-app-user

On peut forcer l'utilisateur à l'exécution :

$ docker run --user my-app-user -d -t my-application

L'utilisateur doit exister dans /etc/passwd à l'intérieur du conteneur.

Ne pas écrire de secrets dans le Dockerfile.
(secret == mot de passe, clé de chiffrement, clé d'API)

Injecter la configuration à l'exécution du conteneur.

Utiliser la fonctionnalité de multi-stage build (Docker > v17.05) pour produire des images plus petites.

Un cas d'usage est celui où l'on doit compiler une binaire afin de produire l'image applicative finale.

En découpant le processus de build, on va tout d'abord construire une image dédié à la compilation du binaire et injecter le résultat de cette compilation dans l'image finale qui sera ainsi d'une beaucoup plus réduite.

Dockerfile

FROM golang:1.10 as builder

WORKDIR /tmp/go
COPY hello.go ./
RUN CGO_ENABLED=0 go build -a -ldflags '-s' -o hello

FROM scratch
CMD [ "/hello" ]
COPY --from=builder /tmp/go/hello /hello

$ docker build -t hello:1 .

$ docker image ls

REPOSITORY                  TAG        IMAGE ID          CREATED            SIZE
hello                       latest     212f44bc4048      4 seconds ago      3.2MB
<none>                      <none>     08370cf772b1      5 seconds ago      693MB

Le résultat final est une image d'environ 3Mb au lieu de 700Mb sans le multi-stage build.

Pour le reste des bonnes pratiques :

Haskell Dockerfile Linter

Un outil pour vérifier les bonnes pratiques
sur les fichiers Dockerfile

$ docker run --rm -i hadolint/hadolint < Dockerfile
/dev/stdin:2 DL3020 Use COPY instead of ADD for files and folders
/dev/stdin:4 DL3025 Use arguments JSON notation for CMD and ENTRYPOINT arguments

https://github.com/hadolint/hadolint

Alternatives : Dockle | Trivy

Nettoyer les images

Les images ont tendance à s'accumuler et à remplir progressivement l'espace de stockage sur les hôtes.

  • $ docker image prune
    Supprime toutes les images qui ne sont ni taguées ni référencées par au moins un conteneur.

  • $ docker image prune -a
    Supprime toutes les images qui ne sont pas référencées par au moins un conteneur.

Registry

  • Le composant Docker Registry permet le stockage et la distribution d'images Docker.

  • Docker Inc. fourni une image officielle prête à l'emploi pour déployer facilement un Registry.

Fonctionnalités

  • Authentification des utilisateurs

  • Push/Pull d'images

  • Stockage des images sur une grande variété de backends (S3, Posix Filesystems, Ceph, Swift...)

### Utilisation du Docker Registry

$ docker pull hello-world
$ docker tag hello-world mon_nom_de_domaine:5000/hello-world
$ docker push mon_nom_de_domaine:5000/hello-world
...
$ docker pull mon_nom_de_domaine:5000/hello-world

Sécurité - rapports

Sécurité - outils

Outils alternatifs pour construire des Images compatibles OCI

Inspecter les couches d'une image

dive