Docker Compose command ve entrypoint farkı nedir?
Docker Compose icerisinde ENTRYPOINT ve CMD arasindaki farki ogrenin. ENTRYPOINT tanimlandiginda CMD neden devre disi kalir ve dort farkli gecersiz kilma kombinasyonu nasil calisir?
Docker Compose command ve entrypoint farkı, tek bir kural ile
Docker Compose içerisinde entrypoint: çalışacak programı, command: ise bu programa iletilecek argümanları belirler. Container süreci, entrypoint listesinin sonuna command listesinin eklenmesiyle oluşur. Bu sayfadaki diğer tüm davranışlar bu tek cümleden türetilir.
Bu iki anahtar, iki Dockerfile talimatına karşılık gelir. entrypoint:, imajın ENTRYPOINT değerinin yerini alır. command: ise imajın CMD değerinin yerini alır. Bunlar birbirinden bağımsız değildir ve kullanıcıların takıldığı nokta burasıdır: entrypoint: ayarını yapmak, imajın CMD değerini de devre dışı bırakır. Compose spesifikasyonu bunu doğrudan belirtir. Eğer entrypoint boş değilse, Compose imajdan gelen varsayılan komutu yok sayar.
İmajın halihazırda bildirdiklerini inceleyin
Herhangi bir ayarı geçersiz kılmadan önce, imajın beraberinde getirdiği yapılandırmayı inceleyin.
docker image inspect --format '{{json .Config.Entrypoint}}' postgres:16
docker image inspect --format '{{json .Config.Cmd}}' postgres:16["docker-entrypoint.sh"] ve ["postgres"] değerlerine sahipsiniz, bu nedenle container docker-entrypoint.sh postgres komutunu çalıştırır. Bu betik, ilk önyüklemede veri dizinini oluşturur, POSTGRES_* değişkenlerini okur, yetkileri postgres kullanıcısına düşürür ve son olarak kendisine verilen argümanları çalıştırır. Hangi kısmı değiştirmeniz gerektiğini bilmek, kararın tamamını oluşturur. Veritabanına bir bayrak (flag) geçirmek için command: kısmını değiştirirsiniz. Eğer entrypoint: kısmını değiştirirseniz, bu kurulumun hiçbiri çalışmaz.
Dört kombinasyon, küçük bir görselde gösterilmiştir
Görevi yalnızca başlatıldığı argüman listesini yazdırmak olan bir imaj oluşturun.
FROM alpine:3.20
ENTRYPOINT ["/bin/echo", "ep"]
CMD ["cmd"]docker build -t argdemo .services:
demo:
image: argdemoHer düzenlemeden sonra docker compose up komutunu çalıştırın ve loglanan tek satırı okuyun.
- Hiçbir anahtar ayarlanmamış. Süreç
/bin/echo ep cmddurumundadır ve logep cmdçıktısını gösterir. - Yalnızca
command: ["cmd2"]. Süreç/bin/echo ep cmd2durumundadır. Entrypoint değiştirilmemiştir ve yalnızca argümanlar değişmiştir. - Yalnızca
entrypoint: ["/bin/echo", "ep2"]. Süreç/bin/echo ep2durumundadır ve logep2çıktısını gösterir. İmajdan gelencmdsilinmiştir ve bu konuda herhangi bir uyarı verilmez. - Her iki anahtar da ayarlanmış. Süreç
/bin/echo ep2 cmd2durumundadır. Argüman listesinin tamamını kontrol edebildiğiniz tek durum budur.
Entrypoint ayarının image CMD değerini neden sildiği
Bir image'in CMD değeri, o image'in ENTRYPOINT değeri için varsayılan argüman listesi olarak yazılır. Entrypoint'i değiştirdiğinizde, bu argümanlar artık çalışmayan bir programa ait hale gelir; bu nedenle Compose, image yazarının amaçlamadığı bir komut satırı oluşturmak yerine bu argümanları devre dışı bırakır. docker run --entrypoint de aynı şekilde davranır, dolayısıyla bu bir Compose tuhaflığı değil, Docker'ın çalışma biçimidir.
Bunun sonucu somuttur. nginx:1.27, ENTRYPOINT ["/docker-entrypoint.sh"] ve CMD ["nginx", "-g", "daemon off;"] değerlerini tanımlar. entrypoint: /custom-init.sh değerini ayarladığınızda, betiğiniz boş bir argüman listesiyle başlar. Genellikle exec "$@" ile biten bir betiğin çalıştıracak (exec) bir şeyi kalmaz; bu durumda exec hiçbir işlem yapmaz, betik son satırına ulaşır ve container hiçbir hata mesajı vermeden 0 koduyla sonlanır. Argümanları kendiniz tekrar ekleyin:
services:
web:
image: nginx:1.27
entrypoint: /custom-init.sh
command: ["nginx", "-g", "daemon off;"]Unutulmaması gereken kural şudur: entrypoint: değerini her değiştirdiğinizde, aynı düzenleme içerisinde command: değerinin ne olması gerektiğine de karar verin.
Exec formu, shell formu ve Compose'un farkı
Dockerfile iki farklı sözdizimini kabul eder. CMD ["nginx", "-g", "daemon off;"] exec formudur: ikili dosya doğrudan çalıştırılır, herhangi bir shell sürece dahil olmaz. CMD nginx -g "daemon off;" ise shell formudur: Docker bunu /bin/sh -c 'nginx -g "daemon off;"' şeklinde yeniden yazar, böylece önce bir shell çalışır ve programınız bu shell'in alt süreci haline gelir.
Compose bu kuralı kopyalamaz ve bu durum kullanıcılar için şaşırtıcı olabilir. command: içindeki bir metin dizisi argümanlara bölünür ve herhangi bir /bin/sh -c sarmalayıcısı olmadan doğrudan çalıştırılır. Compose referansı bu konuda nettir: command alanı, imaj içinde tanımlanan SHELL bağlamında çalışmaz; bu nedenle shell özelliklerine ihtiyaç duyuyorsanız, shell'i kendiniz çağırmanız gerekir.
command: echo "hello $$HOSTNAME" komutunun hello $HOSTNAME metnini olduğu gibi yazdırmasının nedeni budur. Hiçbir shell bu metin dizisini görmediği için herhangi bir genişletme işlemi gerçekleşmemiştir. Bir shell'e ihtiyaç duyduğunuzda onu açıkça talep edin:
services:
demo:
image: alpine:3.20
command: /bin/sh -c 'echo "hello $$HOSTNAME"'Sinyaller, PID 1 ve temiz bir docker compose down işlemi
docker compose stop ve docker compose down, her container içindeki PID 1'e SIGTERM gönderir, stop_grace_period süresini bekler ve ardından SIGKILL gönderir. Varsayılan bekleme süresi 10 saniyedir.
PID 1, Linux'ta özel bir konuma sahiptir. Çekirdek, bir sinyalin varsayılan eylemini PID 1'e uygulamaz; bu nedenle herhangi bir SIGTERM işleyicisi tanımlanmamış bir süreç, PID 1 olarak çalıştığında SIGTERM sinyalini basitçe görmezden gelir. Süreç, tüm bekleme süresi boyunca çalışmaya devam eder ve ardından doğrudan öldürülür; bu durum açık bağlantıların veya tamamlanmamış işlemlerin kesilmesine neden olur.
Programınızın önünde bir kabuk (shell) bulunması bu durumu daha olası kılar, çünkü kabuk PID 1'dir ve çoğu kabuk sinyalleri alt sürece iletmez. Bazı kabuklar, bir -c dizisindeki son komutla kendilerinin yerini değiştirir, bu nedenle programınız bazen yine de PID 1'e ulaşabilir. Bu durum kabuğa ve dizenin tam yapısına bağlıdır, bu yüzden tahmin yürütmeyin. Şunu inceleyin:
docker compose exec -T web cat /proc/1/cmdline | tr '\0' ' '; echoEğer PID 1, programınız yerine /bin/sh -c ... olarak görünüyorsa, iki çözüm yolu vardır. Image içinde exec formunu kullanın veya kabuğu koruyup süreci exec ile devredin:
services:
web:
image: myapp:1.4
command: /bin/sh -c 'exec myapp --config /etc/myapp.toml'exec, bir alt süreç oluşturmak yerine kabuk sürecini programınızla değiştirir; böylece programınız PID 1'i devralır ve sinyali doğrudan alır.
Bazı programlar alt süreçler oluşturur ancak bunları sonlandırmaz (reap), bu da zombi süreçlere yol açar; çünkü PID 1 aynı zamanda süreçleri temizlemekten (reaper) sorumludur. Compose bunun için bir anahtara sahiptir:
services:
web:
image: myapp:1.4
init: true
stop_grace_period: 30sinit: true, PID 1 olarak sinyalleri sürecinize ileten ve alt süreçleri temizleyen küçük bir init süreci çalıştırır. stop_grace_period, yavaş kapanan süreçlere daha fazla zaman tanır. Eğer programınız farklı bir sinyal bekliyorsa, stop_signal: SIGQUIT Compose'un gönderdiği sinyali değiştirir. Bir image'ın halihazırda ne talep ettiğini docker image inspect --format '{{.Config.StopSignal}}' nginx:1.27 ile kontrol edin.
docker compose down işleminin her servis için sürekli on saniye sürdüğü bir yığın, hiçbir şeyin SIGTERM sinyalini işlemediğini gösterir. Araçları suçlamadan önce bunu düzeltin ve her alt komutun neleri kaldırdığını görmek için docker compose down ve stop arasındaki fark konusuna göz atın.
Aynı exec ve shell ayrımı bir noktada daha karşımıza çıkar. test: ["CMD", "curl", "-f", "http://localhost/"] olarak yazılan bir sağlık kontrolü (healthcheck) ikili dosyayı doğrudan çalıştırırken, test: ["CMD-SHELL", "curl -f http://localhost/ || exit 1"] bir kabuk üzerinden çalışır; böylece || bir anlam ifade eder. Dürüstçe başarısız olan Compose sağlık kontrolleri yazma konusu bu alanın geri kalanını ele alır.
Resmi bir imaja bayrak ekleme
Okuyucuların büyük çoğunluğu bu amaçla gelmektedir. postgres üzerinde fazladan bir bayrak kullanmak istiyorsunuz ve başlatma betiğini bozmamalısınız.
services:
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- pgdata:/var/lib/postgresql/data
command: postgres -c max_connections=200 -c shared_buffers=256MB
volumes:
pgdata:Yalnızca command: değiştirildiği için docker-entrypoint.sh çalışmaya devam eder ve verdiğiniz komutu yürütür. Varsayımda bulunmak yerine sonucu kontrol edin:
docker compose up -d db
docker compose exec -T db psql -U postgres -c 'show max_connections;'Çıktı 200 değerini göstermelidir. Eğer hala 100 görünüyorsa, docker compose config komutunu çalıştırın ve beklediğiniz command değerinin birleştirilmiş çıktıda yer aldığını doğrulayın. Compose, birleştirme dosyalarını command değerine ekleme yaparak değil, doğrudan üzerine yazarak işler; bu nedenle command: değerini de ayarlayan ikinci bir dosya sessizce öncelik kazanır.
Yukarıdaki ${POSTGRES_PASSWORD}, konteyner henüz oluşmadan önce Compose tarafından ana makinedeki .env dosyanızdan genişletilir. Compose içinde ortam dosyaları ve gizli veriler bölümü, bu değerin nerede güvenli bir şekilde tutulabileceğini açıklar.
docker compose run ile tek seferlik migrasyon çalıştırma
docker compose run, aynı servis tanımından yeni bir container oluşturur ve komutu servis adından sonra yazdığınız ifadeyle değiştirir. İmajın entrypoint'i çalışmaya devam ettiği için container, sürekli çalışan servis ile tam olarak aynı şekilde hazırlanır.
docker compose run --rm app python manage.py migrate--rm, komut sonlandığında container'ı siler. Bu bayrak kullanılmazsa, her çalıştırma işleminden sonradocker compose ps -aiçinde görülebilen durdurulmuş bir container geride kalır.- Portlar yayınlanmaz. Bir
runcontainer'ı,--service-portseklemediğiniz sürece servisinports:ayarını yok sayar; bu sayede halihazırda çalışan servis ile çakışma yaşanmaz. - Bağımlılıklar önce başlatılır.
depends_oniçindeki her şey komutunuzdan önce ayağa kalkar;--no-depsise bu adımı atlar. - Container,
myproject-app-run-9f2c1agibi üretilmiş bir isim alır, bu nedenle servis container'ı ile asla çakışmaz.
Entrypoint'i de değiştirmek isterseniz bunun için özel bir bayrak mevcuttur:
docker compose run --rm --entrypoint /bin/sh app -c 'python manage.py migrate'Ortaya çıkan argüman listesi /bin/sh -c 'python manage.py migrate' şeklindedir, çünkü servis adından sonra gelen kelimeler komut olarak kabul edilmeye devam eder. docker compose exec diğer araçtır ve farklı çalışır: halihazırda çalışan bir container içinde bir süreç başlatır ve entrypoint: ile command: ayarlarını tamamen yok sayar. Yeni bir container gerektiren görevler için run, çalışan bir container'ın içine bakmak için ise exec kullanın. Compose komutları kopya kağıdı diğer alt komutları yan yana listeler.
Kapsayıcım neden hemen kapanıyor?
Çıkış kodu ile başlayın, çünkü bu kod sorunun nedenini hızla daraltır.
docker compose ps -a
docker compose logs appÇıkış kodu 0 ve çıktı yok. Komut çalıştı ve tamamlandı. En yaygın neden, imajın CMD değerini de beraberinde götüren bir entrypoint: geçersiz kılma işlemidir; bu durumda giriş noktası boş bir argüman listesiyle çalışır ve devredecek bir şey bulamaz.
permission denied ile biten bir hata. Betiğin imaj içinde çalıştırılabilir biti yoktur; bu durum genellikle dosyanın depodaki (repository) izinlerinin hiç ayarlanmamış olmasından kaynaklanır. Derleme zamanında COPY --chmod=0755 entrypoint.sh /entrypoint.sh ile bu biti ayarlayın.
İmaj içinde açıkça görebildiğiniz bir dosya için no such file or directory ile biten bir hata. Betik, Windows satır sonlarına sahiptir. Bu durumda ilk satır, #!/bin/sh ve bir satır başı (carriage return) baytı olarak okunur; çekirdek, isminde bu bayt bulunan bir yorumlayıcı arar ve bulamaz. dos2unix entrypoint.sh komutunu çalıştırın, ardından bunun tekrar oluşmaması için .gitattributes dosyasına * text eol=lf ekleyin.
executable file not found in $PATH. command: içinde belirtilen ikili dosya imajda yoktur veya yalnızca gerçek bir programın çalışabileceği yere cd gibi bir kabuk yerleşik komutu (shell built-in) yazdınız.
Entrypoint'i başarısız olan bir imajda shell erişimi sağlama
Entrypoint, siz inceleme yapamadan önce sonlanıyorsa onu şu şekilde değiştirin:
docker compose run --rm --entrypoint /bin/sh appEğer bu komut executable file not found in $PATH hatası döndürürse, imajda hiçbir shell bulunmuyor demektir. Distroless ve scratch tabanlı imajlar genellikle shell içermez. Entrypoint'i başlatmadan dosya sistemini dışarıdan şu şekilde okuyabilirsiniz:
docker create --name probe myapp:1.4
docker export probe | tar -tv | head -40
docker rm probeContainer'ın sürekli açık kalmasını ve ona tekrar tekrar bağlanabilmeyi istiyorsanız, onu asla sonlanmayan bir süreç üzerinde bekletin. Bunu, commit etmeyeceğiniz bir override dosyasına ekleyin:
services:
app:
entrypoint: ["tail", "-f", "/dev/null"]
command: []command: [] kullanımı kesinlikle gerekli değildir, çünkü entrypoint: ayarı zaten imajın CMD değerini temizlemiştir; ancak bunu yazmak, dosyayı daha sonra okuyacak kişi için niyetinizi belirtir. Servisi ayağa kaldırın ve içine girin:
docker compose -f compose.yaml -f compose.debug.yaml up -d app
docker compose exec app /bin/shŞimdi gerçek entrypoint'i manuel olarak çalıştırın ve nerede durduğunu gözlemleyin. Bu işlem, hata mesajını yarım saniye önce ölen bir container yerine doğrudan terminalinizde görmenizi sağlar. Eğer hala ilk stack'inizi oluşturuyorsanız, bir VPS üzerinde ilk Compose stack'i rehberi, yukarıdaki her şeyin varsaydığı dosya yapısını kapsamaktadır.
FAQ
Konteynerim docker compose up komutundan hemen sonra neden kapanıyor?
Çıkış kodu için docker compose ps -a değerini kontrol edin. Çıktı vermeden 0 koduyla çıkış yapılması, genellikle servis üzerinde entrypoint: ayarının yapıldığı ve bunun imajın CMD değerini temizlediği anlamına gelir; bu durumda entrypoint boş bir argüman listesiyle çalışır ve hemen biter. Argümanları command: ile tekrar ekleyin. permission denied ile biten bir hata, entrypoint betiğinin çalıştırılabilir iznine sahip olmadığını gösterir. Mevcut bir dosya için no such file or directory ile biten bir hata, betiğin Windows satır sonlarına sahip olduğu ve bu nedenle shebang satırının mevcut olmayan bir yorumlayıcıyı işaret ettiği anlamına gelir.
Compose içinde entrypoint ayarı yapmak imajın CMD değerini siler mi?
Evet. Eğer entrypoint boş değilse, Compose imaj tarafından tanımlanan varsayılan komutu yok sayar. Bu belgelenmiş bir davranıştır ve docker run --entrypoint ile uyumludur. Bunun nedeni, bir imajın CMD değerinin, o imajın ENTRYPOINT değeri için argüman olarak yazılmış olmasıdır; dolayısıyla entrypoint değerini değiştirdiğinizde eski argümanlar artık bir anlam ifade etmez. Yeni entrypoint hala argümanlara ihtiyaç duyuyorsa, aynı servis içinde command: ayarını yapın.
Compose içindeki bir komut dizisi shell üzerinden mi çalıştırılır?
Hayır. Dockerfile CMD değerinin aksine, Compose command: içindeki bir dizi doğrudan argümanlara bölünür ve çalıştırılır; herhangi bir /bin/sh -c sarmalayıcısı kullanılmaz. Bu nedenle $VARIABLE, konteyner içindeki bir shell tarafından asla genişletilmez. Bir shell'e ihtiyaç duyduğunuzda, command: /bin/sh -c 'echo "hello $$HOSTNAME"' örneğinde olduğu gibi shell'i kendiniz çağırın. Çift $$ kullanımı dolar işaretini kaçırır (escape), böylece Compose bunu ana makinede (host) genişletmek yerine doğrudan konteynere iletir.
docker compose down komutu tek bir konteyner için neden on saniye sürüyor?
Compose, PID 1 sürecine SIGTERM sinyali gönderir, stop_grace_period süresinin dolmasını bekler (varsayılan olarak 10 saniye) ve ardından SIGKILL sinyalini gönderir. Çekirdek (kernel), varsayılan sinyal işlemlerini PID 1 sürecine uygulamaz; bu nedenle SIGTERM işleyicisi olmayan bir program sinyali görmezden gelir ve her zaman tam sürenin dolmasını bekler. docker compose exec -T app cat /proc/1/cmdline | tr '\0' ' ' komutu ile PID 1 sürecinin gerçekte ne olduğunu öğrenin. Eğer bu bir shell ise, imajı exec formuna geçirin veya shell dizisi içine exec yazın. Eğer süreç, asla sonlandırmadığı alt süreçler (children) oluşturuyorsa, servis üzerinde init: true ayarını yapın.