Cómo pasar argumentos a Shell Script a través de Docker Run

132

Soy nuevo en el mundo de los acopladores. Tengo que invocar un script de shell que toma argumentos de línea de comando a través de un contenedor acoplable. Ej: Mi script de shell se ve así:

#!bin/bash
echo $1

Dockerfile se ve así:

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

No estoy seguro de cómo pasar los argumentos mientras ejecuto el contenedor

Akash Mehta
fuente

Respuestas:

61

Usar lo mismo file.sh

#!/bin/bash
echo $1

Cree la imagen utilizando el Dockerfile existente:

docker build -t test .

Ejecutar la imagen con argumentos abco xyzo algo más.

docker run -ti test /file.sh abc

docker run -ti test /file.sh xyz
BMW
fuente
27
Creo que ENTRYPOINT es el camino a seguir si no desea que el usuario final sepa sobre file.sh directamente.
greg.kindel
¿Cómo puedes iniciar un script como este docker run -ti test /file.sh abc? Siento que el script no se ejecutará porque debería serlo docker run -ti test sh /file.sh abc. sh o / bin / sh lo ejecutarán correctamente.
Vamsidhar Muggulla el
1
Para cualquiera que venga aquí. El truco / usr / bin / env es una preferencia de estilo opcional, no un requisito para que esto funcione. También el #! la línea indica qué intérprete usar comprar por defecto. Por lo tanto, puede ejecutarse simplemente llamando al script.
Wheredidthatnamecomerom desde el
165

con este script en file.sh

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

y esto Dockerfile

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

deberias ser capaz de:

% docker build -t test .
% docker run test hello world
Your container args are: hello world
Parcialmente nublado
fuente
9
Si olvida el "" alrededor "/file.sh" como lo hice, no funcionará.
kev
66
por alguna razón, esto no funciona conENTRYPOINT ./file.sh
phil294
66
No olvides chmod +x file.shconfigurar la bandera ejecutable.
topskip
1
@kev, ¿sabes por qué es así? ¿cuál es la diferencia entre ["/file.sh"]e /file.shincluso[/file.sh]
Nitzankin
1
@Nitzankin ve mi respuesta de por qué se necesita un formato json adecuado.
BMitch
60

Con Docker, la forma correcta de pasar este tipo de información es a través de variables de entorno.

Entonces, con el mismo Dockerfile, cambie el script a

#!/bin/bash
echo $FOO

Después de compilar, use el siguiente comando docker:

docker run -e FOO="hello world!" test
Miguel
fuente
20
¿Por qué es esta la respuesta más votada? Env vars son otra forma de pasar información, pero no es lo que OP está pidiendo. Y, por supuesto, no hay absolutamente nada incorrecto en el deseo de OP de pasar argumentos al contenedor.
Parcialmente nublado
8
@PartlyCloudy Creo que a la gente le gusta esto porque pretende dar la respuesta "adecuada", a pesar de estar obviamente equivocado. Un principio de diseño importante de Docker es priorizar el dogma sobre el sentido común.
augurar
1
@augurar: Para mejorar esta respuesta, ¿quizás explique por qué cree que esta respuesta es "obviamente incorrecta"?
Emil Stenström
2
Hay un montón de problemas XY preguntar en SO. Dado que el OP declaró que eran nuevos en Docker, es perfectamente razonable que una respuesta muestre la forma recomendada de lograr un objetivo. Por lo tanto, esto es una gran respuesta.
colm.anseo
1
La forma más sencilla de hacer el trabajo en lugar de pasar las variables como argumentos de compilación y todo ese desorden. Muy útil para pasar secretos como variables de entorno.
Arvind Sridharan
32

Hay algunas cosas que interactúan aquí:

  1. docker run your_image arg1 arg2reemplazará el valor de CMDcon arg1 arg2. Eso es un reemplazo completo de la CMD, sin agregarle más valores. Es por eso que a menudo ves docker run some_image /bin/bashejecutar un shell bash en el contenedor.

  2. Cuando tiene definidos un ENTRYPOINT y un valor CMD, Docker inicia el contenedor concatenando los dos y ejecutando ese comando concatenado. Entonces, si define su punto de entrada para que sea file.sh, ahora puede ejecutar el contenedor con argumentos adicionales que se pasarán como argumentos a file.sh.

  3. Los puntos de entrada y los comandos en la ventana acoplable tienen dos sintaxis, una sintaxis de cadena que lanzará un shell y una sintaxis json que realizará un exec. El shell es útil para manejar cosas como la redirección de E / S, encadenando múltiples comandos juntos (con cosas como &&), sustitución de variables, etc. Sin embargo, ese shell se interpone en el manejo de la señal (si alguna vez ha visto un retraso de 10 segundos para detenerse). un contenedor, esta suele ser la causa) y con la concatenación de un punto de entrada y comando juntos. Si define su punto de entrada como una cadena, se ejecutará /bin/sh -c "file.sh", lo que solo está bien. Pero si también tiene un comando definido como una cadena, verá algo así /bin/sh -c "file.sh" /bin/sh -c "arg1 arg2"como el comando que se inicia dentro de su contenedor, no tan bueno. Consulte la tabla aquí para obtener más información sobre cómo interactúan estas dos opciones.

  4. La -copción de shell solo toma un único argumento. Todo después de eso se pasaría como $1, $2etc., a ese único argumento, pero no a un script de shell incrustado a menos que haya pasado explícitamente los argumentos. Es decir /bin/sh -c "file.sh $1 $2" "arg1" "arg2", funcionaría, pero /bin/sh -c "file.sh" "arg1" "arg2"no lo file.shharía, ya que se llamaría sin argumentos.

En conjunto, el diseño común es:

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"]

Y luego ejecutas eso con:

docker run your_image arg1 arg2

Hay bastante más detalles sobre esto en:

BMitch
fuente
1
Experimenté con la configuración de mi punto de entrada ["bash", "--login", "-c"]para obtener la fuente / etc / profile en la imagen, pero luego me pregunté por qué no se pasarían argumentos a un script de shell para ejecutar Docker ... Su respuesta se aclaró, gracias !
Apteryx
23

Lo que tengo es un archivo de script que realmente ejecuta cosas. Este archivo de script puede ser relativamente complicado. Llamémoslo "run_container". Este script toma argumentos de la línea de comando:

run_container p1 p2 p3

Un simple run_container podría ser:

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

Lo que quiero hacer es, después de "acoplar" esto, me gustaría poder iniciar este contenedor con los parámetros en la línea de comando del acoplador como este:

docker run image_name p1 p2 p3

y que el script run_container se ejecute con p1 p2 p3 como parámetros.

Esta es mi solución:

Dockerfile:

FROM docker.io/ubuntu
ADD run_container /
ENTRYPOINT ["/bin/bash", "-c", "/run_container \"$@\"", "--"]
jkh
fuente
77
Reemplazar el tercer valor en la ENTRYPOINTmatriz con "/run_container \"$@\""medios significa que los argumentos que contienen espacios se manejan correctamente (por ejemplo docker run image_name foo 'bar baz' quux).
davidchambers
Después de agregar instrucciones de cambio / caso a mi archivo bash, ENTRYPOINT ["run_container.sh"] ya no funcionaba para mí, pero ENTRYPOINT ["sh", "-c", "run_container.sh"] ya no aceptaba mis parámetros. Esta solución (con la sugerencia de @davidchambers) funcionó para mí.
rhamilton
10

Si quieres ejecutarlo @build time:

CMD /bin/bash /file.sh arg1

si quieres ejecutarlo @ tiempo de ejecución:

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

Luego en el host shell

docker build -t test .
docker run -i -t test
Gilles Quenot
fuente
2
ENTRYPOINTes una buena respuesta para el OP que creo que quería tiempo de ejecución, pero si realmente desea variables de tiempo de compilación, esta respuesta simplemente no funciona. Use ARGy docker build --build-arg docs.docker.com/engine/reference/builder/#arg
greg.kindel
0

Otra opción...

Para que esto funcione

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

en dockerfile

ENTRYPOINT ["/entrypoint.sh"]

en 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
fuente
algunos comentarios y limitaciones ... el comando con ":" necesita cambios en cut -d ':' y comandos como docker run -d --rm $ IMG_NAME "bash: echo $ PATH" mostrará el valor de la ruta del host en lugar del host uno
wagnermarques 01 de