Jak przekazać argumenty do skryptu powłoki poprzez uruchomienie dockera

131

Jestem nowy w świecie dokerów. Muszę wywołać skrypt powłoki, który pobiera argumenty wiersza poleceń przez kontener Dockera. Np. Mój skrypt powłoki wygląda następująco:

#!bin/bash
echo $1

Dockerfile wygląda następująco:

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

Nie jestem pewien, jak przekazać argumenty podczas uruchamiania kontenera

Akash Mehta
źródło

Odpowiedzi:

61

Użyj tego samego file.sh

#!/bin/bash
echo $1

Zbuduj obraz, używając istniejącego pliku Dockerfile:

docker build -t test .

Uruchomić obraz z argumentów abclub xyzczy coś innego.

docker run -ti test /file.sh abc

docker run -ti test /file.sh xyz
BMW
źródło
27
Myślę, że ENTRYPOINT jest dobrym rozwiązaniem, jeśli nie chcesz, aby użytkownik końcowy wiedział bezpośrednio o file.sh.
greg.kindel
Jak można po prostu uruchomić skrypt takiego docker run -ti test /file.sh abc. Czuję, że skrypt się nie uruchomi, ponieważ powinien docker run -ti test sh /file.sh abc. sh lub / bin / sh uruchomi to poprawnie.
Vamsidhar Muggulla
1
Dla każdego, kto tu przyjeżdża. Sztuczka / usr / bin / env jest opcjonalną preferencją stylu, a nie wymogiem, aby to zadziałało. Również #! linia wskazuje, którego interpretera użyć kup domyślny. Więc można go uruchomić po prostu wywołując skrypt.
powodu od
163

z tym skryptem w formacie file.sh

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

i to Dockerfile

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

Powinieneś być w stanie:

% docker build -t test .
% docker run test hello world
Your container args are: hello world
Częściowe zachmurzenie
źródło
9
Jeśli zapomnisz o "" około "/file.sh" tak jak ja, to nie zadziała.
kev
6
z jakiegoś powodu to nie działaENTRYPOINT ./file.sh
phil294
6
Nie zapomnij chmod +x file.shustawić flagi pliku wykonywalnego.
topskip
1
@kev, czy wiesz, dlaczego tak jest? jaka jest różnica między ["/file.sh"]a /file.sha nawet[/file.sh]
Nitzankin
1
@Nitzankin zobacz moją odpowiedź, dlaczego potrzebne jest prawidłowe formatowanie json.
BMitch
60

W przypadku Dockera właściwym sposobem przekazywania tego rodzaju informacji są zmienne środowiskowe.

Więc z tym samym plikiem Dockerfile zmień skrypt na

#!/bin/bash
echo $FOO

Po zbudowaniu użyj następującego polecenia dokowanego:

docker run -e FOO="hello world!" test
Michael
źródło
20
Dlaczego jest to najwyższa głosowana odpowiedź? Warianty Env to kolejny sposób przekazywania informacji, ale nie jest tym, o co pyta OP. I oczywiście nie ma absolutnie nic niewłaściwego w chęci OP do przekazywania argumentów do kontenera.
Częściowe zachmurzenie
8
@PartlyCloudy Myślę, że ludzie to lubią, ponieważ rzekomo dają „właściwą” odpowiedź, mimo że są ewidentnie błędne. Główną zasadą projektową Dockera jest nadanie priorytetu dogmatowi nad zdrowym rozsądkiem.
augurar
1
@augurar: Aby poprawić tę odpowiedź, może wyjaśnisz, dlaczego uważasz, że ta odpowiedź jest „oczywiście błędna”?
Emil Stenström
2
Na SO jest mnóstwo problemów z XY. Ponieważ OP stwierdził, że są nowicjuszami w Dockerze, jest całkiem rozsądne, że odpowiedź wskazuje zalecany sposób osiągnięcia celu. W ten sposób jest to świetna odpowiedź.
colm.anseo
1
Najprostszy sposób na wykonanie zadania zamiast przekazywania zmiennych jako argumentów kompilacji i całego tego bałaganu. Bardzo przydatne do przekazywania sekretów jako zmiennych środowiskowych.
Arvind Sridharan
32

Jest tu kilka rzeczy, które wchodzą w interakcje:

  1. docker run your_image arg1 arg2zastąpi wartość CMDz arg1 arg2. To jest pełne zastąpienie CMD, bez dodawania do niego więcej wartości. Dlatego często widzisz docker run some_image /bin/bashuruchamianie powłoki bash w kontenerze.

  2. Gdy masz zdefiniowane zarówno ENTRYPOINT, jak i CMD, docker uruchamia kontener, łącząc te dwa elementy i uruchamiając to połączone polecenie. Jeśli więc zdefiniujesz swój punkt wejścia file.sh, możesz teraz uruchomić kontener z dodatkowymi argumentami, które zostaną przekazane jako argumenty do file.sh.

  3. Punkty wejścia i polecenia w dockerze mają dwie składnie, składnię ciągu, która uruchomi powłokę, oraz składnię json, która wykona polecenie exec. Powłoka jest przydatna do obsługi takich rzeczy jak przekierowanie IO, łączenie wielu poleceń razem (z takimi rzeczami &&), podstawianie zmiennych itp. Jednak powłoka ta przeszkadza w obsłudze sygnału (jeśli kiedykolwiek widziałeś 10-sekundowe opóźnienie zatrzymania kontenera, to jest często przyczyna) oraz z połączeniem punktu wejścia i polecenia razem. Jeśli zdefiniujesz swój punkt wejścia jako łańcuch, będzie działać /bin/sh -c "file.sh", co samo w sobie jest w porządku. Ale jeśli masz polecenie zdefiniowane również jako ciąg, zobaczysz coś takiego, /bin/sh -c "file.sh" /bin/sh -c "arg1 arg2"jak polecenie uruchamiane w twoim kontenerze, nie tak dobrze. Zobacz tabelę tutaj, aby uzyskać więcej informacji na temat interakcji tych dwóch opcji

  4. Opcja powłoki -cprzyjmuje tylko jeden argument. Wszystko po to by uzyskać przekazany jako $1, $2itp, w tym jeden argument, ale nie w osadzonym skryptem powłoki, chyba że wyraźnie przeszły args. To znaczy /bin/sh -c "file.sh $1 $2" "arg1" "arg2"zadziała, ale /bin/sh -c "file.sh" "arg1" "arg2"od file.shtego czasu nie zostanie wywołany bez argumentów.

Podsumowując, wspólny projekt to:

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

Następnie uruchamiasz to z:

docker run your_image arg1 arg2

Jest o wiele więcej szczegółów na ten temat pod adresem:

BMitch
źródło
1
Eksperymentowałem z ustawieniem mojego punktu wejścia, aby ["bash", "--login", "-c"]uzyskać źródło / etc / profile w obrazie, ale później zastanawiałem się, dlaczego żadne argumenty nie zostaną przekazane do skryptu powłoki przekazanego do uruchomienia dockera ... Twoja odpowiedź wyjaśniła to, dziękuję !
Apteryx
23

To, co mam, to plik skryptu, który faktycznie uruchamia. Ten plik skryptu może być stosunkowo skomplikowany. Nazwijmy to „run_container”. Ten skrypt pobiera argumenty z wiersza poleceń:

run_container p1 p2 p3

Prostym run_container może być:

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

To, co chcę zrobić, to po "dokowaniu" tego chciałbym móc uruchomić ten kontener z parametrami w wierszu poleceń dockera, takimi jak:

docker run image_name p1 p2 p3

i uruchom skrypt run_container z parametrami p1 p2 p3.

To jest moje rozwiązanie:

Dockerfile:

FROM docker.io/ubuntu
ADD run_container /
ENTRYPOINT ["/bin/bash", "-c", "/run_container \"$@\"", "--"]
jkh
źródło
7
Zastąpienie trzeciej wartości w ENTRYPOINTtablicy "/run_container \"$@\""środkami oznacza, że ​​argumenty zawierające spacje są obsługiwane poprawnie (np docker run image_name foo 'bar baz' quux.).
davidchambers
Po dodaniu instrukcji switch / case do mojego pliku bash, ENTRYPOINT ["run_container.sh"] nie działał już dla mnie, ale ENTRYPOINT ["sh", "-c", "run_container.sh"] nie akceptował już moich parametrów. To rozwiązanie (z sugestią @davidchambers) zadziałało dla mnie.
rhamilton
10

Jeśli chcesz go uruchomić w czasie kompilacji:

CMD /bin/bash /file.sh arg1

jeśli chcesz go uruchomić @ run time:

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

Następnie w powłoce hosta

docker build -t test .
docker run -i -t test
Gilles Quenot
źródło
2
ENTRYPOINTto dobra odpowiedź dla OP, który moim zdaniem potrzebował środowiska uruchomieniowego, ale jeśli naprawdę chcesz zmiennych czasu kompilacji, ta odpowiedź jest po prostu zepsuta. Użyj ARGi docker build --build-arg docs.docker.com/engine/reference/builder/#arg
greg.kindel
0

Inna opcja...

Aby to działało

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

w pliku dockerfile

ENTRYPOINT ["/entrypoint.sh"]

w 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
źródło
kilka uwag i ograniczeń .... polecenie z ":" wymaga zmian w cut -d ':' i polecenia takie jak docker run -d --rm $ IMG_NAME "bash: echo $ PATH" pokaże ścieżkę hosta zamiast hosta one
wagnermarques