web-dev-qa-db-fra.com

Comment exécuter une commande Shell avant le ENTRYPOINT via le dockerfile

J'ai le fichier suivant pour mon projet nodejs

FROM node:boron

# Create app directory
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app

# Install app dependencies
COPY package.json /usr/src/app/
RUN npm install



# Bundle app source
COPY . /usr/src/app

# Replace with env variable
RUN envsubs < fil1 > file2

EXPOSE 8080
CMD [ "npm", "start" ]

J'exécute le conteneur docker avec l'indicateur -e fournissant la variable d'environnement

Mais je ne vois pas le remplaçant. La commande Exécuter ccommand sera-t-elle exécutée lorsque la variable env sera disponible?

11
user_mda

Les images sont immuables

Dockerfile définit le processus de construction d'une image. Une fois construite, l'image est immuable (ne peut pas être modifiée). Les variables d'exécution ne sont pas quelque chose qui serait intégré à cette image immuable. Dockerfile n'est donc pas le bon endroit pour résoudre ce problème.

Utilisation d'un script de point d'entrée

Ce que vous voulez probablement faire, c'est remplacer le ENTRYPOINT par défaut par votre propre script, et faire en sorte que ce script fasse quelque chose avec les variables d'environnement. Étant donné que le script de point d'entrée s'exécuterait au moment de l'exécution (lorsque le conteneur démarre), c'est le bon moment pour rassembler les variables d'environnement et faire quelque chose avec elles.

Tout d'abord, vous devez ajuster votre Dockerfile pour connaître un script de point d'entrée. Bien que Dockerfile ne soit pas directement impliqué dans la gestion de la variable d'environnement, il doit tout de même connaître ce script, car le script sera intégré à votre image.

Dockerfile:

COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

ENTRYPOINT ["/entrypoint.sh"]
CMD ["npm", "start"]

Maintenant, écrivez un script de point d'entrée qui fait la configuration nécessaire avant l'exécution de la commande, et à la fin, exec la commande elle-même .

entrypoint.sh:

#!/bin/sh

# Where $ENVSUBS is whatever command you are looking to run
$ENVSUBS < fil1 > file2

npm install

# This will exec the CMD from your Dockerfile, i.e. "npm start"
exec "$@"

Ici, j'ai inclus npm install, puisque vous avez posé des questions à ce sujet dans les commentaires. Je noterai que cela fonctionnera npm install à chaque exécution . Si cela est approprié, très bien, mais je voulais souligner qu'il s'exécutera à chaque fois, ce qui ajoutera une certaine latence à votre temps de démarrage.

Reconstruisez maintenant votre image, de sorte que le script de point d'entrée en fasse partie.

Utilisation de variables d'environnement lors de l'exécution

Le script de point d'entrée sait comment utiliser la variable d'environnement, mais vous devez toujours dire à Docker d'importer la variable au moment de l'exécution. Vous pouvez utiliser le -e drapeau à docker run faire cela.

docker run -e "ENVSUBS=$ENVSUBS" <image_name>

Ici, Docker est invité à définir une variable d'environnement ENVSUBS, et la valeur qui lui est attribuée est la valeur de $ENVSUBS à partir de l'environnement Shell actuel.

Fonctionnement des scripts de point d'entrée

Je vais m'étendre un peu là-dessus, car dans les commentaires, il semblait que vous étiez un peu brumeux sur la façon dont cela s'articule.

Lorsque Docker démarre un conteneur, il exécute une (et une seule) commande à l'intérieur du conteneur. Cette commande devient PID 1, tout comme init ou systemd sur un système Linux typique. Ce processus est responsable de l'exécution de tout autre processus dont le conteneur a besoin.

Par défaut, le ENTRYPOINT est /bin/sh -c. Vous pouvez le remplacer dans Dockerfile, ou docker-compose.yml, ou en utilisant la commande docker.

Lorsqu'un conteneur est démarré, Docker exécute la commande entrypoint et lui passe la commande (CMD) en tant que liste d'arguments. Plus tôt, nous avons défini notre propre ENTRYPOINT comme /entrypoint.sh. Cela signifie que dans votre cas, c'est ce que Docker exécutera dans le conteneur au démarrage:

/entrypoint.sh npm start

Car ["npm", "start"] a été défini comme la commande, c'est ce qui est passé en tant que liste d'arguments au script d'entrée.

Parce que nous avons défini une variable d'environnement en utilisant le -e flag, ce script de point d'entrée (et ses enfants) aura accès à cette variable d'environnement.

À la fin du script de point d'entrée, nous exécutons exec "$@". Car $@ se développe dans la liste d'arguments passée au script, cela s'exécutera

exec npm start

Et parce que exec exécute ses arguments comme une commande, remplaçant le processus en cours par lui-même, lorsque vous avez terminé, npm start devient PID 1 dans votre conteneur.

Pourquoi vous ne pouvez pas utiliser plusieurs CMD

Dans les commentaires, vous avez demandé si vous pouvez définir plusieurs entrées CMD pour exécuter plusieurs choses.

Vous ne pouvez définir qu'un ENTRYPOINT et un CMD. Ceux-ci ne sont pas du tout utilisés pendant le processus de génération. Contrairement à RUN et COPY, ils ne sont pas exécutés lors de la génération. Ils sont ajoutés en tant qu'éléments de métadonnées à l'image une fois qu'elle est créée.

Ce n'est que plus tard, lorsque l'image est exécutée en tant que conteneur, que ces champs de métadonnées sont lus et utilisés pour démarrer le conteneur.

Comme mentionné précédemment, le point d'entrée est ce qui est réellement exécuté et le CMD est passé sous forme de liste d'arguments. La raison pour laquelle ils sont séparés est en partie historique. Dans les premières versions de Docker, CMD était la seule option disponible et ENTRYPOINT était corrigé comme étant /bin/sh -c. Mais en raison de situations comme celle-ci, Docker a finalement permis à ENTRYPOINT d'être défini par l'utilisateur.

38
Dan Lowe

La commande Exécuter ccommand sera-t-elle exécutée lorsque la variable env sera disponible?

Variables d'environnement définies avec -e L'indicateur est défini lorsque vous run le conteneur.

Le problème est que Dockerfile est lu sur le conteneur build, donc la commande RUN ne sera pas au courant de ces variables d'environnement .

La façon d'avoir des variables d'environnement définies lors de la construction est d'ajouter dans votre Dockerfile, la ligne ENV. ( https://docs.docker.com/engine/reference/builder/#/environment-replacement )

Votre Dockerfile peut donc être:

FROM node:latest

WORKDIR /src
ADD package.json .

ENV A YOLO

RUN echo "$A"

Et la sortie:

$ docker build .
Sending build context to Docker daemon  2.56 kB
Step 1 : FROM node:latest
 ---> f5eca816b45d
Step 2 : WORKDIR /src
 ---> Using cache
 ---> 4ede3b23756d
Step 3 : ADD package.json .
 ---> Using cache
 ---> a4671a30bfe4
Step 4 : ENV A YOLO
 ---> Running in 7c325474af3c
 ---> eeefe2c8bc47
Removing intermediate container 7c325474af3c
Step 5 : RUN echo "$A"
 ---> Running in 35e0d85d8ce2
YOLO
 ---> 78d5df7d2322

Vous voyez à l'avant-dernière ligne lorsque la commande RUN est lancée, le conteneur sait que la variable d'environnement est définie.

2
Jacques Cornat