Référence de Dockerfile
Docker peut construire des images automatiquement en lisant les instructions d'un Dockerfile. Un Dockerfile est un document texte qui contient toutes les commandes qu'un utilisateur peut appeler sur la ligne de commande pour assembler une image. Cette page décrit les commandes que vous pouvez utiliser dans un Dockerfile.
Vue d'ensemble
Le Dockerfile prend en charge les instructions suivantes :
Instruction | Description |
---|---|
ADD | Ajouter des fichiers et des répertoires locaux ou distants. |
ARG | Utiliser des variables au moment de la construction. |
CMD | Spécifier les commandes par défaut. |
COPY | Copier des fichiers et des répertoires. |
ENTRYPOINT | Spécifier l'exécutable par défaut. |
ENV | Définir des variables d'environnement. |
EXPOSE | Décrire sur quels ports votre application est à l'écoute. |
FROM | Créer une nouvelle étape de construction à partir d'une image de base. |
HEALTHCHECK | Vérifier la santé d'un conteneur au démarrage. |
LABEL | Ajouter des métadonnées à une image. |
MAINTAINER | Spécifier l'auteur d'une image. |
ONBUILD | Spécifier les instructions pour quand l'image est utilisée dans une construction. |
RUN | Exécuter les commandes de construction. |
SHELL | Définir le shell par défaut d'une image. |
STOPSIGNAL | Spécifier le signal d'appel système pour quitter un conteneur. |
USER | Définir l'ID utilisateur et groupe. |
VOLUME | Créer des montages de volumes. |
WORKDIR | Changer le répertoire de travail. |
ADD
L'instruction ADD
et COPY
ont essentiellement le même format et la même nature. Cependant, ADD
ajoute certaines fonctionnalités supplémentaires par-dessus COPY
.
Par exemple, le <chemin source>
peut être une URL
, dans ce cas, le moteur Docker essaiera de télécharger le fichier depuis ce lien et de le placer dans le <chemin cible>
. Les permissions du fichier téléchargé seront automatiquement définies à 600
. Si ce n'est pas la permission souhaitée, une couche RUN
supplémentaire est nécessaire pour ajuster les permissions. En outre, si le fichier téléchargé est une archive compressée, une couche RUN
supplémentaire est également nécessaire pour l'extraire. Par conséquent, il est plus raisonnable d'utiliser directement l'instruction RUN
, puis d'utiliser l'outil wget
ou curl
pour télécharger, gérer les permissions, extraire et ensuite nettoyer les fichiers inutiles. Ainsi, cette fonctionnalité n'est pas réellement pratique et n'est pas recommandée.
Si le <chemin source>
est un fichier compressé tar
, et que le format de compression est gzip
, bzip2
ou xz
, l'instruction ADD
décompressera automatiquement ce fichier compressé dans le <chemin cible>
.
Dans certains cas, cette fonctionnalité de décompression automatique est très utile, comme dans l'image officielle ubuntu
:
FROM scratch
ADD ubuntu-xenial-core-cloudimg-amd64-root.tar.gz /
...
Mais dans certains cas, si nous voulons vraiment copier un fichier compressé sans l'extraire, alors nous ne pouvons pas utiliser la commande ADD
.
Dans la documentation des meilleures pratiques de Dockerfile officielle de Docker, il est requis d'utiliser COPY
chaque fois que possible, car la sémantique de COPY
est très claire : cela copie juste des fichiers, tandis que ADD
inclut des fonctionnalités plus complexes et son comportement peut ne pas être aussi clair. Le cas d'utilisation le plus approprié pour ADD
est le besoin mentionné de décompression automatique.
De plus, il convient de noter que l'instruction ADD
provoquera l'invalidation du cache de construction d'image, ce qui peut ralentir le processus de construction d'image.
Par conséquent, lorsque vous choisissez entre les instructions COPY
et ADD
, vous pouvez suivre ce principe : utilisez l'instruction COPY
pour toutes les opérations de copie de fichiers, et utilisez ADD
uniquement lorsque la décompression automatique est nécessaire.
Lorsque vous utilisez cette instruction, vous pouvez également ajouter l'option --chown=<utilisateur>:<groupe>
pour changer le propriétaire utilisateur et groupe du fichier.
ADD --chown=55:mygroup files* /mydir/
ADD --chown=bin files* /mydir/
ADD --chown=1 files* /mydir/
ADD --chown=10:11 files* /mydir/
ARG
Format : ARG <nom_du_paramètre>[=<valeur_par_défaut>]
Les arguments de construction ont le même effet que ENV
, les deux définissent des variables d'environnement. La différence est que les variables d'environnement définies par ARG
pendant l'environnement de construction n'existeront pas lorsque le conteneur est en cours d'exécution. Cependant, n'utilisez pas ARG
pour stocker des mots de passe ou d'autres informations sensibles juste à cause de cela, car docker history
peut toujours afficher toutes les valeurs.
L'instruction ARG
dans le Dockerfile définit les noms de paramètres et leurs valeurs par défaut. Cette valeur par défaut peut être remplacée dans la commande docker build
avec --build-arg <nom_du_paramètre>=<valeur>
.
L'utilisation flexible de l'instruction ARG
vous permet de construire différentes images sans modifier le Dockerfile.
L'instruction ARG
a une portée d'effet. Si elle est spécifiée avant l'instruction FROM
, elle ne peut être utilisée que dans l'instruction FROM
.
ARG DOCKER_USERNAME=library
FROM ${DOCKER_USERNAME}/alpine
RUN set -x ; echo ${DOCKER_USERNAME}
En utilisant le Dockerfile ci-dessus, vous trouverez que la valeur de la variable ${DOCKER_USERNAME}
ne peut pas être sortie. Pour la sortir correctement, vous devez spécifier ARG
à nouveau après FROM
.
# Uniquement efficace dans FROM
ARG DOCKER_USERNAME=library
FROM ${DOCKER_USERNAME}/alpine
# Pour l'utiliser après FROM, vous devez le spécifier à nouveau
ARG DOCKER_USERNAME=library
RUN set -x ; echo ${DOCKER_USERNAME}
Pour les constructions à plusieurs étapes, faites particulièrement attention à ce problème.
# Cette variable est efficace dans chaque FROM
ARG DOCKER_USERNAME=library
FROM ${DOCKER_USERNAME}/alpine
RUN set -x ; echo 1
FROM ${DOCKER_USERNAME}/alpine
RUN set -x ; echo 2
Pour le Dockerfile ci-dessus, les deux instructions FROM
peuvent utiliser ${DOCKER_USERNAME}
. Pour les variables utilisées à chaque étape, elles doivent être spécifiées séparément à chaque étape :
ARG DOCKER_USERNAME=library
FROM ${DOCKER_USERNAME}/alpine
# Pour utiliser la variable après FROM, elle doit être spécifiée séparément à chaque étape
ARG DOCKER_USERNAME=library
RUN set -x ; echo ${DOCKER_USERNAME}
FROM ${DOCKER_USERNAME}/alpine
# Pour utiliser la variable après FROM, elle doit être spécifiée séparément à chaque étape
ARG DOCKER_USERNAME=library
RUN set -x ; echo ${DOCKER_USERNAME}
CMD
L'instruction CMD
a un format similaire à RUN
, avec deux formats :
- format
shell
:CMD <commande>
- format
exec
:CMD ["executable", "param1", "param2"...]
- Format de liste de paramètres :
CMD ["param1", "param2"...]
. Utilisé pour spécifier les paramètres réels après que l'instructionENTRYPOINT
a été spécifiée.
Lors de l'introduction des conteneurs, il a été mentionné que Docker n'est pas une machine virtuelle, et que les conteneurs sont des processus. Puisqu'il s'agit de processus, un programme et ses paramètres doivent être spécifiés lors du démarrage d'un conteneur. L'instruction CMD
est utilisée pour spécifier la commande de démarrage par défaut pour le processus principal du conteneur.
Pendant l'exécution, il est possible de spécifier une nouvelle commande pour remplacer cette commande par défaut définie dans l'image. Par exemple, la commande CMD
par défaut pour l'image ubuntu
est /bin/bash
. Si nous exécutons directement docker run -it ubuntu
, nous entrerons dans bash
. Nous pouvons également spécifier une commande différente à exécuter pendant l'exécution, comme docker run -it ubuntu cat /etc/os-release
. Cela remplace la commande par défaut /bin/bash
par cat /etc/os-release
, en sortant les informations de version du système.
En termes de format d'instruction, le format exec
est généralement recommandé, car il sera analysé comme un tableau JSON pendant l'analyse, donc les guillemets doubles "
doivent être utilisés, et non les guillemets simples.
Si le format shell
est utilisé, la commande réelle sera enveloppée comme un argument à sh -c
pour exécution. Par exemple :
CMD echo $HOME
Pendant l'exécution réelle, cela sera changé en :
CMD [ "sh", "-c", "echo $HOME" ]
C'est pourquoi nous pouvons utiliser des variables d'environnement, car ces variables seront analysées et traitées par le shell.
L'évocation de CMD
amène inévitablement la question de savoir si les applications dans le conteneur doivent s'exécuter en avant-plan ou en arrière-plan. C'est une confusion commune pour les débutants.
Docker n'est pas une machine virtuelle, et les applications dans les conteneurs devraient s'exécuter en avant-plan, pas comme dans les machines virtuelles ou les machines physiques où systemd
est utilisé pour démarrer des services en arrière-plan. Il n'y a pas de concept de services en arrière-plan à l'intérieur d'un conteneur.
Certains débutants écrivent la CMD
comme suit :
CMD service nginx start
Ensuite, ils constatent que le conteneur se ferme immédiatement après l'exécution. Même lors de l'utilisation de la commande systemctl
à l'intérieur du conteneur, ils constatent qu'elle ne peut pas être exécutée du tout. Cela est dû au fait qu'ils n'ont pas compris les concepts d'avant-plan et d'arrière-plan, et qu'ils n'ont pas fait la distinction entre conteneurs et machines virtuelles, essayant encore de comprendre les conteneurs du point de vue des machines virtuelles traditionnelles.
Pour un conteneur, son programme de démarrage est le processus d'application du conteneur. Le conteneur existe pour le processus principal, et lorsque le processus principal quitte, le conteneur perd son but et se ferme également. Les autres processus auxiliaires ne sont pas quelque chose dont il doit se préoccuper.
Utiliser la commande service nginx start
est une tentative pour faire démarrer le service nginx par le système d'initiation comme un processus daemon en arrière-plan. Comme mentionné précédemment, CMD service nginx start
sera interprété comme CMD [ "sh", "-c", "service nginx start"]
, donc le processus principal est en fait sh
. Lorsque la commande service nginx start
se termine, sh
se termine également, provoquant la fin du processus principal et la sortie naturelle du conteneur.
L'approche correcte est d'exécuter directement le fichier exécutable nginx
et de le requérir pour s'exécuter en avant-plan. Par exemple :
CMD ["nginx", "-g", "daemon off;"]
COPY
COPY [--chown=<utilisateur>:<groupe>] <chemin_source>... <chemin_destination>
COPY [--chown=<utilisateur>:<groupe>] ["<chemin_source1>",... "<chemin_destination>"]
Semblable à l'instruction `RUN`, il existe deux formats, un semblable à la ligne de commande, et un semblable à un appel de fonction.
L'instruction `COPY` copie des fichiers/dossiers depuis le `<chemin_source>` dans le contexte de construction vers l'emplacement `<chemin_destination>` dans la nouvelle couche de l'image. Par exemple :
```docker
COPY package.json /usr/src/app/
<chemin_source>
peut être de multiples chemins, et peut même être un joker, où les règles du joker doivent satisfaire les règles de filepath.Match
de Go, tel que :
COPY hom* /mydir/
COPY hom?.txt /mydir/
<chemin_destination>
peut être un chemin absolu à l'intérieur du conteneur, ou un chemin relatif par rapport au répertoire de travail (le répertoire de travail peut être spécifié en utilisant l'instruction WORKDIR
). Le chemin de destination n'a pas besoin d'être créé au préalable. Si le répertoire n'existe pas, il sera créé avant la copie des fichiers.
De plus, il convient de noter que lors de l'utilisation de l'instruction COPY
, divers métadonnées des fichiers sources seront préservées, tels que les permissions de lecture, écriture et exécution, et les temps de modification des fichiers. Cette fonctionnalité est utile pour la personnalisation de l'image, en particulier lorsque tous les fichiers pertinents sont gérés en utilisant Git.
Lors de l'utilisation de cette instruction, vous pouvez également ajouter l'option --chown=<utilisateur>:<groupe>
pour changer le propriétaire et le groupe des fichiers.
COPY --chown=55:mygroup files* /mydir/
COPY --chown=bin files* /mydir/
COPY --chown=1 files* /mydir/
COPY --chown=10:11 files* /mydir/
Si le chemin source est un dossier, lors de la copie, il ne copie pas directement le dossier, mais copie le contenu du dossier vers le chemin de destination.
ENTRYPOINT
Le format de ENTRYPOINT
est le même que celui de l'instruction RUN
, divisé en format exec
et format shell
.
Le but de ENTRYPOINT
est le même que CMD
, les deux sont utilisés pour spécifier le programme de démarrage du conteneur et ses paramètres. ENTRYPOINT
peut également être remplacé au moment de l'exécution, mais c'est un peu plus encombrant que CMD
, et nécessite l'utilisation du paramètre --entrypoint
de docker run
pour le spécifier.
Une fois ENTRYPOINT
spécifié, la signification de CMD
change. Il n'exécute plus directement sa commande, mais traite plutôt le contenu de CMD
comme des arguments passés à l'instruction ENTRYPOINT
. En d'autres termes, lorsqu'exécuté, il deviendra :
<ENTRYPOINT> "<CMD>"
Alors avec CMD
, pourquoi avons-nous besoin de ENTRYPOINT
? Quels sont les avantages de ce <ENTRYPOINT> "<CMD>"
? Voyons quelques scénarios.
Scénario 1 : Faire en sorte que l'image se comporte comme une commande
Supposons que nous avons besoin d'une image pour connaître notre adresse IP publique actuelle, nous pouvons d'abord l'implémenter avec CMD
:
FROM ubuntu:18.04
RUN apt-get update \
&& apt-get install -y curl \
&& rm -rf /var/lib/apt/lists/*
CMD [ "curl", "-s", "http://myip.ipip.net" ]
Si nous utilisons docker build -t myip .
pour construire l'image, si nous avons besoin de consulter l'IP publique actuelle, nous avons seulement besoin d'exécuter :
$ docker run myip
IP actuelle : 8.8.8.8 depuis la Californie, États-Unis
Eh bien, il semble que nous pouvons utiliser l'image comme une commande maintenant, mais les commandes ont généralement des arguments. Que se passe-t-il si nous voulons ajouter des arguments ? D'après le CMD
ci-dessus, nous pouvons voir que la commande réelle est curl
, donc si nous voulons afficher les informations de l'en-tête HTTP, nous devons ajouter le paramètre -i
. Pouvons-nous directement ajouter le paramètre -i
à docker run myip
?
$ docker run myip -i
docker: Error response from daemon: invalid header field value "oci runtime error: container_linux.go:247: starting container process caused \"exec: \\\"-i\\\": executable file not found in $PATH\"\n".
Nous pouvons voir une erreur indiquant que le fichier exécutable n'est pas trouvé, executable file not found
. Comme mentionné précédemment, ce qui vient après le nom de l'image est la commande
, qui remplacera la valeur par défaut de CMD
au moment de l'exécution. Par conséquent, ici -i
remplace le CMD
original au lieu d'être ajouté à la fin du curl -s original http://myip.ipip.net
. Cependant, -i
n'est pas du tout une commande, donc naturellement il ne peut pas être trouvé.
Donc si nous voulons ajouter le paramètre -i
, nous devons réentrer la commande complète :
$ docker run myip curl -s http://myip.ipip.net -i
Ceci est évidemment pas une bonne solution, mais utiliser ENTRYPOINT
peut résoudre ce problème. Maintenant nous réimplémentons cette image en utilisant ENTRYPOINT
:
FROM ubuntu:18.04
RUN apt-get update \
&& apt-get install -y curl \
&& rm -rf /var/lib/apt/lists/*
ENTRYPOINT [ "curl", "-s", "http://myip.ipip.net" ]
Maintenant essayons d'utiliser docker run myip -i
à nouveau :
$ docker run myip
IP actuelle : 8.8.8.8 depuis : la Californie, États-Unis
$ docker run myip -i
HTTP/1.1 200 OK
Server: nginx/1.8.0
Date: Tue, 22 Nov 2016 05:12:40 GMT
Content-Type: text/html; charset=UTF-8
Vary: Accept-Encoding
X-Powered-By: PHP/5.6.24-1~dotdeb+7.1
X-Cache: MISS from cache-2
X-Cache-Lookup: MISS from cache-2:80
X-Cache: MISS from proxy-2_6
Transfer-Encoding: chunked
Via: 1.1 cache-2:80, 1.1 proxy-2_6:8006
Connection: keep-alive
IP actuelle : 8.8.8.8 depuis : la Californie, États-Unis
Nous pouvons voir que ça a fonctionné cette fois. Cela est dû au fait que lorsque ENTRYPOINT
existe, le contenu de CMD
sera passé comme arguments à ENTRYPOINT
, et ici -i
est le nouveau CMD
, donc il sera passé comme un argument à curl
, atteignant l'effet désiré.
Sc énario 2 : Travail de préparation avant le démarrage de l'application
Démarrer un conteneur, c'est démarrer le processus principal, mais parfois, un travail de préparation est nécessaire avant de démarrer le processus principal.
Par exemple, pour des types de bases de données comme mysql
, il peut être nécessaire de réaliser un travail de configuration et d'initialisation de la base de données, et ces tâches doivent être résolues avant que le serveur mysql final ne s'exécute.
De plus, vous pourriez vouloir éviter de démarrer le service en tant qu'utilisateur root
, améliorant ainsi la sécurité, mais avant de démarrer le service, il est encore nécessaire d'effectuer un travail de préparation nécessaire en tant qu'utilisateur root
, et enfin passer à l'identité de l'utilisateur du service pour démarrer le service. Alternativement, les commandes autres que le service peuvent encore être exécutées en tant qu'utilisateur root
pour la commodité comme le débogage.
Ces tâches de préparation ne sont pas liées au CMD
du conteneur, et doivent être effectuées en tant qu'étape de prétraitement quel que soit le CMD
. Dans ce cas, vous pouvez écrire un script et le mettre dans ENTRYPOINT
pour exécuter, et ce script prendra les arguments reçus (c.-à-d., <CMD>
) comme la commande à exécuter à la fin. Par exemple, l'image officielle de redis
fait cela :
FROM alpine:3.4
...
RUN addgroup -S redis && adduser -S -G redis redis
...
ENTRYPOINT ["docker-entrypoint.sh"]
EXPOSE 6379
CMD [ "redis-server" ]
Vous pouvez voir qu'il crée l'utilisateur redis
pour le service redis
, et finalement spécifie l'ENTRYPOINT
comme le script docker-entrypoint.sh
.
#!/bin/sh
# permettre au conteneur de démarrer avec `--user`
if [ "$1" = 'redis-server' -a "$(id -u)" = '0' ]; then
find . \! -user redis -exec chown redis '{}' +
exec gosu redis "$0" "$@"
fi
exec "$@"
Le contenu de ce script est de juger sur la base du contenu de CMD
. S'il s'agit de redis-server
, il passera à l'identité de l'utilisateur redis
pour démarrer le serveur. Sinon, il continuera de fonctionner en tant qu'utilisateur root
. Par exemple :
$ docker run -it redis id
uid=0(root) gid=0(root) groupes=0(root)
ENV
Il y a deux formats :
ENV <clé> <valeur>
ENV <clé1>=<valeur1> <clé2>=<valeur2>...
Cette instruction est simple, il suffit de définir des variables d'environnement. Les instructions subséquentes telles que RUN
, ou les applications fonctionnant au moment de l'exécution, peuvent utiliser directement les variables d'environnement définies ici.
ENV VERSION=1.0 DEBUG=on \
NAME="Happy Feet"
Cet exemple montre comment casser des lignes, et comment utiliser des guillemets doubles pour inclure des valeurs contenant des espaces, ce qui est cohérent avec le comportement dans le Shell.
Une fois les variables d'environnement définies, elles peuvent être utilisées dans les instructions suivantes. Par exemple, dans le Dockerfile
de l'image officielle node
, il y a un code comme celui-ci :
ENV NODE_VERSION 7.2.0
RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.xz" \
&& curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc" \
&& gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \
&& grep " node-v$NODE_VERSION-linux-x64.tar.xz\$" SHASUMS256.txt | sha256sum -c - \
&& tar -xJf "node-v$NODE_VERSION-linux-x64.tar.xz" -C /usr/local --strip-components=1 \
&& rm "node-v$NODE_VERSION-linux-x64.tar.xz" SHASUMS256.txt.asc SHASUMS256.txt \
&& ln -s /usr/local/bin/node /usr/local/bin/nodejs
Les instructions suivantes prennent en charge l'expansion des variables d'environnement : ADD, COPY, ENV, EXPOSE, FROM, LABEL, USER, WORKDIR, VOLUME, STOPSIGNAL, ONBUILD, RUN.
Vous pouvez sentir à partir de cette liste d'instructions que les variables d'environnement peuvent être utilisées à de nombreux endroits, ce qui est très puissant. Avec les variables d'environnement, nous pouvons créer plus d'images à partir d'un seul Dockerfile, simplement en utilisant différentes variables d'environnement.
EXPOSE
Le format est EXPOSE <port1> [<port2>...]
.
L'instruction EXPOSE
déclare les ports que le conteneur fournira des services lors de l'exécution. Il s'agit simplement d'une déclaration, et l'application n'ouvrira pas réellement ces ports lorsque le conteneur est en cours d'exécution en raison de cette déclaration. Écrire cette déclaration dans le Dockerfile a deux avantages : l'un est d'aider l'utilisateur de l'image à comprendre les ports de démon de ce service d'image, ce qui est pratique pour configurer la cartographie des ports ; l'autre est que lors de l'utilisation de la cartographie des ports aléatoires au moment de l'exécution, c'est-à-dire docker run -P
, cela cartographiera automatiquement aléatoirement les ports exposés par EXPOSE
.
L'instruction EXPOSE
doit être distinguée de l'utilisation de -p <port hôte>:<port conteneur>
au moment de l'exécution. -p
mappe le port hôte et le port conteneur, en d'autres termes, il expose le service de port correspondant du conteneur à l'accès externe, tandis que EXPOSE
déclare seulement quels ports le conteneur a l'intention d'utiliser, et ne réalise pas automatiquement la cartographie des ports sur l'hôte.
FROM
Dans un Dockerfile, l'instruction FROM
spécifie l'image de base, qui est le point de départ pour construire une nouvelle image. C'est la première instruction dans le Dockerfile, utilisée pour définir l'environnement de base pour le processus de construction.
Utilisations courantes de l'instruction FROM
-
Construction à partir d'une image officielle :
FROM image:tag
Cette utilisation sp écifie une image officielle existante comme image de base.
image
est le nom de l'image, ettag
est le tag de version. Par exemple, vous pouvez utiliserubuntu:18.04
comme image de base. -
Construction à partir d'une image personnalisée :
FROM <username>/<imagename>:<tag>
Cette utilisation spécifie une image personnalisée comme image de base.
<username>
est le nom d'utilisateur sur Docker Hub,<imagename>
est le nom de l'image, et<tag>
est le tag de version. -
Construction multi-étapes :
FROM <base-image> AS <stage-name>
Cette utilisation permet de définir plusieurs étapes de construction dans un seul Dockerfile et d'utiliser différentes images de base pour chaque étape.
<base-image>
est l'image de base, et<stage-name>
est le nom de l'étape.Les constructions multi-étapes sont généralement utilisées pour utiliser différents outils et dépendances pendant le processus de construction, puis copier les fichiers ou exécutables nécessaires d'une étape à l'autre, réduisant ainsi la taille de l'image finale.
-
Construction à partir du système de fichiers local :
FROM scratch
Cette utilisation indique que la construction démarre à partir d'une image vide, sans utiliser aucune image de base existante. Dans ce cas, vous devez ajouter vous-même les fichiers et configurations requis.
L'instruction FROM
ne peut apparaître qu'une fois dans un Dockerfile et doit être la première instruction. Elle définit le point de départ pour le processus de construction, et les instructions subséquentes seront basées sur ce point de départ.
HEALTHCHECK
Format :
HEALTHCHECK [options] CMD <commande>
: Définit la commande pour vérifier l'état de santé du conteneur.HEALTHCHECK NONE
: Si l'image de base a une instruction de vérification de santé, utilisez cette ligne pour la remplacer.
L'instruction HEALTHCHECK
indique à Docker comment déterminer si l'état du conteneur est normal. Cette instruction a été introduite dans Docker 1.12.
Avant l'instruction HEALTHCHECK
, le moteur Docker ne pouvait déterminer si un conteneur était dans un état anormal qu'en vérifiant si le processus principal à l'intérieur du conteneur s'était terminé. Dans de nombreux cas, cela ne posait pas de problème, mais si le programme entrait dans un état de blocage ou de boucle infinie, le processus d'application ne se terminait pas, mais le conteneur ne pouvait plus fournir de services. Avant le 1.12, Docker ne détectait pas un tel état dans le conteneur et ne replanifiait pas, ce qui résultait en des conteneurs qui ne pouvaient plus fournir de services mais acceptaient encore les requêtes des utilisateurs.
Depuis le 1.12, Docker a fourni l'instruction HEALTHCHECK
, qui spécifie une commande pour déterminer si l'état du service du processus principal est toujours normal, reflétant ainsi plus précisément l'état réel du conteneur.
Lorsqu'une instruction HEALTHCHECK
est spécifiée dans une image et qu'un conteneur est démarré à partir de celle-ci, l'état initial sera starting
. Après que l'instruction HEALTHCHECK
passe, l'état change en healthy
. Si la vérification échoue consécutivement un certain nombre de fois, l'état change en unhealthy
.
HEALTHCHECK
prend en charge les options suivantes :
--interval=<intervalle>
: L'intervalle entre deux vérifications de santé, par défaut 30 secondes.--timeout=<durée>
: Le délai d'exécution de la commande de vérification de santé. Si ce temps est dépassé, la vérification de santé en cours est considérée comme un échec, par défaut 30 secondes.--retries=<nombre>
: Après avoir échoué consécutivement le nombre de fois spécifié, l'état du conteneur est considéré commeunhealthy
, par défaut 3 fois.
Comme pour CMD
et ENTRYPOINT
, HEALTHCHECK
ne peut apparaître qu'une seule fois. Si plusieurs instances sont écrites, seule la dernière prend effet.
La commande après HEALTHCHECK [options] CMD
a le même format que ENTRYPOINT
, avec les formats shell
et exec
. La valeur de retour de la commande détermine le succès ou l'échec de la vérification de santé : 0
: succès ; 1
: échec ; 2
: réservé, ne pas utiliser cette valeur.
Supposons que nous ayons une image qui est un service Web simple, et que nous voulons ajouter une vérification de santé pour déterminer si son service Web fonctionne correctement. Nous pouvons utiliser curl
pour aider à la détermination. Le HEALTHCHECK
dans le Dockerfile
peut être écrit comme ceci :
FROM nginx
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
HEALTHCHECK --interval=5s --timeout=3s \
CMD curl -fs http://localhost/ || exit 1
Ici, nous réglons la vérification pour qu'elle s'exécute toutes les 5 secondes (cet intervalle est très court à des fins de test et devrait être relativement plus long en pratique), et si la commande de vérification de santé ne répond pas dans les 3 secondes, elle est considérée comme un échec. Nous utilisons curl -fs http://localhost/ || exit 1
comme commande de vérification de santé.
Utilisez docker build
pour construire cette image :
$ docker build -t myweb:v1 .
Après la construction, nous démarrons un conteneur :
$ docker run -d --name web -p 80:80 myweb:v1
Après avoir exécuté cette image, vous pouvez voir l'état initial (health: starting)
en utilisant docker container ls
:
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
03e28eb00bd0 myweb:v1 "nginx -g 'daemon off" 3 seconds ago Up 2 seconds (health: starting) 80/tcp, 443/tcp web
Après avoir attendu quelques secondes et exécuté docker container ls
à nouveau, l'état de santé aura changé en (healthy)
:
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
03e28eb00bd0 myweb:v1 "nginx -g 'daemon off" 18 seconds ago Up 16 seconds (healthy) 80/tcp, 443/tcp web
Si la vérification de santé échoue consécutivement plus de fois que le nombre de tentatives, l'état changera en (unhealthy)
.
Pour aider au dépannage, la sortie de la commande de vérification de santé (y compris stdout
et stderr
) est stockée dans l'état de santé et peut être visualisée en utilisant docker inspect
.
$ docker inspect --format '{{json .State.Health}}' web | python -m json.tool
{
"FailingStreak": 0,
"Log": [
{
"End": "2016-11-25T14:35:37.940957051Z",
"ExitCode": 0,
"Output": "<!DOCTYPE html>\n<html>\n<head>\n<title>Bienvenue sur nginx!</title>\n<style>\n body {\n width: 35em;\n margin: 0 auto;\n font-family: Tahoma, Verdana, Arial, sans-serif;\n }\n</style>\n</head>\n<body>\n<h1>Bienvenue sur nginx!</h1>\n<p>Si vous voyez cette page, le serveur web nginx est installé avec succès et\nfonctionne. Une configuration supplémentaire est requise.</p>\n\n<p>Pour la documentation en ligne et le support, veuillez vous référer à\n<a href=\"http://nginx.org/\">nginx.org</a>.<br/>\nLe support commercial est disponible à\n<a href=\"http://nginx.com/\">nginx.com</a>.</p>\n\n<p><em>Merci d'utiliser nginx.</em></p>\n</body>\n</html>\n",
"Start": "2016-11-25T14:35:37.780192565Z"
}
],
"Status": "healthy"
}
LABEL
L'instruction LABEL
est utilisée pour ajouter des métadonnées sous forme de paires clé-valeur à une image.
LABEL <clé>=<valeur> <clé>=<valeur> <clé>=<valeur> ...
Nous pouvons également utiliser certains labels pour déclarer l'auteur de l'image, l'adresse de la documentation, etc. :
LABEL org.opencontainers.image.authors="yeasy"
LABEL org.opencontainers.image.documentation="https://www.ubitools.com"
RUN
Dans un Dockerfile, l'instruction RUN
est utilisée pour exécuter des commandes à l'intérieur du conteneur. Elle peut exécuter toute commande valide et script shell.
Utilisations courantes de l'instruction RUN
-
Exécuter une commande unique :
RUN <commande>
Dans cette utilisation,
<commande>
est la seule commande à être exécutée à l'intérieur du conteneur. Par exemple :RUN apt-get update
RUN apt-get install -y packageCela exécutera les commandes pour mettre à jour les listes de paquets et installer un paquet à l'intérieur du conteneur respectivement.
-
Exécuter plusieurs commandes :
RUN <commande1> && <commande2>
Cette utilisation permet d'exécuter plusieurs commandes consécutivement sur une ligne, en utilisant l'opérateur
&&
pour s'assurer que chaque commande réussit avant d'exécuter la suivante. Par exemple :RUN apt-get update && apt-get install -y package
Cela exécutera les commandes pour mettre à jour les listes de paquets et installer un paquet à l'intérieur du conteneur séquentiellement, en s'assurant que la commande précédente réussit avant d'exécuter la suivante.
-
Exécuter un script shell :
RUN /bin/bash -c "<script>"
Cette utilisation permet d'exécuter des scripts shell complexes à l'intérieur du conteneur. Le script est placé à l'intérieur de guillemets, et
/bin/bash -c
est utilisé pour spécifier que Bash doit interpréter et exécuter le script. Par exemple :RUN /bin/bash -c "source setup.sh && build.sh"
Cela exécutera les scripts
setup.sh
etbuild.sh
à l'intérieur du conteneur.
L'instruction RUN
peut être utilisée plusieurs fois, et chaque instruction exécutera une commande à l'intérieur du conteneur. Chaque instruction RUN
créera une nouvelle couche d'image au-dessus de la précédente.
Notes :
- Les commandes exécutées dans une instruction
RUN
affecteront de manière permanente le conteneur, donc des commandes de nettoyage devraient être incluses pour éviter les fichiers et données inutiles dans l'image. - Si vous devez utiliser des variables d'environnement dans une instruction
RUN
, vous pouvez les définir dans le Dockerfile à l'aide de l'instructionENV
.
SHELL
Format : SHELL ["exécutable", "paramètres"]
La commande SHELL
permet de spécifier l’interpréteur de commandes utilisé pour les commandes RUN
, ENTRYPOINT
et CMD
. La valeur par défaut sous Linux est ["/bin/sh", "-c"]
.
SHELL ["/bin/sh", "-c"]
RUN ls ; ls
SHELL ["/bin/sh", "-cex"]
RUN ls ; ls
Les deux instructions RUN
exécutent la même commande, mais la seconde instruction RUN
affichera chaque commande et se terminera en cas d’erreur.
Lorsque ENTRYPOINT
et CMD
sont spécifiés au format shell, l’interpréteur de commandes défini par SHELL
deviendra également l’interpréteur pour ces deux instructions.
SHELL ["/bin/sh", "-cex"]
# /bin/sh -cex "nginx"
ENTRYPOINT nginx
SHELL ["/bin/sh", "-cex"]
# /bin/sh -cex "nginx"
CMD nginx
STOPSIGNAL
L'instruction STOPSIGNAL
est utilisée pour définir le signal d'appel système à utiliser lors de l'envoi d'un signal d'arrêt au conteneur. Cette instruction accepte la valeur du signal ou le nom correspondant du signal en argument.
Syntaxe :
STOPSIGNAL signal
Où :
signal
peut être la valeur numérique ou le nom du signal, tel queSIGKILL
.
Si l'instruction STOPSIGNAL
n'est pas spécifiée, le signal SIGTERM
sera envoyé lors de l'utilisation de la commande docker stop
. Si le conteneur ne s'arrête pas dans le délai spécifié, le signal SIGKILL
sera envoyé pour terminer de force le conteneur.
Exemple 1 :
FROM ubuntu:18.04
STOPSIGNAL SIGTERM
CMD ["/usr/bin/bash", "-c", "while true; do sleep 1; done"]
Dans l'exemple ci-dessus, nous avons configuré le signal SIGTERM
pour être envoyé comme signal d'arrêt. Lorsque la commande docker stop
est exécutée, le conteneur recevra d'abord le signal SIGTERM
, et si celui-ci ne s'arrête pas normalement dans le temps imparti, le signal SIGKILL
sera envoyé pour terminer le conteneur.
Exemple 2 :
FROM ubuntu:18.04
STOPSIGNAL 9
CMD ["/usr/bin/bash", "-c", "while true; do sleep 1; done"]
Dans cet exemple, nous utilisons la valeur du signal 9
pour spécifier l'envoi du signal SIGKILL
, qui terminera directement le processus du conteneur sans attendre un arrêt normal.
Notez que même si STOPSIGNAL
est défini, Docker peut encore envoyer le signal SIGKILL
pour terminer le conteneur dans certaines situations, telles que lorsque le conteneur est dans un état irrécupérable.
USER
Format : USER <nomutilisateur>[:<groupe>]
L'instruction USER
est similaire à WORKDIR
, car elles modifient toutes les deux l'état de l'environnement et affectent les couches suivantes. WORKDIR
change le répertoire de travail, tandis que USER
change l'identité de l'utilisateur pour exécuter les commandes RUN
, CMD
et ENTRYPOINT
suivantes.
Notez que USER
vous aide uniquement à passer à l'utilisateur spécifié ; cet utilisateur doit être préalablement créé, sinon, il ne pourra pas être utilisé.
RUN groupadd -r redis && useradd -r -g redis redis
USER redis
RUN [ "redis-server" ]
Si un script est exécuté en tant que root
mais que vous souhaitez changer l'identité pendant l'exécution, comme l'exécution d'un processus de service en tant qu'utilisateur précréé, n'utilisez pas su
ou sudo
, car ils nécessitent une configuration compliquée et échouent souvent dans des environnements sans TTY. Il est recommandé d'utiliser gosu
.
# Créer l'utilisateur redis, et utiliser gosu pour passer à un autre utilisateur pour exécuter les commandes
RUN groupadd -r redis && useradd -r -g redis redis
# Télécharger gosu
RUN wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/1.12/gosu-amd64" \
&& chmod +x /usr/local/bin/gosu \
&& gosu nobody true
# Définir CMD, et l'exécuter en tant qu'un autre utilisateur
CMD [ "exec", "gosu", "redis", "redis-server" ]
VOLUME
Format :
VOLUME ["<chemin1>", "<chemin2>"...]
VOLUME <chemin>
Comme mentionné précédemment, les couches de stockage des conteneurs doivent rester en lecture seule pendant l'exécution autant que possible. Pour les applications qui ont besoin de stocker des données dynamiques, comme les bases de données, leurs fichiers de base de données doivent être stockés dans des volumes. Nous présenterons plus en détail le concept de volumes Docker dans les chapitres suivants. Afin d'empêcher les utilisateurs d'oublier de monter des répertoires pour des données dynamiques en tant que volumes pendant l'exécution, nous pouvons spécifier certains répertoires à être montés comme volumes anonymes dans le Dockerfile
. De cette façon, même si l'utilisateur ne spécifie pas de montage, l'application peut toujours fonctionner normalement sans écrire une grande quantité de données dans la couche de stockage du conteneur.
VOLUME /data
Dans cet exemple, le répertoire /data
sera automatiquement monté comme un volume anonyme pendant l'exécution du conteneur. Toutes les données écrites dans /data
ne seront pas enregistrées dans la couche de stockage du conteneur, assurant la nature sans état de la couche de stockage du conteneur. Bien sûr, ce paramètre de montage peut être outrepassé lors de l'exécution du conteneur. Par exemple :
$ docker run -d -v mydata:/data xxxx
Dans cette commande, le volume nommé mydata
est monté à l'emplacement /data
, outrepassant la configuration de montage de volume anonyme définie dans le Dockerfile
.
WORKDIR
Format : WORKDIR <chemin du répertoire de travail>
L'instruction WORKDIR
peut être utilisée pour spécifier le répertoire de travail (ou répertoire courant). Après cette instruction, le répertoire courant pour les couches subséquentes sera défini au répertoire spécifié. Si le répertoire n'existe pas, WORKDIR
le créera pour vous.
Auparavant, nous avons mentionné une erreur commune faite par les débutants consistant à traiter le Dockerfile
comme un script shell. Ce malentendu pourrait également conduire à l'erreur suivante :
RUN cd /app
RUN echo "hello" > world.txt
Si vous construisez une image à partir de ce Dockerfile
et l'exécutez, vous constaterez que le fichier /app/world.txt
n'est pas trouvé, ou son contenu n'est pas hello
. La raison est simple : dans le shell, deux lignes consécutives font partie du même environnement d'exécution de processus, donc l'état de mémoire modifié par la commande précédente affectera directement la commande suivante. Cependant, dans un Dockerfile
, ces deux commandes RUN
sont exécutées dans des environnements de conteneurs complètement différents, qui sont deux conteneurs entièrement séparés. C'est une erreur causée par un manque de compréhension du concept de stockage en couches dans les constructions de Dockerfile
.
Comme mentionné précédemment, chaque RUN
démarre un nouveau conteneur, exécute la commande, puis valide les changements de fichiers. La première couche RUN cd /app
ne change que le répertoire de travail du processus actuel, qui est simplement un changement en mémoire et ne résulte en aucun changement de fichier. D'ici la seconde couche, un tout nouveau conteneur est démarré, complètement sans rapport avec le conteneur de la première couche, donc il ne peut pas hériter des changements en mémoire du processus de construction précédent.
Par conséquent, si vous avez besoin de changer l'emplacement du répertoire de travail pour les couches subséquentes, vous devriez utiliser l'instruction WORKDIR
.
WORKDIR /app
RUN echo "hello" > world.txt
Si vous utilisez un chemin relatif dans votre instruction WORKDIR
, le répertoire vers lequel vous changez est relatif au WORKDIR
précédent :
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
Le répertoire de travail pour RUN pwd
sera /a/b/c
.