Cara meneruskan argumen ke Shell Script melalui docker run

132

Saya baru ke dunia buruh pelabuhan. Saya harus menjalankan skrip shell yang mengambil argumen baris perintah melalui wadah buruh pelabuhan. Contoh: Skrip shell saya terlihat seperti:

#!bin/bash
echo $1

Dockerfile terlihat seperti ini:

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

Saya tidak yakin bagaimana menyampaikan argumen saat menjalankan wadah

Akash Mehta
sumber

Jawaban:

61

Gunakan hal yang sama file.sh

#!/bin/bash
echo $1

Bangun gambar menggunakan Dockerfile yang ada:

docker build -t test .

Jalankan gambar dengan argumen abcatau xyzatau sesuatu yang lain.

docker run -ti test /file.sh abc

docker run -ti test /file.sh xyz
BMW
sumber
27
Saya pikir ENTRYPOINT adalah cara untuk pergi jika Anda tidak ingin pengguna akhir tahu tentang file.sh secara langsung.
greg.kindel
Bagaimana Anda bisa memulai skrip seperti ini docker run -ti test /file.sh abc. Saya merasa bahwa skrip tidak akan berjalan karena seharusnya docker run -ti test sh /file.sh abc. sh atau / bin / sh akan menjalankannya dengan benar.
Vamsidhar Muggulla
1
Untuk orang lain yang datang ke sini. Trik / usr / bin / env adalah preferensi gaya opsional bukan persyaratan agar ini berfungsi. Juga #! baris menunjukkan penerjemah mana yang akan menggunakan buy default. Sehingga bisa berjalan hanya dengan memanggil script.
Diberi kata
165

dengan skrip ini di file.sh

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

dan ini Dockerfile

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

kamu harus bisa:

% docker build -t test .
% docker run test hello world
Your container args are: hello world
Berawan
sumber
9
Jika Anda lupa "" sekitar "/ file.sh" seperti yang saya lakukan, itu tidak akan berhasil.
kev
6
untuk beberapa alasan, ini tidak bekerja denganENTRYPOINT ./file.sh
phil294
6
Jangan lupa chmod +x file.shuntuk mengatur bendera yang dapat dieksekusi.
topskip
1
@ kev, apakah Anda tahu mengapa seperti itu? apa perbedaan antara ["/file.sh"]dan /file.shatau bahkan[/file.sh]
Nitzankin
1
@Nitzankin lihat jawaban saya untuk mengapa format json yang tepat diperlukan.
BMitch
60

Dengan Docker, cara yang tepat untuk meneruskan informasi semacam ini adalah melalui variabel lingkungan.

Jadi dengan Dockerfile yang sama, ubah skrip menjadi

#!/bin/bash
echo $FOO

Setelah membangun, gunakan perintah buruh pelabuhan berikut:

docker run -e FOO="hello world!" test
Michael
sumber
20
Mengapa ini jawaban tertinggi? Env vars adalah cara lain untuk menyampaikan info, tetapi bukan itu yang diminta OP. Dan tentu saja sama sekali tidak ada yang tidak pantas tentang keinginan OP untuk memberikan args ke wadah.
Sebagian Berawan
8
@PartlyCloudy Saya pikir orang-orang seperti ini karena ingin memberikan jawaban yang "tepat", meskipun jelas salah. Prinsip desain utama Docker adalah memprioritaskan dogma daripada akal sehat.
augurar
1
@ augurar: Untuk meningkatkan jawaban ini, mungkin Anda menjelaskan mengapa menurut Anda jawaban ini "jelas salah"?
Emil Stenström
2
Ada banyak masalah XY yang ditanyakan pada SO. Karena OP menyatakan mereka baru di Docker, sangat masuk akal jika jawaban menunjukkan cara yang disarankan untuk mencapai tujuan. Dengan demikian menjadikan ini jawaban yang bagus.
colm.anseo
1
Cara paling sederhana untuk menyelesaikan pekerjaan alih-alih meneruskan variabel sebagai build args dan semua kekacauan itu. Sangat berguna untuk meneruskan rahasia sebagai variabel lingkungan.
Arvind Sridharan
32

Ada beberapa hal yang berinteraksi di sini:

  1. docker run your_image arg1 arg2akan mengganti nilai CMDdengan arg1 arg2. Itu pengganti CMD penuh, tidak menambahkan nilai lebih untuk itu. Inilah sebabnya mengapa Anda sering melihat docker run some_image /bin/bashuntuk menjalankan bash shell di wadah.

  2. Ketika Anda memiliki nilai ENTRYPOINT dan CMD yang ditentukan, buruh pelabuhan memulai wadah dengan menggabungkan keduanya dan menjalankan perintah yang digabungkan itu. Jadi, jika Anda menetapkan entry point Anda menjadi file.sh, Anda sekarang dapat menjalankan wadah dengan args tambahan yang akan diteruskan sebagai args file.sh.

  3. Entrypoint dan Perintah di buruh pelabuhan memiliki dua sintaks, sintaks string yang akan meluncurkan shell, dan sintaks json yang akan melakukan exec. Shell berguna untuk menangani hal-hal seperti pengalihan IO, merantai beberapa perintah secara bersamaan (dengan hal-hal seperti&& ), substitusi variabel, dll. Namun, shell tersebut menghalangi penanganan sinyal (jika Anda pernah melihat penundaan 10 detik untuk berhenti sebuah wadah, ini sering penyebabnya) dan dengan menggabungkan titik masuk dan perintah bersama. Jika Anda mendefinisikan entrypoint Anda sebagai string, itu akan berjalan /bin/sh -c "file.sh", yang mana saja tidak masalah. Tetapi jika Anda memiliki perintah yang didefinisikan sebagai string juga, Anda akan melihat sesuatu seperti /bin/sh -c "file.sh" /bin/sh -c "arg1 arg2"perintah yang diluncurkan di dalam wadah Anda, tidak begitu bagus. Lihat tabel di sini untuk informasi lebih lanjut tentang bagaimana dua opsi ini berinteraksi

  4. -cOpsi shell hanya membutuhkan satu argumen. Segala sesuatu setelah itu akan diteruskan sebagai $1,, $2dll, ke argumen tunggal itu, tetapi tidak ke dalam skrip shell tertanam kecuali Anda secara eksplisit melewati argumen. Yaitu/bin/sh -c "file.sh $1 $2" "arg1" "arg2" akan bekerja, tetapi /bin/sh -c "file.sh" "arg1" "arg2"tidak file.shakan karena akan dipanggil tanpa argumen.

Secara keseluruhan, desain umum adalah:

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

Dan Anda kemudian menjalankannya dengan:

docker run your_image arg1 arg2

Ada detail yang lebih adil tentang ini di:

BMitch
sumber
1
Saya telah bereksperimen dengan menetapkan ["bash", "--login", "-c"]titik masuk saya untuk mendapatkan / etc / source profile di dalam gambar, tetapi kemudian bertanya-tanya mengapa tidak ada args yang diteruskan ke skrip shell yang diteruskan ke docker run ... Jawaban Anda menjelaskan, terima kasih !
Apteryx
23

Apa yang saya miliki adalah file skrip yang benar-benar menjalankan sesuatu. File skrip ini mungkin relatif rumit. Sebut saja "run_container". Script ini mengambil argumen dari baris perintah:

run_container p1 p2 p3

Run_container sederhana mungkin:

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

Yang ingin saya lakukan adalah, setelah "meredam" ini saya ingin dapat memulai wadah ini dengan parameter pada baris perintah buruh pelabuhan seperti ini:

docker run image_name p1 p2 p3

dan minta script run_container dijalankan dengan p1 p2 p3 sebagai parameter.

Ini solusi saya:

Dockerfile:

FROM docker.io/ubuntu
ADD run_container /
ENTRYPOINT ["/bin/bash", "-c", "/run_container \"$@\"", "--"]
jkh
sumber
7
Mengganti nilai ketiga dalam ENTRYPOINTarray dengan "/run_container \"$@\""cara argumen yang berisi spasi ditangani dengan benar (misalnya docker run image_name foo 'bar baz' quux).
davidchambers
Setelah menambahkan pernyataan sakelar / kasus ke file bash saya, ENTRYPOINT ["run_container.sh"] tidak lagi berfungsi untuk saya, tetapi ENTRYPOINT ["sh", "-c", "run_container.sh"] tidak akan lagi menerima parameter saya. Solusi ini (dengan saran @davidchambers) bekerja untuk saya.
rhamilton
10

Jika Anda ingin menjalankannya @build time:

CMD /bin/bash /file.sh arg1

jika Anda ingin menjalankannya @ run time:

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

Kemudian di shell host

docker build -t test .
docker run -i -t test
Gilles Quenot
sumber
2
ENTRYPOINTadalah jawaban yang baik untuk OP yang saya pikir ingin runtime, tetapi jika Anda benar-benar ingin membangun variabel waktu, jawaban ini hanya rusak. Gunakan ARGdan docker build --build-arg docs.docker.com/engine/reference/builder/#arg
greg.kindel
0

Pilihan lain...

Untuk membuatnya bekerja

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

di dockerfile

ENTRYPOINT ["/entrypoint.sh"]

di 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
sumber
beberapa komentar dan batasan .... perintah dengan ":" perlu perubahan dalam cut -d ':' dan perintah seperti buruh pelabuhan jalankan -d --rm $ IMG_NAME "bash: echo $ PATH" akan menampilkan nilai jalur host alih-alih tuan rumah satu
wagnermarques