163 votes

Comment puis-je utiliser une variable à l'intérieur d'un Dockerfile CMD ?

Dans mon Dockerfile :

ENV PROJECTNAME mytestwebsite
CMD ["django-admin", "startproject", "$PROJECTNAME"]

Erreur :

CommandError: '$PROJECTNAME' is not a valid project name

Quelle est la solution la plus rapide ? Docker prévoit-il de " corriger " ou d'introduire cette fonctionnalité dans les versions ultérieures de Docker ?

NOTE : Si je supprime la ligne CMD du fichier Docker et que j'exécute ensuite le conteneur Docker, je peux exécuter manuellement Django-admin startproject $PROJECTNAME depuis l'intérieur du conteneur et le projet sera créé...

1 votes

Comment et quand définissez-vous $PROJECTNAME ?

0 votes

Au début de mon Dockerfile en utilisant ENV. J'ai également oublié de mentionner que si je supprime la ligne CMD du Dockerfile et que je lance ensuite le conteneur, je peux exécuter cette commande depuis l'intérieur du conteneur et le projet sera créé (ce qui signifie que la variable ENV est valide).

0 votes

De quel type de variable parlez-vous : variable de dockerfile ou variable environnementale (comme dans votre système d'exécution) ?

239voto

larsks Points 23184

Lorsque vous utilisez une liste d'exécution, comme dans...

CMD ["django-admin", "startproject", "$PROJECTNAME"]

...alors Docker exécutera la commande donnée directement sans impliquer un shell. Puisqu'il n'y a pas de shell impliqué, cela signifie :

  • Pas d'expansion variable
  • Pas d'expansion des caractères de substitution
  • Pas de redirection des entrées/sorties avec > , < , | etc.
  • Pas de commandes multiples via command1; command2
  • Et ainsi de suite.

Si vous voulez que votre CMD pour développer les variables, vous devez prévoir une coquille. Vous pouvez le faire comme ceci :

CMD ["sh", "-c", "django-admin startproject $PROJECTNAME"]

Ou bien vous pouvez utiliser une simple chaîne de caractères au lieu d'une liste d'exécution, ce qui vous donne un résultat largement identique à l'exemple précédent :

CMD django-admin startproject $PROJECTNAME

5 votes

Un peu plus de lecture dans le gestionnaire de problèmes de Docker : github.com/docker/docker/issues/5509

34voto

rex roy Points 141

Si vous souhaitez utiliser la valeur au moment de l'exécution, définissez l'option ENV dans le Dockerfile . Si vous voulez l'utiliser au moment de la construction, vous devez utiliser ARG .

Exemple :

ARG value
ENV envValue=$value
CMD ["sh", "-c", "java -jar ${envValue}.jar"]

Passez la valeur dans la commande de construction :

docker build -t tagName --build-arg value="jarName"

12voto

Flavio Aiello Points 11

Disons que vous voulez lancer un processus Java dans un conteneur :

Exemple d'extrait de Dockerfile :

ENV JAVA_OPTS -XX +UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XX:MaxRAMFraction=1 -XshowSettings:vm 
... 
ENTRYPOINT ["/sbin/tini", "--", "entrypoint.sh"] 
CMD ["java", "${JAVA_OPTS}", "-myargument=true"]

Exemple d'extrait de entrypoint.sh :

#!/bin/sh 
... 
echo "*** Startup $0 suceeded now starting service using eval to expand CMD variables ***"
exec su-exec mytechuser $(eval echo "$@")

1voto

Zana Simsek Points 71

Pour les développeurs Java, suivre ma solution ci-dessous va fonctionner :

si vous avez essayé d'exécuter votre conteneur avec un Dockerfile comme ci-dessous

ENTRYPOINT ["/docker-entrypoint.sh"]
# does not matter your parameter $JAVA_OPTS wrapped as ${JAVA_OPTS}
CMD ["java", "$JAVA_OPTS", "-javaagent:/opt/newrelic/newrelic.jar", "-server", "-jar", "app.jar"]

avec un shell ENTRYPOINT script ci-dessous :

#!/bin/bash
set -e
source /work-dir/env.sh
exec "$@"

il va construire l'image correctement mais imprimer l'erreur ci-dessous pendant l'exécution du conteneur :

Error: Could not find or load main class $JAVA_OPTS
Caused by: java.lang.ClassNotFoundException: $JAVA_OPTS

Au lieu de cela, Java peut lire les paramètres de la ligne de commande soit par la ligne de commande soit par _JAVA_OPTIONS Cela signifie que nous pouvons transmettre les paramètres de ligne de commande souhaités par le biais de la variable d'environnement _JAVA_OPTIONS sans changer quoi que ce soit dans le Dockerfile, ainsi que pour lui permettre de démarrer en tant que processus parent du conteneur pour la signalisation valide de docker via exec "$@" .

Celle ci-dessous est ma version finale de la Dockerfile y docker-entrypoint.sh des fichiers :

...
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["java", "-server", "-jar", "app.jar"]

#!/bin/bash
set -e
source /work-dir/env.sh
export _JAVA_OPTIONS="-XX:+PrintFlagsFinal"
exec "$@"

et après avoir construit votre image docker et essayé de l'exécuter, vous verrez les logs ci-dessous qui signifient que cela a bien fonctionné :

Picked up _JAVA_OPTIONS: -XX:+PrintFlagsFinal
[Global flags]
      int ActiveProcessorCount                     = -1                                        {product} {default}

0voto

csomakk Points 1279

Inspiré par ce qui précède, j'ai fait ça :

#snapshot by default. 1 is release.
ENV isTagAndRelease=0

CMD     echo is_tag: ${isTagAndRelease} && \
        if [ ${isTagAndRelease} -eq 1 ]; then echo "release build"; mvn -B release:clean release:prepare release:perform; fi && \
        if [ ${isTagAndRelease} -ne 1 ]; then echo "snapshot build"; mvn clean install; fi && \ 
       .....

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X