Comment passer des arguments à Shell Script via l'exécution de docker

132

Je suis nouveau dans le monde des dockers. Je dois invoquer un script shell qui prend des arguments de ligne de commande via un conteneur docker. Ex: Mon script shell ressemble à:

#!bin/bash
echo $1

Dockerfile ressemble à ceci:

FROM ubuntu:14.04
COPY ./file.sh /
CMD /bin/bash file.sh

Je ne sais pas comment transmettre les arguments lors de l'exécution du conteneur

Akash Mehta
la source

Réponses:

61

Utilisez le même file.sh

#!/bin/bash
echo $1

Créez l'image à l'aide du Dockerfile existant:

docker build -t test .

Exécutez l'image avec des arguments abcou xyzautre chose.

docker run -ti test /file.sh abc

docker run -ti test /file.sh xyz
BMW
la source
27
Je pense que ENTRYPOINT est la voie à suivre si vous ne voulez pas que l'utilisateur final connaisse directement file.sh.
greg.kindel
Comment pouvez-vous simplement démarrer un script comme celui-ci docker run -ti test /file.sh abc. Je pense que le script ne fonctionnera pas parce qu'il devrait l'être docker run -ti test sh /file.sh abc. sh ou / bin / sh l'exécutera correctement.
Vamsidhar Muggulla
1
Pour quiconque vient ici. L'astuce / usr / bin / env est une préférence de style facultative et non une exigence pour que cela fonctionne. Également #! La ligne indique quel interpréteur utiliser buy default. Il peut donc fonctionner simplement en appelant le script.
wheredidthatnamecome du
165

avec ce script dans file.sh

#!/bin/bash
echo Your container args are: "$@"

et ça Dockerfile

FROM ubuntu:14.04
COPY ./file.sh /
ENTRYPOINT ["/file.sh"]

tu devrais être capable de:

% docker build -t test .
% docker run test hello world
Your container args are: hello world
Partiellement nuageux
la source
9
Si vous oubliez le "" autour de "/file.sh" comme je l'ai fait, cela ne fonctionnera pas.
kev
6
pour une raison quelconque, cela ne fonctionne pas avecENTRYPOINT ./file.sh
phil294
6
N'oubliez pas chmod +x file.shde définir l'indicateur exécutable.
topskip
1
@kev, savez-vous pourquoi c'est comme ça? quelle est la différence entre ["/file.sh"]et /file.shou même[/file.sh]
Nitzankin
1
@Nitzankin voyez ma réponse pour savoir pourquoi un formatage json approprié est nécessaire.
BMitch
60

Avec Docker, la bonne façon de transmettre ce type d'informations consiste à utiliser des variables d'environnement.

Donc, avec le même Dockerfile, changez le script en

#!/bin/bash
echo $FOO

Après la construction, utilisez la commande docker suivante:

docker run -e FOO="hello world!" test
Michael
la source
20
Pourquoi est-ce la réponse la plus votée? Les variables d'environnement sont un autre moyen de transmettre des informations, mais ce n'est pas ce que demande OP. Et bien sûr, il n'y a absolument rien d'incorrect dans le désir d'OP de transmettre des arguments au conteneur.
Partiellement nuageux
8
@PartlyCloudy Je pense que les gens aiment ça parce que cela prétend donner la «bonne» réponse, même si c'est manifestement faux. Un principe de conception majeur de Docker est de donner la priorité au dogme au bon sens.
augurar
1
@augurar: Pour améliorer cette réponse, peut-être expliquez-vous pourquoi vous pensez que cette réponse est "manifestement fausse"?
Emil Stenström
2
Il y a une tonne de problèmes XY à poser sur SO. Étant donné que l'OP a déclaré qu'il était nouveau sur Docker, il est parfaitement raisonnable qu'une réponse montre la façon recommandée d'atteindre un objectif. Ce qui en fait une excellente réponse.
colm.anseo
1
Le moyen le plus simple de faire le travail au lieu de passer les variables en tant qu'arguments de construction et tout ce bordel. Très utile pour passer des secrets en tant que variables d'environnement.
Arvind Sridharan
32

Il y a plusieurs choses qui interagissent ici:

  1. docker run your_image arg1 arg2remplacera la valeur de CMDpar arg1 arg2. C'est un remplacement complet du CMD, sans lui ajouter plus de valeurs. C'est pourquoi vous voyez souvent docker run some_image /bin/bashexécuter un shell bash dans le conteneur.

  2. Lorsque vous avez défini à la fois un ENTRYPOINT et une valeur CMD, docker démarre le conteneur en concaténant les deux et en exécutant cette commande concaténée. Donc, si vous définissez votre point d'entrée comme étant file.sh, vous pouvez maintenant exécuter le conteneur avec des arguments supplémentaires qui seront transmis en tant qu'arguments à file.sh.

  3. Les points d'entrée et les commandes dans le menu fixe ont deux syntaxes, une syntaxe de chaîne qui lancera un shell et une syntaxe json qui exécutera un exec. Le shell est utile pour gérer des choses comme la redirection IO, l'enchaînement de plusieurs commandes (avec des choses comme &&), la substitution de variables, etc. un conteneur, c'est souvent la cause) et avec la concaténation d'un point d'entrée et d'une commande ensemble. Si vous définissez votre point d'entrée comme une chaîne, il s'exécuterait /bin/sh -c "file.sh", ce qui seul convient. Mais si vous avez également une commande définie comme une chaîne, vous verrez quelque chose comme /bin/sh -c "file.sh" /bin/sh -c "arg1 arg2"la commande lancée à l'intérieur de votre conteneur, pas si bon. Consultez le tableau ici pour en savoir plus sur l'interaction de ces deux options

  4. L' -coption shell ne prend qu'un seul argument. Tout après qui se passé comme $1, $2, etc., à cet argument unique, mais pas dans un script shell intégré , sauf si vous avez passé explicitement les args. Ie /bin/sh -c "file.sh $1 $2" "arg1" "arg2"fonctionnerait, mais /bin/sh -c "file.sh" "arg1" "arg2"ne le ferait pas depuis file.shserait appelé sans argument.

En mettant tout cela ensemble, la conception commune est:

FROM ubuntu:14.04
COPY ./file.sh /
RUN chmod 755 /file.sh
# Note the json syntax on this next line is strict, double quotes, and any syntax
# error will result in a shell being used to run the line.
ENTRYPOINT ["file.sh"]

Et vous exécutez ensuite cela avec:

docker run your_image arg1 arg2

Il y a un peu plus de détails à ce sujet sur:

BMitch
la source
1
J'avais essayé de définir mon point d'entrée sur ["bash", "--login", "-c"]pour obtenir la source / etc / profile dans l'image, mais je me suis ensuite demandé pourquoi aucun argument ne serait passé à un script shell passé à docker run ... Votre réponse a clarifié cela, merci !
Apteryx
23

Ce que j'ai est un fichier de script qui exécute réellement les choses. Ce fichier script peut être relativement compliqué. Appelons cela "run_container". Ce script prend les arguments de la ligne de commande:

run_container p1 p2 p3

Un simple run_container pourrait être:

#!/bin/bash
echo "argc = ${#*}"
echo "argv = ${*}"

Ce que je veux faire, c'est qu'après "docker" ceci, je voudrais pouvoir démarrer ce conteneur avec les paramètres de la ligne de commande docker comme ceci:

docker run image_name p1 p2 p3

et faites exécuter le script run_container avec p1 p2 p3 comme paramètres.

Voici ma solution:

Dockerfile:

FROM docker.io/ubuntu
ADD run_container /
ENTRYPOINT ["/bin/bash", "-c", "/run_container \"$@\"", "--"]
jkh
la source
7
Remplacer la troisième valeur du ENTRYPOINTtableau par des "/run_container \"$@\""moyens que les arguments contenant des espaces sont gérés correctement (par exemple docker run image_name foo 'bar baz' quux).
davidchambers
Après avoir ajouté des instructions switch / case à mon fichier bash, ENTRYPOINT ["run_container.sh"] ne fonctionnait plus pour moi, mais ENTRYPOINT ["sh", "-c", "run_container.sh"] n'accepterait plus mes paramètres. Cette solution (avec la suggestion @davidchambers) a fonctionné pour moi.
rhamilton
10

Si vous voulez l'exécuter à l'heure de construction:

CMD /bin/bash /file.sh arg1

si vous voulez l'exécuter à l'heure de l'exécution:

ENTRYPOINT ["/bin/bash"]
CMD ["/file.sh", "arg1"]

Puis dans le shell hôte

docker build -t test .
docker run -i -t test
Gilles Quenot
la source
2
ENTRYPOINTest une bonne réponse pour l'OP qui, je pense, voulait un runtime, mais si vous voulez vraiment des variables de temps de construction, cette réponse est tout simplement cassée. Utilisez ARGet docker build --build-arg docs.docker.com/engine/reference/builder/#arg
greg.kindel
0

Une autre option...

Pour que cela fonctionne

docker run -d --rm $IMG_NAME "bash:command1&&command2&&command3"

dans dockerfile

ENTRYPOINT ["/entrypoint.sh"]

dans entrypoint.sh

#!/bin/sh

entrypoint_params=$1
printf "==>[entrypoint.sh] %s\n" "entry_point_param is $entrypoint_params"

PARAM1=$(echo $entrypoint_params | cut -d':' -f1) # output is 1 must be 'bash' it     will be tested    
PARAM2=$(echo $entrypoint_params | cut -d':' -f2) # the real command separated by     &&

printf "==>[entrypoint.sh] %s\n" "PARAM1=$PARAM1"
printf "==>[entrypoint.sh] %s\n" "PARAM2=$PARAM2"

if [ "$PARAM1" = "bash" ];
then
    printf "==>[entrypoint.sh] %s\n" "about to running $PARAM2 command"
    echo $PARAM2 | tr '&&' '\n' | while read cmd; do
        $cmd
    done    
fi
wagnermarques
la source
quelques remarques et limitations .... la commande avec ":" nécessite des changements dans cut -d ':' et des commandes comme docker run -d --rm $ IMG_NAME "bash: echo $ PATH" affichera la valeur du chemin de l'hôte à la place de l'hôte one
wagnermarques