web-dev-qa-db-fra.com

Utilisation de l'interpolation variable dans une chaîne dans Docker

Je ne parviens pas à créer et à utiliser des variables dans un fichier Dockerfile. Je crée une image Docker via un fichier Dockerfile à l'aide de cette commande:

$ docker build --build-arg s=scripts/a.sh -t a .

(Donc, parce que j'utilise --build-arg, $ s sera un argument disponible dans le fichier Dockerfile, et cette partie fonctionne)

Le Dockerfile est comme ça:

ARG s

RUN echo $s 

RUN useradd -ms /bin/bash newuser

USER newuser
WORKDIR /home/newuser

ENV fn=$(filename $s)  # fails on this line

COPY $s .

ENTRYPOINT ["/bin/bash", "/home/newuser/$fn"]

Le problème que j'ai est que la construction de Docker échoue sur la ligne indiquée ci-dessus.

Error response from daemon: Syntax error - can't find = in "$s)". Must be of the form: name=value

Si je change cette ligne en ceci:

RUN fn=$(filename $s)

Je reçois cette erreur:

Error: Command failed: docker build --build-arg s=scripts/a.sh -t a .
The command '/bin/sh -c fn=$(filename $s)' returned a non-zero code: 127

Quelqu'un sait comment s'y prendre

  1. Créer une variable dans le fichier docker
  2. Utilisez une interpolation de chaîne avec cette variable pour pouvoir référencer la variable dans les arguments ENTRYPOINT comme suit:

    ENTRYPOINT ["/ bin/bash", "/ home/newuser/$ var"]

Même si je fais ça:

ARG s

ARG sname

RUN echo $s          # works as expected
RUN echo $sname      # works as expected

RUN useradd -ms /bin/bash newuser

USER newuser
WORKDIR /home/newuser


COPY $s .  # works as expected (I believe)

ENTRYPOINT /bin/bash /home/newuser/$sname  # does not work as expected

même si j'utilise la version "non-JSON" de ENTRYPOINT, il ne semble toujours pas capter la valeur de la variable $sname.

13
Alexander Mills

Je voudrais éviter d'utiliser variable dans ENTRYPOINT du tout. C'est délicat et nécessite une compréhension profonde de ce qui se passe. Et il est facile de le casser par accident. Il suffit de penser à l’un des éléments suivants.

Créez un lien avec le nom connu de votre script de démarrage.

RUN ln -s /home/newuser/$sname /home/newuser/docker_entrypoint.sh
ENTRYPOINT ["/home/newuser/docker_entrypoint.sh"]

ou écrivez un script de point d’entrée autonome qui exécute ce dont vous avez besoin.

Mais si vous voulez savoir comment et pourquoi les solutions de vos questions fonctionnent, continuez à lire.

D'abord quelques définitions.

  • ENV - la variable environment est-elle disponible lors de la compilation (docker build) et de l'exécution (docker run
  • ARG - la variable environment est-elle disponible uniquement pendant la phase de construction

Si vous consultez https://docs.docker.com/engine/reference/builder/#environment-replacement vous voyez la liste des instructions de dockerfile qui prennent directement en charge ces variables d’environnement. C'est pourquoi COPY "prend la variable" comme vous l'avez dit. 

Veuillez noter qu'il n'y a pas de RUN ni ENTRYPOINT. Comment ça marche?

Vous devez creuser dans la documentation. Première RUN ( https://docs.docker.com/engine/reference/builder/#run ). Il y a 2 formes. Le premier exécute la commande via le shell et ce dernier a accès aux variables d'environnement de compilation.

# this works because it is called as /bin/sh -c 'echo $sname'
# the /bin/sh replace $sname for you      
RUN echo $sname 

# this does NOT work. There is no Shell process to do $sname replacement 
# for you
RUN ["echo", "$sname"]

La même chose s'applique aux variables ENTRYPOINT et CMD, sauf que seules les variables d'exécution sont disponibles au démarrage du conteneur.

# first you need to make some runtime variable from builtime one
ENV sname $sname

# Then you can use it during container runtime
# docker runs `/bin/sh -c '/bin/bash /home/newuser/$sname'` for you
# and this `/bin/sh` proces interprets `$sname`
ENTRYPOINT /bin/bash /home/newuser/$sname

# but this does NOT work. There is no process to interpolate `$sname`
# docker runs what you describe.
ENTRYPOINT ["/bin/bash", "/home/newuser/$sname"]

edit 2017-04-03: mise à jour des liens vers les documentations du docker et légère reformulation pour éviter toute confusion que je ressens à partir d'autres réponses et commentaires.

17
Villlem

J'ai demandé à @Villem de répondre, et sa réponse est beaucoup plus définitive, mais ce qui suit fonctionnera (ce n'est tout simplement pas une solution stable). Sa réponse est essentiellement de dire que cette réponse n’est pas un bon moyen de le faire:

ARG s           # passed in via --build-arg s=foo
ARG sname       # passed in via --build-arg sname=bar

RUN echo $s       
RUN echo $sname  

ENV sname $sname   # this is the key part

RUN useradd -ms /bin/bash newuser

USER newuser
WORKDIR /home/newuser

COPY $s .

ENTRYPOINT /bin/bash /home/newuser/$sname   # works, but is not stable!

ne me demandez pas pourquoi la commande COPY récupère la variable déclarée via ARG, mais que la commande ENTRYPOINT ne pas semble capturer la variable déclarée via ARG, mais uniquement la variable déclarée via ENV. Au moins, cela semble être le cas.

3
Alexander Mills

Je voulais à la fois la substitution de variable et les arguments qui passent.

Disons que notre Dockerfile a:

ENV VARIABLE=replaced

Et nous voulons exécuter ceci:

docker run <image> arg1 arg2

J'ai évidemment voulu cette sortie:

replaced arg1 arg2

J'ai finalement trouvé celui-ci:

ENTRYPOINT [ "sh", "-c", "echo $VARIABLE $0 $@" ]

Ca marche !!! Mais je me sens tellement sale!

Évidemment, dans la vie réelle, je voulais faire quelque chose de plus utile:

docker run <image> --app.options.additional.second=true --app.options.additional.third=false

ENTRYPOINT [ "sh", "-c", "Java -Xmx$XMX $0 $@", \
             "-jar", \
             "/my.jar", \
             "--app.options.first=true" ]

Pourquoi une réponse si compliquée?

  • "ENTRYPOINT Java ..." ne transmet pas les arguments du menu fixe au point d'entrée => "ENTRYPOINT [..." est obligatoire pour cela.
  • "ENTRYPOINT [..." n'appellera PAS le shell, aucune substitution de variable n'est donc effectuée => "sh -c" est obligatoire pour cela.
  • "sh -c" ne prend que l'argument FIRST qui lui a été transmis et le divise en commande + arguments => afin que tout soit dans le premier argument de "sh -c" pour que les variables soient visibles par la commande
  • Les arguments de Docker sont transmis en tant qu'entrées de tableau supplémentaires de "ENTRYPOINT [..." =>; par conséquent, le "$ @" est nécessaire pour "copier" les arguments restants de "sh-c" dans la commande "echo ..." de être exécuté (et en bonus, nous pouvons réutiliser les entrées de tableau supplémentaires de ENTRYPOINT [] pour placer les arguments forcés de manière lisible dans le fichier Docker)
  • "$ @" supprime le premier argument => donc explicite "$ 0" doit être utilisé avant "$ @"

Fiou ...

J'ai ajouté un commentaire à ce problème afin que les développeurs Docker puissent voir ce que nous sommes obligés de faire et qu'ils changent peut-être d'avis pour que ENTRYPOINT [] remplace les variables d'environnement: https://github.com/moby/moby/issues/4783 # issuecomment-442466609

0
Sebien