Berbagai perintah dalam direktif Docker CMD

39

Tidak mengerti apa yang terjadi ketika saya mencoba menjalankan dua perintah pada saat runtime melalui CMD directive di `Dockerfile. Saya berasumsi bahwa ini akan berhasil:

CMD ["/etc/init.d/nullmailer", "start", ";", "/usr/sbin/php5-fpm"]

Tapi itu tidak berhasil. Kontainer belum dimulai. Jadi saya harus melakukannya seperti ini:

CMD ["sh", "-c", "/etc/init.d/nullmailer start ; /usr/sbin/php5-fpm"]

Saya tidak mengerti. Mengapa demikian? Mengapa baris pertama bukan jalan yang benar? Adakah yang bisa menjelaskan hal-hal "CMD shell format vs JSON, dll" ini. Dengan kata-kata sederhana.

Hanya untuk dicatat - sama dengan command:direktif docker-compose.yml, seperti yang diharapkan.

Vladan
sumber

Jawaban:

33

Saya percaya perbedaan mungkin ada hubungannya dengan, perintah kedua tidak memproses shell, sedangkan yang pertama tidak. Per dokumentasi resmi , ada execdan shellbentuk, perintah pertama Anda adalah formulir exec dan itu tidak misalnya memperluas variabel lingkungan, sedangkan yang kedua tidak. Jadi ada kemungkinan bahwa, dengan menggunakan formulir exec, perintah mungkin gagal karena ketergantungannya pada pemrosesan shell. Anda dapat memeriksa ini dengan menjalankandocker logs CONTAINERID

Perintah kedua Anda, formulir shell, setara dengan -

CMD /etc/init.d/nullmailer start ; /usr/sbin/php5-fpm

Kutipan dari dokumentasi -

Catatan: Berbeda dengan form shell, form exec tidak memanggil shell perintah. Ini berarti pemrosesan shell yang normal tidak terjadi. Misalnya, CMD [ "echo", "$HOME" ]tidak akan melakukan penggantian variabel pada $HOME. Jika Anda ingin pengolahan shell maka baik menggunakan bentuk shell atau mengeksekusi shell langsung, misalnya: CMD [ "sh", "-c", "echo", "$HOME" ].

Daniel t.
sumber
Mungkin perintah gagal karena variabel lingkungan. Haruskah saya masih menggunakan execformulir ini , karena ini adalah formulir yang disukai? Mengapa lebih disukai? Atau haruskah saya menggunakan shellformulir yang lebih sederhana ?
Vladan
Gagal karena menjalankan satu perintah demi satu adalah fungsi shell. Variabel lingkungan adalah herring merah.
Bryan
Jika Anda menjalankan beberapa layanan di Docker, saya akan merekomendasikan menggunakan manajer proses seperti penyelia. Dengan begitu Anda hanya meluncurkan supervisord di bawah bagian CMD dan itu akan mengurus memulai layanan. Anda dapat memeriksa detailnya di sini - docs.docker.com/articles/using_supervisord
Daniel t.
Ini adalah artikel yang tepat yang baru saja saya baca :) Terima kasih.
Vladan
Saya masih tidak mengerti mengapa Anda perlu melakukannya CMD [ "sh", "-c", "echo", "$HOME"]. Kenapa tidak CMD ["sh", "-c", "echo $HOME"]atau, dalam hal ini CMD ["sh -c echo $HOME"],?
sixty4bit
4

Jangan menyulitkan diri sendiri. Cukup buat file bash "start.sh":

#!/bin/bash

/usr/bin/command2 param1
/usr/bin/commnad1

di Dockerfile Anda lakukan:

ADD start.sh /
RUN chmod +x /start.sh

CMD ["/start.sh"]
Manusia Digital
sumber
2

Sintaks json dari CMD(dan RUNdan ENTRYPOINT) meneruskan argumen ke kernel secara langsung sebagai syscall exec. Tidak ada pemisahan perintah dari argumen dengan spasi, melarikan diri dari kutipan, pengalihan IO, substitusi variabel, perpipaan antara perintah, menjalankan beberapa perintah, dll, dalam syscall exec. Syscall hanya membutuhkan executable untuk dijalankan dan daftar argumen untuk diteruskan ke executable itu, dan menjalankannya.

Karakter suka $memperluas variabel, ;untuk memisahkan perintah, (spasi) untuk memisahkan argumen, &&dan ||untuk rantai perintah, >untuk pengalihan output, |untuk menyalurkan antar perintah, dll, semua fitur dari shell dan memerlukan sesuatu seperti /bin/shatau /bin/bashuntuk menafsirkan dan mengimplementasikannya.


Jika Anda beralih ke sintaks string CMD, buruh pelabuhan akan menjalankan perintah Anda dengan sebuah shell:

CMD /etc/init.d/nullmailer start ; /usr/sbin/php5-fpm

Jika tidak, sintaks kedua Anda melakukan hal yang persis sama:

CMD ["sh", "-c", "/etc/init.d/nullmailer start ; /usr/sbin/php5-fpm"]

Perhatikan bahwa saya tidak merekomendasikan menjalankan banyak perintah dengan cara ini di dalam sebuah wadah karena tidak ada kesalahan penanganan jika perintah pertama Anda gagal, terutama jika itu berjalan di latar belakang. Anda juga membiarkan cangkang berjalan sebagai pid 1 di dalam wadah yang akan merusak penanganan sinyal, menghasilkan penundaan 10 detik dan pembunuhan yang tidak berterima terhadap kontainer oleh buruh pelabuhan. Penanganan sinyal dapat dikurangi dengan menggunakan execperintah shell :

CMD /etc/init.d/nullmailer start ; exec /usr/sbin/php5-fpm

Namun, proses penanganan gagal secara diam-diam di latar belakang mengharuskan Anda beralih ke beberapa jenis manajer multi-proses seperti pengawas, atau lebih baik memecah aplikasi Anda menjadi beberapa wadah dan menyebarkannya dengan sesuatu seperti komposisi buruh pelabuhan.

BMitch
sumber
1

Saya kira perintah pertama gagal karena dalam bentuk DOCKER CMD, hanya parameter pertama yang dieksekusi, sisanya dimasukkan ke dalam perintah ini.

Bentuk kedua berfungsi karena semua perintah dipisahkan dengan ";" dimasukkan ke dalam perintah sh, yang mengeksekusi mereka.

devrimbaris
sumber
1

Saya tidak berpikir Anda harus meletakkan semi koma setelah "mulai"

alih-alih menggunakan

CMD ["/etc/init.d/nullmailer", "start", ";", "/usr/sbin/php5-fpm"]

mencoba

CMD ["/etc/init.d/nullmailer", "start", "/usr/sbin/php5-fpm"]

karena buruh pelabuhan menggunakan "sh -c", perintah di atas akan dieksekusi seperti di bawah ini

/etc/init.d/nullmailer start
/etc/init.d/nullmailer /usr/sbin/php5-fpm
vedat
sumber
Sintaks json tidak menjalankan perintah dengan shell, tidak ada sh -cdalam skenario itu.
BMitch