Docker Compose exec ile container icinde shell acma
Calisan bir Docker Compose servisine docker compose exec -it [servis] bash komutuyla nasil baglanacaginizi ogrenin. Durdurulmus servisler icin run --rm kullanimini inceleyin.
docker compose exec ile etkileşimli bir kabuk elde etme
docker compose exec web bash, hâlihazırda web servisi olarak çalışan container içerisinde etkileşimli bir kabuk açar. exec ifadesinden sonra gelen isim, container adı değil, compose.yaml dosyanızdaki servis adıdır. Eğer imaj içerisinde bash bulunmuyorsa, bunun yerine sh komutunu deneyin.
docker compose ps
docker compose exec web bashÖnce docker compose ps komutunu çalıştırın. Bu komut, running durumundaki web öğelerini listelemelidir. Ardından ikinci komut sizi container içerisinde bir komut satırına ulaştırır; exit veya Ctrl-D tuş kombinasyonu sizi ana makineye geri döndürür. exec komutu ana sürecin yanında ikinci bir süreç başlattığı için, siz çıktıktan sonra servis çalışmaya devam eder. Kabuğu kapatmak, container'ın çalışması için oluşturulduğu PID 1 (süreç kimliği 1) sürecine müdahale etmez.
Bu, içerideki sisteme erişmenin iki yolundan biridir. exec, hâlihazırda var olan bir container'a katılır. docker compose run ise aynı servis tanımından yeni bir container oluşturur. Bu kılavuzdaki hemen hemen her şey, bu temel farktan türetilmiştir.
Compose'da -it neden isteğe bağlı, ancak düz docker ile neden zorunludur
Etkileşimli bir oturumun parçalarını iki bayrak kontrol eder. -i, stdin'i açık tutar; böylece yazdıklarınız sürece ulaşır. -t, TTY adı verilen bir sözde terminal ayırır; böylece kabuk bir komut istemi yazdırır ve ok tuşlarını işler. Düz docker exec, varsayılan olarak her ikisini de kapalı bırakır; bu nedenle gördüğünüz her örnekte docker exec -it yazılır. docker compose exec, her ikisini de sizin için açar; bu nedenle docker compose exec -it web bash ve docker compose exec web bash aynı işi yapar. Compose, -it bayrağını kabul etmeye devam eder, böylece eski alışkanlıklarınız çalışmaya devam eder.
Eksik bir TTY'yi saniyeler içinde fark edersiniz. Kabuk çalışır ancak hiçbir komut istemi yazdırmaz ve Ctrl-C tuşları sürece asla ulaşmaz. Compose'dan bir TTY ayırmamasını istemeniz gereken ters durumun kendi bayrağı ve aşağıda kendi bölümü vardır.
İmajda bash bulunmadığında ne yapılmalı
Alpine tabanlı bir imajdan bash isterseniz ve exec şu şekilde başarısız olursa:
OCI runtime exec failed: exec failed: unable to start container process: exec: "bash": executable file not found in $PATH: unknownBu mesaj bir exec sorunu değildir. İmaj içerisinde istediğiniz ikili dosyanın bulunmadığını belirtir. Alpine, ash komutunu /bin/sh olarak sağlayan BusyBox ile gelir ve içerisinde bash bulunmaz; bu nedenle sh kullanın:
docker compose exec web sh-slim etiketleri dahil olmak üzere Debian ve Ubuntu tabanlı imajlar bash içerir; bash size komut geçmişi ve daha iyi tamamlama özellikleri sunar. Bu yüzden önce bash deneyin, başarısız olursa sh komutuna geçin. sh neredeyse her genel amaçlı imajda bulunur.
Bazı imajlarda hiçbir kabuk bulunmaz. Distroless imajlar ve FROM scratch ile oluşturulan imajlar, uygulama ikili dosyasını ve kütüphanelerini barındırır; başka hiçbir şey içermezler. Bu kasıtlıdır çünkü var olmayan bir kabuk size karşı kullanılamaz. Bu imajlarda sh aynı mesajla başarısız olur ve denenecek başka bir şey kalmaz. İki yöntem işe yarar. Google'ın distroless imajları, BusyBox kabuğu ekleyen :debug etiketlerini yayınlar; bu nedenle etiketi geçici olarak değiştirmek içeri girmenizi sağlar. Alternatif olarak, hedefin namespace'leri içerisinde ayrı bir container başlatın:
CID=$(docker compose ps -q web)
docker run --rm -it --network "container:$CID" --pid "container:$CID" nicolaka/netshootArtık netshoot araçlarını uygulamanın ağına yönlendirdiniz; bu sayede curl localhost:8080 ve ss -lntp sanki uygulamanın içindeymişsiniz gibi davranır. Gördüğünüz dosya sistemi uygulamaya değil, netshoot'a aittir. Süreç namespace'i paylaşıldığı için, root yetkisine sahip olduğunuzda ls /proc/1/root/ ile hedefin kendi dosyalarına erişebilirsiniz.
Servis çalışmadığında docker compose run --rm kullanın
exec komutu çalışan bir container gerektirir. Durdurulmuş bir servise işaret ettiğinde işlem reddedilir:
service "web" is not runningBu komut sizin için hiçbir şeyi başlatmaz. docker compose run ise başlatacaktır:
docker compose run --rm web bashrun komutu, web servis tanımını kullanarak aynı imaj, ortam değişkenleri, volume ve ağ yapılandırmasıyla yeni bir container oluşturur ve servis komutunu sizin yazdığınız komutla değiştirir. --rm, çıktığınızda bu container'ı siler. --rm bayrağını kullanmazsanız, geriye kalanlar myproject-web-run-4f1c2b gibi isimlerle birikir; bunları docker compose ps -a ile görebilirsiniz ve başka hiçbir süreç bunları temizlemez.
run komutunun iki davranışı kullanıcıları şaşırtır. --service-ports eklemediğiniz sürece servis portlarını yayınlamaz; bu kasıtlıdır: ilk container 8080 numaralı ana makine portunu tutarken ikinci bir container aynı porta bağlanmaya çalışırsa bind: address already in use hatasıyla başarısız olur. Ayrıca, shell ekranınız gelmeden önce servisin depends_on altında listelediği her şeyi başlatır; bu nedenle içeride hızlı bir inceleme yapmak, bir veritabanını ve önbelleği başlatabilir. --no-deps bu adımı atlar.
run komutu imajın ENTRYPOINT değerini işler, exec ise işlemez. exec komutu, komutunuzu doğrudan mevcut container içinde başlatır, bu nedenle entrypoint betiği komutu görmez. run altında ise bash değeriniz, o betiğe argüman olarak iletilir. Birçok resmi imaj, entrypoint betiğini exec "$@" ile bitirir; böylece komut doğrudan geçer ve shell ekranına ulaşırsınız. Kendi argümanlarını yorumlayan bir betik ise bunları farklı şekilde işleyecektir; bu durumda o çalıştırma için entrypoint değerini değiştirmeniz gerekir:
docker compose run --rm --entrypoint sh webexec altında çalışan bir komutun run altında farklı davranmasının en yaygın nedeni budur ve komut ve entrypoint arasındaki ayrım, her seferinde imaj yapılandırmasının hangi yarısını değiştirdiğinizi açıklar.
exec mi run mı: nasıl seçim yapılır
- exec komutu çalışan bir container gerektirir. run komutu gerektirmez ve run bağımlılıkları başlatabilir.
- exec, o anki canlı süreç listesini ve uygulamanın başladığından beri yazdığı dosyalar dahil olmak üzere mevcut dosya durumunu görür. run ise imajın temiz bir kopyasını alır, dolayısıyla bu değişiklikler orada bulunmaz.
- exec, entrypoint kısmını atlar. run ise bunu çalıştırır.
- run,
--rmparametresini kullanmadığınız sürece geride bir container bırakır.
Gerçekte ne olduğunu incelemek için exec kullanın. Aynı ortamın tek seferlik bir kopyası, bir kerelik migration komutları veya gerçek servisin içine exec ile girilemeyecek kadar kısa süre ayakta kaldığı durumlar için run --rm kullanın.
Faydalı exec bayrakları: kullanıcı, çalışma dizini ve kopyalar
Çoğu imaj root olmayan bir kullanıcıya düşer, bu nedenle exec kabuğunuzun içinde bir tanılama aracı kurmak burada durur:
E: Could not open lock file /var/lib/dpkg/lock-frontend - open (13: Permission denied)-u root size aynı container içinde bir root kabuğu sağlar:
docker compose exec -u root web sh-w /srv/app çalışma dizinini yalnızca o komut için ayarlar. -e KEY=value oturumunuza bir ortam değişkeni ekler, ancak bunu servise eklemez. Bir servis birden fazla kopya (replica) çalıştırdığında, --index 2 hangi container içine gireceğinize karar verir. Eğer mount edilmiş bir dizindeki dosya sahipliğinin peşindeyseniz, container imajlarında PUID ve PGID konusu, oraya kimin yazabileceğine kullanıcı adlarının değil, sayısal kimliklerin neden karar verdiğini açıklar.
Veritabanı container'ı içerisinde psql veya mysql kabuğuna erişim
İstemci zaten veritabanı imajının içerisinde yer aldığından, ana makinede bir istemciye ihtiyacınız yoktur ve portu dışarıya açmanız gerekmez:
docker compose exec db psql -U postgres -d app
docker compose exec db mariadb -u root -pPostgres imajları psql, MySQL imajları mysql ve MariaDB imajları mariadb içerir. Bağlantı container içerisinden yapıldığı için, compose dosyası hiçbir veritabanı portunu dışarıya açmasa bile bu yöntem çalışır. Bu daha güvenli bir yapılandırmadır: dışarıya açmadığınız bir porta internet üzerinden kimse erişemez.
Bir tuzak, kullanıcıların vaktini boşa harcamasına neden olur. Komut Docker'a ulaşmadan önce kabuğunuz değişkenleri ana makinede genişletir; bu nedenle -U "$POSTGRES_USER", değişken yalnızca container içerisinde mevcut olduğunda boş bir dizge gönderir. Tek tırnak işaretleri ve container içerisindeki bir kabuk, değişkeni doğru yerde genişletir:
docker compose exec db sh -c 'psql -U "$POSTGRES_USER" -d "$POSTGRES_DB"'Burada komutsuz bir docker compose run --rm db kullanmaya çalışmayın. Bu, aynı veri birimi üzerinde ikinci bir Postgres sunucusu başlatmaya çalışır ve sunucu başlamayı reddeder:
FATAL: lock file "postmaster.pid" already existsKilit dosyası görevini yapmaktadır; çünkü aynı veri dizinine yazan iki sunucu veriyi bozacaktır. Veritabanı çalışır durumdayken, çalışan container'a exec komutu ile giriş yapın. Veritabanının Compose içerisinde yer alıp almaması ayrı bir karardır ve veritabanını Docker'da veya ana makinede çalıştırma konusu bu konudaki tercihleri açıklamaktadır.
Başlangıçta konsola ihtiyaç duyan servisler: stdin_open ve tty
exec ve run komutları, elle açtığınız kabuklar için kullanılır. Ana süreci doğası gereği etkileşimli olan bir servis, compose dosyasında iki anahtara ihtiyaç duyar:
services:
console:
image: python:3.12-slim
command: python
stdin_open: true
tty: truestdin_open: true değeri docker run -i ve tty: true değeri docker run -t olarak ayarlanmalıdır. Bu anahtarlar olmadan container başlar ve hemen 0 koduyla sonlanır; docker compose ps -a üzerinde Exited (0) mesajı görünür. Bir çökme yaşanmamıştır. stdin üzerinde terminali olmayan bir python, dosya sonunu (EOF) hemen okur ve normal şekilde çıkar; bu, başında kimsenin olmadığı bir program için beklenen doğru davranıştır.
Her iki anahtar da ayarlandığında, çalışan sürece şu şekilde bağlanabilirsiniz:
docker attach $(docker compose ps -q console)Ctrl-P ve ardından Ctrl-Q tuşlarına basarak süreci durdurmadan bağlantıyı kesin. Bu tuş dizisi, yalnızca container hem bir TTY'ye sahip olduğunda hem de stdin açık olduğunda çalışır. Bunun yerine Ctrl-C kullanmak, PID 1'e bir kesme sinyali gönderir ve servisi durdurur.
Sıradan servisler için her iki anahtarı da kapalı tutun. Bir web sunucusu asla stdin okumaz; ayrıca tty: true kullanımı, birçok programın bir insan tarafından izlendiğini varsayarak renkli çıktı vermesine ve satır tamponlamasına (line buffering) geçmesine neden olur. Bu durum, docker compose logs dosyasının kaçış kodlarıyla (escape codes) dolmasına yol açar.
Cron ve CI ortamlarında scripted exec neden başarısız olur: -T bayrağı
Terminalinizde çalışan bir exec komutu, bir cron işi veya sürekli entegrasyon (CI) çalıştırıcısı içinde başarısız olur:
the input device is not a TTYCompose varsayılan olarak bir pseudo terminal talep eder, ancak cron işe bir terminal sağlamaz; bu nedenle istek, komutunuz çalışmadan önce başarısız olur. -T bu isteği devre dışı bırakır:
0 3 * * * docker compose -f /srv/app/compose.yaml exec -T db pg_dump -U postgres -Fc app > /srv/backups/app.dump-T ikinci bir nedenden dolayı önemlidir. Bir TTY, çıkış yolundaki bayt akışını yeniden yazar; bu nedenle bir TTY üzerinden geçen sıkıştırılmış bir döküm, hedefe hasarlı ulaşır. Yönlendirilen veya pipe edilen tüm çıktılar için -T gereklidir.
Cron ile ilgili iki detay daha. -f bayrağını mutlak bir yol ile birlikte geçirin; çünkü cron işi, compose dosyasının bulunmadığı ana dizinden çalıştırır ve Compose bu durumda no configuration file provided: not found hatasıyla durur. Ayrıca exec, çalıştırdığı komutun çıkış kodunu döndürür; bu nedenle başarısız olan bir pg_dump, boş bir yedek oluşturup başarılı raporu vermek yerine set -e altında betiğinizi başarısız kılar. Günlük kullanılan diğer komutların geri kalanı, betiklerinizin yanında bulundurmaya değer bir Compose komut kopya kağıdında toplanmıştır.
Konteyner içinde yaptığınız değişiklikler neden kaybolur
Bir aracı exec ile kurar, bir yapılandırma dosyasını düzenler ve sorunu çözersiniz; ancak bir hafta sonra yaptığınız düzeltme kaybolur. Bu, konteynerin yazılabilir katmanının tasarlandığı gibi çalışmasıdır. docker compose up -d, image etiketi veya servis tanımındaki herhangi bir değişiklikten sonra eski konteyneri yok eder ve image üzerinden yenisini oluşturur; bu durumda elle yapılan tüm düzenlemeler eski konteyner ile birlikte silinir.
docker compose restart ise farklıdır. Aynı konteyneri durdurup yeniden başlatır, bu nedenle elle yapılan düzenlemeler bu işlemden etkilenmez. Manuel bir düzeltmenin haftalarca kalıcı gibi görünüp alakasız bir güncelleme sırasında yok olmasının nedeni budur. Named volumes ve bind mounts her iki işlemde de varlığını korur, çünkü verileri konteynerin dışında yaşar. bind mounts and named volumes bölümü, saklamak istediğiniz veriler için hangisinin seçilmesi gerektiğini açıklar.
Bu nedenle exec kabuğunu yalnızca okuma ve test yapma alanı olarak görün. Düzeltmeyi belirledikten sonra, kalıcı olacağı yere yazın: bir paketi Dockerfile içine, bir ayarı ise compose dosyasına ekleyin. Ardından değişikliği uygulamak için docker compose up -d komutunu çalıştırın ve yeni konteynerin düzeltmeyi içerdiğini başka bir exec ile doğrulayın.
FAQ
docker compose exec ve docker compose run arasındaki fark nedir?
exec, halihazırda çalışmakta olan bir container içinde, ana sürecin yanında bir komut çalıştırır ve image entrypoint'ini atlar. run, aynı servis tanımından; aynı image, ortam değişkenleri, volume ve ağ yapılandırmasıyla yeni bir container oluşturur, komutunuzu entrypoint üzerinden iletir ve önce tüm depends_on servislerini başlatır. run ayrıca, --service-ports eklemediğiniz sürece servisin portlarını dışarıya açmaz. Canlı servisi incelemek için exec kullanın. Servis durdurulmuşsa veya servisi rahatsız etmek istemiyorsanız run --rm kullanın.
docker compose exec neden servisin çalışmadığını bildiriyor?
exec mevcut bir container'a bağlanır ve yeni bir tane oluşturamaz; bu nedenle durdurulmuş veya çökmüş bir servis service "web" is not running hatası verir. Exited (1) gibi bir durumla çıkış yapmış container'ları listeleyen docker compose ps -a komutunu kontrol edin ve durma nedenini öğrenmek için docker compose logs web komutunu okuyun. Yine de bir shell erişimi sağlamak için docker compose run --rm --entrypoint sh web komutunu çalıştırın. Bu, hatalı başlangıç komutunun çalışmasına izin vermeden, aynı servis tanımından yeni bir container oluşturur.
Image içinde bash yoksa nasıl shell açarım?
docker compose exec web bash komutunun exec: "bash": executable file not found in $PATH hatası vermesi, image içinde bash bulunmadığı anlamına gelir; bu, Alpine tabanlı yapılar için normaldir. BusyBox, /bin/sh sağladığı için docker compose exec web sh kullanın. Distroless ve scratch image'lar hiçbir shell içermez, bu yüzden hiçbir exec komutu çalışmayacaktır. Yayıncı sunuyorsa image'ın :debug etiketine geçin veya docker compose ps -q web üzerinden alınan $CID ile hedef namespace'lerde bir hata ayıklama container'ı başlatmak için docker run --rm -it --network "container:$CID" --pid "container:$CID" nicolaka/netshoot kullanın.
exec komutum cron içinde neden "the input device is not a TTY" hatası veriyor?
docker compose exec varsayılan olarak bir pseudo terminal talep eder ancak cron bunu sağlamaz, bu yüzden komutunuz çalışmadan önce istek başarısız olur. Bunu kapatmak için -T ekleyin: docker compose exec -T db pg_dump -U postgres app. Bir TTY, byte akışını değiştirip binary dökümleri bozabileceğinden, yönlendirilmiş veya pipe edilmiş çıktılar için de -T kullanın. Cron içinde ayrıca compose dosyanızın mutlak yolunu içeren -f parametresini geçin, aksi takdirde Compose no configuration file provided: not found hatasıyla sonlanır.
exec ile container içinde yaptığım değişiklikler yeniden başlatmada kalıcı olur mu?
Aynı container'ı yeniden kullanan docker compose restart işlemlerinde değişiklikler korunur. Herhangi bir image veya yapılandırma değişikliğinden sonra yapılan docker compose up -d işlemlerinde ise değişiklikler kaybolur; çünkü bu işlem container'ı image üzerinden yeniden oluşturur ve yazılabilir katmanı atar. Named volume veya bind mount içine yazılan veriler, container dışında yaşadıkları için her iki durumda da korunur. Tanısal değişiklikleri exec ile yapın, ardından kalıcı sürümü Dockerfile veya compose dosyasına ekleyin.