Como passar argumentos para o Shell Script por meio da janela de encaixe

131

Eu sou novo no mundo dos estivadores. Eu tenho que chamar um script de shell que leva argumentos de linha de comando através de um contêiner de janela de encaixe. Ex: Meu script shell se parece com:

#!bin/bash
echo $1

O Dockerfile fica assim:

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

Não sei como transmitir os argumentos enquanto estiver executando o contêiner

Akash Mehta
fonte

Respostas:

61

Use o mesmo file.sh

#!/bin/bash
echo $1

Crie a imagem usando o Dockerfile existente:

docker build -t test .

Executar a imagem com argumentos abcou xyzou qualquer outra coisa.

docker run -ti test /file.sh abc

docker run -ti test /file.sh xyz
BMW
fonte
27
Acho que o ENTRYPOINT é o caminho a percorrer se você não quiser que o usuário final saiba sobre o file.sh diretamente.
greg.kindel
Como você pode simplesmente iniciar um script como este docker run -ti test /file.sh abc. Eu sinto que o script não será executado porque deveria ser docker run -ti test sh /file.sh abc. sh ou / bin / sh executará corretamente.
Vamsidhar Muggulla 4/11
1
Para mais alguém vindo aqui. O truque / usr / bin / env é uma preferência de estilo opcional, não um requisito para fazer com que isso funcione. Também o #! linha indica qual intérprete usar o padrão de compra. Portanto, ele pode ser executado apenas chamando o script.
partir de
163

com este script em file.sh

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

e isto Dockerfile

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

você deve ser capaz de:

% docker build -t test .
% docker run test hello world
Your container args are: hello world
Parcialmente nublado
fonte
9
Se você esquecer o "" around "/file.sh" como eu fiz, ele não funcionará.
kev
6
por alguma razão, isso não funciona com #ENTRYPOINT ./file.sh
phil294
6
Não se esqueça de chmod +x file.shdefinir o sinalizador executável.
topskip
1
@ kev, você sabe por que é assim? qual é a diferença entre ["/file.sh"]e /file.shou mesmo[/file.sh]
Nitzankin
1
@ Nitzankin, veja minha resposta para saber por que a formatação json adequada é necessária.
BMitch
60

Com o Docker, a maneira correta de passar esse tipo de informação é através de variáveis ​​de ambiente.

Portanto, com o mesmo Dockerfile, altere o script para

#!/bin/bash
echo $FOO

Após a construção, use o seguinte comando do docker:

docker run -e FOO="hello world!" test
Michael
fonte
20
Por que essa é a resposta mais votada? Os env vars são outra maneira de transmitir informações, mas não é o que o OP está pedindo. E, claro, não há absolutamente nada de impróprio no desejo do OP de passar argumentos para o contêiner.
Parcialmente nublado
8
@PartlyCloudy Acho que as pessoas gostam disso porque pretendem dar a resposta "adequada", apesar de estarem obviamente erradas. Um princípio de design importante do Docker é priorizar o dogma em vez do senso comum.
Augurar
1
@augurar: Para melhorar esta resposta, talvez você explique por que acha que esta resposta está "obviamente errada"?
Emil Stenström
2
Há muitos problemas XY solicitados no SO. Como o OP declarou que era novo no Docker, é perfeitamente razoável uma resposta mostrar a maneira recomendada de atingir uma meta. Tornando assim uma ótima resposta.
Colm.anseo 26/05/19
1
A maneira mais simples de realizar o trabalho, em vez de passar as variáveis ​​como build args e toda essa bagunça. Muito útil para transmitir segredos como variáveis ​​de ambiente.
Arvind Sridharan
32

Existem algumas coisas interagindo aqui:

  1. docker run your_image arg1 arg2substituirá o valor de CMDcom arg1 arg2. Essa é uma substituição completa do CMD, não acrescentando mais valores a ele. É por isso que você costuma docker run some_image /bin/bashexecutar um shell bash no contêiner.

  2. Quando você tem um valor ENTRYPOINT e um valor CMD definidos, a janela de encaixe inicia o contêiner concatenando os dois e executando esse comando concatenado. Portanto, se você definir seu ponto de entrada file.sh, agora poderá executar o contêiner com argumentos adicionais que serão passados ​​como argumentos para file.sh.

  3. Pontos de entrada e comandos na janela de encaixe possuem duas sintaxes, uma sintaxe de cadeia que iniciará um shell e uma sintaxe json que executará um exec. O shell é útil para lidar com coisas como redirecionamento de E / S, encadear vários comandos juntos (com coisas como &&), substituição de variáveis ​​etc. No entanto, esse shell atrapalha a manipulação do sinal (se você já viu um atraso de 10 segundos para parar um contêiner, isso geralmente é a causa) e concatenar um ponto de entrada e um comando juntos. Se você definir seu ponto de entrada como uma sequência, ele será executado /bin/sh -c "file.sh", o que por si só é bom. Mas se você tiver um comando definido como uma sequência também, verá algo como /bin/sh -c "file.sh" /bin/sh -c "arg1 arg2"o comando sendo lançado dentro do seu contêiner, não tão bom. Veja a tabela aqui para mais informações sobre como essas duas opções interagem

  4. A -copção shell usa apenas um único argumento. Tudo depois disso seria passado como $1, $2etc, para esse único argumento, mas não para um script de shell incorporado, a menos que você passasse explicitamente os argumentos. Ou seja /bin/sh -c "file.sh $1 $2" "arg1" "arg2", funcionaria, mas /bin/sh -c "file.sh" "arg1" "arg2"não file.shseria desde então chamado sem argumentos.

Juntando tudo isso, o design comum é:

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

E você executa isso com:

docker run your_image arg1 arg2

Há um pouco mais de detalhes sobre isso em:

BMitch
fonte
1
Eu havia experimentado definir meu ponto de entrada ["bash", "--login", "-c"]para obter a origem do / etc / profile na imagem, mas depois fiquei perguntando por que nenhum argumento seria passado para um script de shell passado para a execução do docker ... Sua resposta esclareceu isso, obrigado !
Apteryx
23

O que tenho é um arquivo de script que realmente executa as coisas. Esse arquivo de script pode ser relativamente complicado. Vamos chamá-lo de "run_container". Este script usa argumentos da linha de comando:

run_container p1 p2 p3

Um simples run_container pode ser:

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

O que eu quero fazer é que, depois de "encaixar" isso, eu gostaria de inicializar este contêiner com os parâmetros na linha de comando do docker, como este:

docker run image_name p1 p2 p3

e faça com que o script run_container seja executado com p1 p2 p3 como parâmetros.

Esta é a minha solução:

Dockerfile:

FROM docker.io/ubuntu
ADD run_container /
ENTRYPOINT ["/bin/bash", "-c", "/run_container \"$@\"", "--"]
jkh
fonte
7
Substituir o terceiro valor na ENTRYPOINTmatriz por "/run_container \"$@\""argumentos de média contendo espaços é tratado corretamente (por exemplo docker run image_name foo 'bar baz' quux).
Davidchambers
Após adicionar instruções switch / case ao meu arquivo bash, o ENTRYPOINT ["run_container.sh"] não funcionou mais para mim, mas o ENTRYPOINT ["sh", "-c", "run_container.sh"] não aceita mais meus parâmetros. Esta solução (com sugestão de @davidchambers) funcionou para mim.
Rhamilton #
10

Se você deseja executá-lo em @build time:

CMD /bin/bash /file.sh arg1

se você quiser executá-lo em @run time:

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

Em seguida, no shell do host

docker build -t test .
docker run -i -t test
Gilles Quenot
fonte
2
ENTRYPOINTé uma boa resposta para o OP que, na minha opinião, queria tempo de execução, mas se você realmente deseja criar variáveis ​​de tempo, essa resposta está quebrada. Use ARGe docker build --build-arg docs.docker.com/engine/reference/builder/#arg
greg.kindel 15/03
0

Outra opção...

Para fazer isso funcionar

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

em dockerfile

ENTRYPOINT ["/entrypoint.sh"]

no 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
fonte
algumas observações e limitações .... o comando com ":" precisa de alterações em cut -d ':' e comandos como o docker run -d --rm $ IMG_NAME "bash: echo $ PATH" mostrarão o valor do caminho do host em vez do host one
wagnermarques 01/07/19