Cara buka shell interaktif dalam Docker Compose
Gunakan docker compose exec untuk masuk ke kontena yang sedang berjalan tanpa mengganggu proses utama. Jika servis mati, gunakan docker compose run --rm sebagai ganti.
Dapatkan shell interaktif dengan docker compose exec
docker compose exec web bash membuka shell interaktif di dalam kontena yang sedang berjalan sebagai servis web. Nama selepas exec ialah nama servis daripada compose.yaml anda, bukannya nama kontena. Jika imej tersebut tidak mempunyai bash, minta sh sebagai ganti.
docker compose ps
docker compose exec web bashJalankan docker compose ps terlebih dahulu. Ia sepatutnya menyenaraikan web dengan status running. Kemudian, arahan kedua meletakkan anda pada prompt di dalam kontena, dan exit atau Ctrl-D akan mengembalikan anda ke hos. Servis tersebut terus berjalan selepas anda keluar, kerana exec memulakan proses kedua di samping proses utama. Menutup shell anda tidak menjejaskan PID 1 (process id 1), iaitu proses yang dibina untuk dijalankan oleh kontena tersebut.
Itu adalah salah satu daripada dua cara untuk masuk. exec menyertai kontena yang sudah wujud. docker compose run mencipta kontena baharu daripada definisi servis yang sama. Hampir semua perkara lain dalam panduan ini berpunca daripada perbezaan tunggal tersebut.
Mengapa -it adalah pilihan dalam Compose tetapi wajib dengan docker biasa
Dua flag mengawal bahagian interaktif sesuatu sesi. -i memastikan stdin sentiasa terbuka, supaya apa yang anda taip sampai kepada proses tersebut. -t memperuntukkan pseudo terminal, yang dipanggil TTY, supaya shell memaparkan prompt dan mengendalikan kekunci anak panah. docker exec biasa membiarkan kedua-duanya dimatikan secara lalai, itulah sebabnya setiap contoh yang anda lihat menulis docker exec -it. docker compose exec menghidupkan kedua-duanya untuk anda, jadi docker compose exec -it web bash dan docker compose exec web bash melakukan perkara yang sama. Compose masih menerima -it supaya memori otot lama anda terus berfungsi.
Anda akan menyedari ketiadaan TTY dalam beberapa saat. Shell berjalan, tetapi ia tidak memaparkan prompt dan Ctrl-C tidak pernah sampai kepada proses tersebut. Kes sebaliknya, di mana anda perlu meminta Compose supaya tidak memperuntukkan TTY, mempunyai flag sendiri dan bahagiannya sendiri di bawah.
Apa yang perlu dilakukan apabila imej tidak mempunyai bash
Meminta bash daripada imej berasaskan Alpine dan exec gagal seperti ini:
OCI runtime exec failed: exec failed: unable to start container process: exec: "bash": executable file not found in $PATH: unknownMesej tersebut bukan masalah exec. Ia menyatakan bahawa binari yang anda minta tiada dalam imej tersebut. Alpine membekalkan BusyBox, yang menyediakan ash sebagai /bin/sh dan tiada bash langsung, jadi minta sh:
docker compose exec web shImej berasaskan Debian dan Ubuntu, termasuk tag -slim, memang membawa bash, dan bash memberikan anda sejarah arahan serta pelengkapan yang lebih baik. Jadi cuba bash dahulu dan gunakan sh sebagai pilihan terakhir. sh wujud dalam hampir setiap imej kegunaan am.
Sesetengah imej tidak mempunyai shell langsung. Imej distroless dan imej yang dibina FROM scratch hanya mengandungi binari aplikasi serta pustakanya dan tiada benda lain, secara sengaja, kerana shell yang tiada tidak boleh digunakan untuk menyerang anda. Dalam imej tersebut, sh gagal dengan mesej yang sama dan tiada lagi yang boleh dicuba. Dua pendekatan boleh digunakan. Imej distroless Google menerbitkan tag :debug yang menambah shell BusyBox, jadi menukar tag buat sementara waktu membolehkan anda masuk. Atau mulakan kontena berasingan di dalam namespace sasaran:
CID=$(docker compose ps -q web)
docker run --rm -it --network "container:$CID" --pid "container:$CID" nicolaka/netshootAnda kini mempunyai alatan netshoot yang dihalakan ke rangkaian aplikasi tersebut, jadi curl localhost:8080 dan ss -lntp berkelakuan seolah-olah anda berada di dalamnya. Sistem fail yang anda lihat adalah milik netshoot, bukan milik aplikasi. Oleh kerana namespace proses dikongsi, ls /proc/1/root/ boleh mencapai fail sasaran itu sendiri apabila anda adalah root.
Apabila servis tidak berjalan, gunakan docker compose run --rm
exec memerlukan kontena yang sedang berjalan. Jika anda menghalakannya kepada servis yang telah berhenti, ia akan menolak:
service "web" is not runningIa tidak akan memulakan apa-apa untuk anda. docker compose run akan melakukannya:
docker compose run --rm web bashrun mencipta kontena baharu daripada definisi servis web, dengan imej, persekitaran, volum dan rangkaian yang sama, serta menggantikan arahan servis tersebut dengan arahan yang anda taip. --rm memadamkan kontena itu apabila anda keluar. Jika anda membiarkan --rm tidak digunakan, sisa kontena akan terkumpul di bawah nama seperti myproject-web-run-4f1c2b, yang boleh dilihat melalui docker compose ps -a dan tiada proses lain yang akan membersihkannya.
Dua kelakuan run sering mengejutkan pengguna. Ia tidak menerbitkan port servis melainkan anda menambah --service-ports, dan ini dilakukan dengan sengaja: kontena kedua yang mengikat port hos 8080 semasa kontena pertama masih memegangnya akan gagal dengan bind: address already in use. Ia juga memulakan semua yang disenaraikan oleh servis di bawah depends_on sebelum shell anda muncul, jadi pemeriksaan pantas di dalam mungkin memulakan pangkalan data dan cache. --no-deps melangkau proses tersebut.
run melalui ENTRYPOINT imej, manakala exec tidak. exec memulakan arahan anda secara terus di dalam kontena sedia ada, jadi skrip entrypoint tidak akan melihatnya. Di bawah run, bash anda sampai sebagai argumen kepada skrip tersebut. Banyak imej rasmi menamatkan entrypoint mereka dengan exec "$@", jadi ia melepasi secara terus dan anda mendapat shell anda. Skrip yang mentafsir argumennya sendiri akan melakukan perkara lain dengannya, dan dalam kes itu anda perlu menggantikan entrypoint untuk run tersebut:
docker compose run --rm --entrypoint sh webIni adalah sebab paling biasa mengapa arahan yang berfungsi di bawah exec berkelakuan berbeza di bawah run, dan perbezaan antara command dan entrypoint menjelaskan bahagian konfigurasi imej yang anda gantikan setiap kali.
exec atau run: cara memilih
- exec memerlukan kontena yang sedang berjalan. run tidak memerlukannya, dan run boleh memulakan dependency.
- exec melihat senarai proses yang sedang aktif dan fail seperti keadaan semasa, termasuk apa sahaja yang telah ditulis oleh aplikasi sejak ia bermula. run mendapat salinan imej yang bersih, jadi tiada data tersebut di dalamnya.
- exec melangkau entrypoint. run menjalankannya.
- run meninggalkan kontena di belakang melainkan anda menghantar
--rm.
Gunakan exec untuk melihat apa yang sebenarnya berlaku. Gunakan run --rm untuk salinan sementara bagi persekitaran yang sama, untuk arahan migrasi sekali jalan, atau apabila servis sebenar tidak kekal aktif cukup lama untuk anda melakukan exec ke dalamnya.
Flag exec yang berguna: pengguna, direktori kerja dan replika
Kebanyakan imej bertukar kepada pengguna bukan root, jadi pemasangan alat diagnostik di dalam shell exec anda terhenti di sini:
E: Could not open lock file /var/lib/dpkg/lock-frontend - open (13: Permission denied)-u root memberikan anda shell root dalam bekas yang sama:
docker compose exec -u root web sh-w /srv/app menetapkan direktori kerja untuk arahan itu sahaja. -e KEY=value menambah pemboleh ubah persekitaran pada sesi anda dan bukan pada servis tersebut. Apabila sesuatu servis menjalankan lebih daripada satu replika, --index 2 menentukan bekas mana yang anda masuki. Jika pemilikan fail pada direktori yang dilekap (mounted) adalah perkara yang anda cari, PUID dan PGID dalam imej bekas merangkumi sebab mengapa ID berangka, bukan nama pengguna, menentukan siapa yang boleh menulis di sana.
Dapatkan shell psql atau mysql di dalam container pangkalan data
Client sudah pun berada di dalam imej pangkalan data, jadi anda tidak perlukan client pada host dan anda tidak perlu menerbitkan port tersebut:
docker compose exec db psql -U postgres -d app
docker compose exec db mariadb -u root -pImej Postgres mengandungi psql, imej MySQL mengandungi mysql, dan imej MariaDB mengandungi mariadb. Sambungan dibuat dari dalam container, jadi kaedah ini berfungsi walaupun fail compose tidak menerbitkan sebarang port pangkalan data. Ini adalah aturan yang lebih selamat: tiada apa-apa di internet yang boleh mencapai port yang tidak pernah anda terbitkan.
Satu perangkap sering membuang masa pengguna. Shell anda mengembangkan pemboleh ubah (variable) pada host sebelum Docker melihat arahan tersebut, jadi -U "$POSTGRES_USER" akan menghantar rentetan kosong apabila pemboleh ubah itu hanya wujud di dalam container. Tanda petik tunggal dan shell di dalam container akan mengembangkannya di tempat yang betul:
docker compose exec db sh -c 'psql -U "$POSTGRES_USER" -d "$POSTGRES_DB"'Jangan gunakan docker compose run --rm db tanpa arahan di sini. Tindakan itu akan memulakan pelayan Postgres kedua pada volume data yang sama, dan ia akan menolak untuk bermula:
FATAL: lock file "postmaster.pid" already existsFail kunci (lock file) menjalankan fungsinya, kerana dua pelayan yang menulis pada satu direktori data akan merosakkannya. Semasa pangkalan data sedang berjalan, lakukan exec ke dalam container yang sedang aktif. Sama ada pangkalan data perlu diletakkan dalam Compose atau tidak adalah keputusan yang berasingan, dan menjalankan pangkalan data dalam Docker atau pada host menjelaskan perbandingan tersebut.
Servis yang memerlukan konsol semasa permulaan: stdin_open dan tty
exec dan run meliputi shell yang anda buka secara manual. Servis yang proses utamanya bersifat interaktif memerlukan dua kunci dalam fail compose:
services:
console:
image: python:3.12-slim
command: python
stdin_open: true
tty: truestdin_open: true ialah docker run -i dan tty: true ialah docker run -t. Tanpanya, kontena akan bermula dan terus keluar dengan kod 0, dan docker compose ps -a akan menunjukkan Exited (0). Tiada apa-apa yang terhempas. python tanpa terminal pada stdin akan membaca penghujung fail (end of file) dengan serta-merta dan berhenti secara normal, yang merupakan kelakuan betul bagi program yang tidak menerima input daripada pengguna.
Dengan kedua-dua kunci ditetapkan, lampirkan (attach) pada proses yang sedang berjalan:
docker attach $(docker compose ps -q console)Nyahlampir (detach) dengan menekan Ctrl-P kemudian Ctrl-Q, yang akan membiarkan proses terus berjalan. Urutan tersebut hanya berfungsi apabila kontena mempunyai kedua-dua TTY dan stdin yang terbuka. Sebaliknya, Ctrl-C akan menghantar isyarat interrupt kepada PID 1 dan menghentikan servis tersebut.
Biarkan kedua-dua kunci tidak ditetapkan untuk servis biasa. Pelayan web tidak pernah membaca stdin, dan tty: true menyebabkan banyak program bertukar kepada output berwarna dan penimbalan baris (line buffering) kerana ia menganggap terdapat manusia yang sedang memerhati, yang akan memenuhi docker compose logs dengan kod escape.
Mengapa exec berskrip gagal dalam cron dan CI: flag -T
Perintah exec yang berfungsi dalam terminal anda gagal di dalam kerja cron atau pelari continuous integration (CI):
the input device is not a TTYCompose meminta pseudo terminal secara lalai, manakala cron tidak memberikan terminal kepada kerja tersebut, jadi permintaan itu gagal sebelum perintah anda sempat dijalankan. -T mematikan permintaan tersebut:
0 3 * * * docker compose -f /srv/app/compose.yaml exec -T db pg_dump -U postgres -Fc app > /srv/backups/app.dump-T penting atas sebab kedua. TTY menulis semula aliran bait (byte stream) semasa proses keluar, jadi dump termampat yang melaluinya akan sampai dalam keadaan rosak. Sebarang output yang diubah hala atau disalurkan (piped) memerlukan -T.
Dua lagi perincian mengenai cron. Sertakan -f dengan laluan mutlak (absolute path), kerana cron menjalankan kerja tersebut dari direktori home yang tidak mempunyai fail compose, dan Compose kemudian berhenti dengan no configuration file provided: not found. Selain itu, exec mengembalikan kod keluar (exit code) bagi perintah yang dijalankannya, jadi pg_dump yang gagal akan menyebabkan skrip anda gagal di bawah set -e dan bukannya menulis sandaran kosong serta melaporkan kejayaan. Selebihnya perintah harian dikumpulkan dalam helaian nota perintah Compose yang wajar disimpan bersama skrip tersebut.
Mengapa perubahan yang anda buat di dalam container hilang
Anda memasang alatan menggunakan exec, menyunting fail konfigurasi, membaiki masalah tersebut, dan seminggu kemudian pembaikan itu hilang. Ini adalah lapisan boleh tulis (writable layer) container yang berfungsi mengikut reka bentuknya. docker compose up -d selepas sebarang perubahan pada tag imej atau definisi servis akan memusnahkan container lama dan membina yang baharu daripada imej tersebut, dan setiap suntingan manual akan hilang bersama container lama.
docker compose restart adalah berbeza. Ia menghenti dan memulakan container yang sama, jadi suntingan manual akan kekal. Itulah sebabnya pembaikan manual boleh kelihatan bertahan selama berminggu-minggu dan kemudian hilang semasa kemas kini yang tidak berkaitan. Named volumes dan bind mounts akan bertahan dalam kedua-dua operasi tersebut, kerana datanya berada di luar container, dan bind mounts dan named volumes menerangkan yang mana satu perlu dipilih untuk data yang anda ingin simpan.
Oleh itu, anggap shell exec sebagai tempat untuk membaca dan menguji. Sebaik sahaja anda mengetahui pembaikan tersebut, tulis ia di tempat yang akan mengekalkannya: pakej ke dalam Dockerfile, tetapan ke dalam fail compose. Kemudian docker compose up -d untuk melaksanakannya, dan sahkan dengan exec lain bahawa container baharu benar-benar memilikinya.
FAQ
Apakah perbezaan antara docker compose exec dan docker compose run?
exec menjalankan arahan di dalam kontena yang sedang berjalan, di samping proses utama, dan ia melangkau entrypoint imej. run mencipta kontena baharu daripada definisi servis yang sama dengan imej, persekitaran, volum dan rangkaian yang sama, menghantar arahan anda melalui entrypoint, dan memulakan sebarang servis depends_on terlebih dahulu. run juga tidak menerbitkan port servis melainkan anda menambah --service-ports. Gunakan exec untuk memeriksa servis yang sedang aktif. Gunakan run --rm apabila servis dihentikan atau apabila anda tidak mahu mengganggu servis tersebut.
Mengapa docker compose exec melaporkan bahawa servis tidak berjalan?
exec melekat pada kontena sedia ada dan tidak boleh mencipta kontena baharu, jadi servis yang dihentikan atau terhenti secara tidak sengaja akan memberikan service "web" is not running. Semak docker compose ps -a, yang menyenaraikan kontena yang telah keluar dengan status seperti Exited (1), dan baca docker compose logs web untuk mengetahui sebab ia berhenti. Untuk mendapatkan shell juga, jalankan docker compose run --rm --entrypoint sh web. Ini membina kontena baharu daripada definisi servis yang sama tanpa membenarkan arahan mula yang rosak dijalankan.
Bagaimanakah cara untuk membuka shell apabila imej tidak mempunyai bash?
docker compose exec web bash gagal dengan exec: "bash": executable file not found in $PATH bermakna bash tiada dalam imej tersebut, yang merupakan perkara biasa bagi mana-mana imej yang dibina berasaskan Alpine. Gunakan docker compose exec web sh, kerana BusyBox menyediakan /bin/sh. Imej Distroless dan scratch tidak mengandungi shell langsung, jadi tiada arahan exec akan berfungsi. Tukar kepada tag :debug imej tersebut jika penerbit menyediakannya, atau mulakan kontena nyahpepijat dalam namespace sasaran dengan docker run --rm -it --network "container:$CID" --pid "container:$CID" nicolaka/netshoot, di mana $CID diperoleh daripada docker compose ps -q web.
Mengapa arahan exec saya gagal dengan "the input device is not a TTY" dalam cron?
docker compose exec meminta pseudo terminal secara lalai dan cron tidak menyediakannya, jadi permintaan tersebut gagal sebelum arahan anda dijalankan. Tambah -T untuk mematikannya: docker compose exec -T db pg_dump -U postgres app. Gunakan -T untuk sebarang output yang diubah hala atau disalurkan (piped) juga, kerana TTY mengubah aliran bait dan merosakkan dump binari. Dalam cron, pastikan juga anda menghantar -f dengan laluan mutlak ke fail compose anda, atau Compose akan keluar dengan no configuration file provided: not found.
Adakah perubahan yang saya buat di dalam kontena dengan exec kekal selepas but semula?
Perubahan tersebut kekal selepas docker compose restart, yang menggunakan semula kontena yang sama. Perubahan tersebut akan hilang selepas docker compose up -d berikutan sebarang perubahan imej atau konfigurasi, kerana ia mencipta semula kontena daripada imej dan membuang lapisan boleh tulisnya. Data yang ditulis ke dalam volum bernama atau bind mounts akan kekal dalam kedua-dua keadaan, kerana ia berada di luar kontena. Lakukan perubahan diagnostik dengan exec, kemudian masukkan versi kekal ke dalam Dockerfile atau fail compose.