Docker Compose Birden Fazla Dosya Kullanımı ve Birleştirme
Docker Compose dosya birleştirme sırasını, compose.override.yaml dosyasının otomatik yüklenme mantığını ve ports çakışmalarını çözmek için gereken yapılandırma yöntemlerini öğrenin.
Compose'un birden fazla dosya ile yaptığı işlemler
Docker Compose, tek bir projeyi birden fazla dosyadan oluşturabilir. Dosyaları kendisine iletildikleri sırayla okur ve tek bir modelde birleştirir; bu nedenle çakışan değerlerde en son okunan dosya geçerli olur. Komut satırından bunu yapan iki mekanizma vardır: Compose'un kendiliğinden yüklediği bir geçersiz kılma (override) dosyası ve manuel olarak ilettiğiniz -f bayrağı. Üçüncü mekanizma ise dosyanın kendi içinde yer alan include öğesidir ve her ikisinden de farklı çalışır.
Birleştirme işlemi basit bir üzerine yazma değildir. Eşlemeler anahtar bazında birleştirilir, diziler birbirine eklenir ve küçük bir alan kümesi tamamen değiştirilir. Sürprizlerin kaynağı bu farktır ve ports listesi neredeyse herkesin takıldığı noktadır.
Aşağıdaki her şey, eski docker-compose betiği yerine docker compose eklentisi olan Compose v2 sürümünü temel alır. Kontrol etmek için docker compose version komutunu çalıştırın. Henüz bir Compose dosyası yazmadıysanız, Docker Compose temel bilgiler kılavuzu ile başlayıp geri dönün.
Compose'un belirtilmesine gerek kalmadan yüklediği geçersiz kılma dosyası
docker compose up komutunu herhangi bir -f bayrağı olmadan çalıştırdığınızda, Compose çalışma dizinini ve ardından üst dizinlerini compose.yaml veya docker-compose.yaml dosyaları için tarar. Eğer temel dosyanın yanında bir geçersiz kılma (override) dosyası bulunuyorsa, Compose bunu otomatik olarak ikinci sırada yükler.
ls compose.yaml compose.override.yaml
docker compose up -dHer iki dosyanın da mevcut olması, bunları manuel olarak belirtmekle aynı sonucu verir.
docker compose -f compose.yaml -f compose.override.yaml up -dCompose tarafından tanınan dosya adları compose.override.yaml, compose.override.yml ve daha eski olan docker-compose.override.yml ile docker-compose.override.yaml'dur. compose.dev.yaml gibi diğer tüm isimler, yalnızca -f ile açıkça belirtildiğinde yüklenir.
Herhangi bir -f parametresi girdiğiniz anda otomatik yükleme durur. docker compose -f compose.yaml up yalnızca belirtilen dosyayı okur ve geçersiz kılma dosyasını yok sayar; bu rehberin ilerleyen bölümlerinde yer alan geliştirme ve üretim ortamı modeli bu özellik üzerine kuruludur.
Sunucuda bu durumun iki yönlü etkisi vardır. Deploy dizininde bırakılan bir override dosyası, bu dizinden çalıştırılan her yalın docker compose komutuyla yüklenir; cron job tarafından çalıştırılan komut da buna dahildir. Üretim ortamındaki bir stack’in kimsenin dağıtmayı amaçlamadığı bir kaynak dizini bind-mount etmesinin nedeni budur. Her deploy işleminden sonra docker compose config komutunu çalıştırın ve oluşan çıktıyı okuyun. Deploy işlemi unattended olarak yürütüldüğünde bu kontrol yalnızca bir şey ters gittiğini size bildiren bir mekanizma varsa işe yarar. Bu bildirim görevini, bir cron job’ın veya systemd OnFailure unit’inin post isteği gönderebildiği self-hosted ntfy sunucusu gibi bir push channel üstlenir.
-f ile sıralama ve göreli yolların çözümlenmesi
Compose, yapılandırmayı sağladığınız dosyaların sırasına göre oluşturur; sonraki dosyalar öncekilerin üzerine yazar veya onlara ekleme yapar. Soldan sağa doğru, en son belirtilen dosya geçerli olur.
docker compose -f compose.yaml -f compose.prod.yaml config
docker compose -f compose.yaml -f compose.prod.yaml up -dProjedeki her komut aynı dosya listesine ihtiyaç duyar. up komutunu iki dosya ile, logs komutunu ise tek dosya ile çalıştırırsanız, Compose'un mevcut olmadığını belirttiği bir servise yol açabilecek farklı bir birleştirilmiş model ile işlem yaparsınız. Yükseltmeleri tek seferlik komutlar olarak çalışan yığınlarda risk artar; örneğin kendi kendine barındırılan bir Chatwoot destek masası kurulumundaki veritabanı taşıma adımında, yanlış dosya listesiyle verilen bir docker compose run komutu, servislerinizin halihazırda kullandığı modelden farklı bir modeli sessizce hedefler. Bunun yerine listeyi COMPOSE_FILE ortam değişkeni ile bir kez ayarlayın.
export COMPOSE_FILE=compose.yaml:compose.prod.yaml
docker compose config
docker compose up -dAyırıcı, Linux üzerinde : karakteridir; COMPOSE_PATH_SEPARATOR ile bu değiştirilebilir. COMPOSE_FILE değişkeni, proje dizinindeki .env dosyasında da tanımlanabilir; bu sayede ayar, kabuk geçmişinizin bir parçası olmak yerine projenin bir parçası haline gelir. Komut satırında açıkça belirtilen her değer, ortam değişkenini geçersiz kılar.
Şimdi bind mount işlemlerini bozan kurala gelelim. -f ile birden fazla dosya kullandığınızda, tüm dosyalardaki göreli yollar, o dosyanın bulunduğu dizine göre değil, ilk dosyanın bulunduğu dizine göre çözümlenir. deploy/prod/compose.prod.yaml içinde ./data:/var/lib/postgresql/data yazsanız bile, Compose ./data dosyasını temel dosyanın yanında arar. Docker, bu yanlış yolda boş bir dizin oluşturur ve container içi boş bir şekilde başlar; bu durum veri kaybı gibi görünse de aslında öyle değildir. Temel yolu kendiniz belirlemek için --project-directory parametresini kullanın veya her dosyayı kendi dizinine göre çözümleyen include seçeneğinden yararlanın.
Proje adı aynı temel dizinden gelir, bu nedenle ilk dosyanın değiştirilmesi projenin yeniden adlandırılmasına neden olabilir. Yeniden adlandırılan bir proje, yeni container adları ve yeni volume adları anlamına gelir; eski volume ise eski adıyla diskte kalmaya devam eder. Bunun yerine temel dosyada en üst düzeyde bir name: kullanarak projeyi sabitleyin.
name: myappHangi alanların birleştirildiği ve hangilerinin değiştirildiği
Compose, birleştirmeleri alan adına göre değil, değerin türüne göre yapar.
- Tek değerli alanlar değiştirilir.
image,command,entrypointvemem_limit, sonraki değeri doğrudan alır. Bircommandalanına argüman ekleyemezsiniz, çünkü geçersiz kılma (override) işleminin tamamı satırı yeniden yazar. - Eşlemeler (mappings) anahtar bazında birleştirilir.
environment,labels,volumesvedevicesher iki dosyadan gelen tüm anahtarları korur; her iki dosyada da bulunan anahtarlarda ise sonraki dosyanın değeri geçerli olur.environmentvelabelsiçin anahtar, değişken veya etiket adıdır.volumesvedevicesiçin anahtar, container yoludur. - Diziler (sequences) sona eklenir.
dns,dns_search,expose,tmpfsveexternal_linksbirbirine eklenir.expose: ["3000"]içeren bir temel dosya,["4000", "5000"]içeren bir geçersiz kılma dosyasıyla birleştirildiğinde sonuç["3000", "4000", "5000"]olur.
Dört dizi bir kimlik anahtarı taşır; bu nedenle o anahtarla eşleşen girdiler sona eklenmek yerine birleştirilir. volumes, secrets ve configs, target üzerinden eşleşir. ports ise ip, target, published ve protocol kombinasyonu üzerinden eşleşir.
Bu ports kuralını iki kez okuyun, çünkü hata yapmaya en müsait nokta burasıdır. İki port girdisi, yalnızca bu dört parçanın tamamı aynı olduğunda tek bir girdi olarak kabul edilir. Bunlardan herhangi birini değiştirirseniz, Compose bunu ikinci ve alakasız bir port olarak görür; bu nedenle her ikisini de tutar.
Portun geçersiz kılma işleminden sonra neden hala yayında olduğu
Servisi her arayüzde yayınlayan bir temel dosya:
services:
web:
image: nginx:1.27
ports:
- "8080:80"Bir reverse proxy önünde yer alacağı için onu yalnızca localhost'a bağlamak üzere yazılmış bir geçersiz kılma (override):
services:
web:
ports:
- "127.0.0.1:8080:80"İşlemin başarılı olduğunu varsaymadan önce sonucu kontrol edin.
docker compose -f compose.yaml -f compose.prod.yaml configHer iki girdi de çıktıda yer alır. ip kısmı farklıdır; 0.0.0.0 ile 127.0.0.1 karşılaştırıldığında, birleştirme işlemi açısından bunlar iki farklı porttur ve kaldırmaya çalıştığınız genel bağlama (public binding) modelde kalmaya devam eder. Bu durum Docker'da diğer yerlerden daha önemlidir, çünkü yayınlanan bir port, güvenlik duvarı kurallarınızdan önce iptables içine yazılır. Mekanizma yayınlanan Docker portlarının ufw'yi neden devre dışı bıraktığı bölümünde ele alınmıştır.
İki çözüm yolu mevcuttur. Açık olan yöntem, tüm özniteliği değiştiren ve birleştirme kurallarını atlayan !override etiketidir:
services:
web:
ports: !override
- "127.0.0.1:8080:80"!override, Compose v2.24.4 veya daha yeni bir sürüm gerektirir. Taşınabilir çözüm ise hiçbir etiket gerektirmez: ports kısmını temel dosyadan tamamen çıkarın ve yalnızca ortama özel dosyalarda tanımlayın. Birleştirilecek bir şeyin olmaması, sızıntı ihtimalinin de olmaması demektir. Aşağıdaki örnekte kullanılan yöntem bu kalıptır.
Temel dosya kümesindeki bir değeri silme
!reset, bir özniteliği kaldırarak onu varsayılan değerine veya null durumuna döndürür. Bu komut bir değer alır ancak bunu dikkate almaz; bu nedenle geçerli ve boş bir değer yazılmalıdır.
services:
web:
ports: !reset []
environment:
DEBUG: !reset null!reset, Compose v2.24 veya daha yeni bir sürümünü gerektirir. Temel dosya üzerinde düzenleme yetkiniz olmadığında, örneğin dışarıdan aldığınız bir satıcı parçasını kullanırken bu yönteme başvurun. Yayınlanmış bir upstream yığını tam olarak bu duruma örnektir: kendi kendine barındırılan bir AFFiNE çalışma alanı arkasındaki Compose dosyası, sizin yazmadığınız dört container tanımlar ve !reset, dosyayı çatallayıp (fork) takip etme yükünü üstlenmek zorunda kalmadan bunlardan birindeki bir özniteliği temizlemenize olanak tanır.
include, parçalardan oluşturulan yığınlar için
include, başka bir Compose uygulamasını modelinize dahil eder. Bu, bir bayrak değil, en üst düzey bir öğedir.
include:
- path: ../commons/compose.yamlinclude içindeki her yol, kendi proje dizinine sahip bağımsız bir Compose uygulama modeli olarak yüklenir; bu nedenle dosya içindeki göreli yollar, o dosyanın kendi dizinine göre çözümlenir. -f ile arasındaki temel fark budur ve bir parça başka bir klasörde veya başka bir depoda bulunduğunda include aracının doğru tercih olmasının nedeni de budur. Bu, sizin yazmadığınız bir tedarikçi yığınının genel biçimidir: kendi kendine barındırılan bir Authentik SSO kurulumunun arkasındaki çok servisli Compose dosyası, kendi göreli yolları bozulmadan kendi dizininde durabilir; sizin dosyanız ise yalnızca kendi servislerinize odaklanır.
Uzun biçim, alt seçenekleri kabul eder.
include:
- path:
- ../monitoring/compose.yaml
- ../monitoring/compose.vps.yaml
project_directory: ../monitoring
env_file: ../monitoring/.envpath bir liste kabul eder ve bu dosyalar, sonuç modelinize eklenmeden önce normal kurallara göre birleştirilir. project_directory, dahil edilen dosyadaki göreli yolları çözümlemek için kullanılan temel yolu belirler. env_file, dahil edilen dosyaya enterpolasyon için kendi değişkenlerini sağlar; bu, paylaşılan bir parçanın projenizin .env dosyasını sessizce okumasını engeller. include, Compose v2.20.0 veya daha yeni bir sürüm gerektirir. Aynı seçenekler, halihazırda çalıştırdığınız bir yığına tek konteynerli bir eklenti eklemek için de uygundur; örneğin Jellyfin kütüphanesini 90'ların video kiralama dükkanı gibi gösteren Halcyon: bu eklentinin dosyası kendi image etiketini ve kendi env_file ayarlarını korur, böylece yükseltme yapmak medya yığınınızın bulunduğu dosyaya dokunmanızı gerektirmez.
Sizin dosyanız ile dahil edilen dosya arasındaki yinelenen kaynak adları sessizce birleştirilmek yerine hata olarak raporlanır; bu kasıtlı bir durumdur. Dahil edilen bir dosyanın tanımladığı bir şeyi değiştirmek için değişikliği compose.override.yaml içine yerleştirin: geçersiz kılma (override), birleştirilmiş modele uygulanır, böylece dahil edilen kaynaklara, onlarla çakışmadan müdahale edebilir. Bu alışkanlık, özellikle PhotoPrism ile Immich karşılaştırmasında ele alınan çok konteynerli fotoğraf sunucuları gibi her sürümde upstream dosyası yeniden yazılan yığınlarda işe yarar; localhost bağlaması veya ek bir volume, bir sonraki yükseltmede değiştirilecek dosya yerine sizin geçersiz kılma dosyanızda yer almalıdır.
Kısa özet: include ayrı uygulamaları birleştirir, -f ise yapılandırmayı tek bir uygulama üzerine katmanlar halinde ekler.
Tek bir VPS üzerinde geliştirme ve üretim ortamı ayrımı
Bu modelin tamamı üç dosyada toplanmıştır. Temel dosya, her yerde geçerli olan kuralları tanımlar ve hiçbir portu dış dünyaya açmaz.
name: myapp
services:
app:
image: ghcr.io/example/app:1.4.2
environment:
DATABASE_URL: postgres://app:${POSTGRES_PASSWORD}@db:5432/app
LOG_LEVEL: info
depends_on:
db:
condition: service_healthy
restart: unless-stopped
db:
image: postgres:16
environment:
POSTGRES_USER: app
POSTGRES_DB: app
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- db_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d app"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
volumes:
db_data:depends_on koşulu, uygulamanın sadece var olan bir container'ı değil, yanıt veren bir veritabanını beklemesini sağlar; bu durum healthcheck ve depends_on koşulları bölümünde açıklanmıştır. POSTGRES_PASSWORD değeri, git deposuna asla dahil edilmemesi gereken .env dosyasından alınır. Daha güvenli yöntemler için env dosyaları ve Compose secret'ları bölümüne bakınız.
Sırada, Compose tarafından otomatik olarak yüklenen compose.override.yaml dosyası vardır. Bu, geliştiricinin dosyasıdır.
services:
app:
build: .
command: npm run dev
environment:
LOG_LEVEL: debug
ports:
- "3000:3000"
volumes:
- ./src:/app/src
db:
ports:
- "127.0.0.1:5432:5432"Bir dizüstü bilgisayarda, sade bir docker compose up komutu bu iki dosyayı birleştirir. command, tek bir değer içerdiği için image varsayılanını değiştirir. LOG_LEVEL, environment anahtar bazlı birleştirme yaptığı için info değerinin yerini alır. Bind mount ve yayınlanan iki port tamamen eklemedir; veritabanı portu ise localhost'a bağlandığından, paylaşımlı bir ağdaki dizüstü bilgisayar PostgreSQL servisini dışarıya açmaz.
Son olarak compose.prod.yaml dosyası. İsmi Compose tarafından otomatik aranan bir dosya olmadığı için yanlışlıkla yüklenmesi mümkün değildir.
services:
app:
ports:
- "127.0.0.1:8000:3000"
deploy:
resources:
limits:
memory: 512MVPS üzerinde her iki dosyayı da belirtirsiniz; bu belirtme işlemi, geçersiz kılma (override) dosyasının devre dışı kalmasını sağlar.
docker compose -f compose.yaml -f compose.prod.yaml config
docker compose -f compose.yaml -f compose.prod.yaml up -d
docker compose -f compose.yaml -f compose.prod.yaml psps komutu, her iki servisin de çalıştığını ve db çıktısının (healthy) olduğunu göstermelidir. -f parametresini verdiğiniz için compose.override.yaml dosyası okunmamıştır; bu nedenle geliştirme komutu, kaynak kod bind mount'u ve 3000 numaralı genel port, dosya aynı dizinde bulunsa bile üretim ortamına erişemez. 8000 numaralı port yalnızca localhost üzerindedir ve bir proxy için hazırdır: ikinci servisi eklediğinizde Traefik arkasında birden fazla uygulama çalıştırma bölümüne bakınız.
Sunucunun .env dosyasına COMPOSE_FILE=compose.yaml:compose.prod.yaml değişkenini tanımladığınızda, diğer tüm komutlarınız tekrar sade docker compose logs -f app haline döner.
Tek servisli bir yığın (stack) da aynı yapıyı kullanır; çünkü self-hosted openGym antrenman takipçisi, ilk passkey'i kaydetmeden önce bir proxy arkasında TLS üzerinden yanıt vermelidir. İçinde ports tanımlanmamış bir temel dosya, başıboş bir genel port bağlamasının proxy'nin önüne geçmesini engelleyen şeydir.
Dağıtım yapmadan önce birleştirilmiş modeli okuyun
docker compose config, tamamen birleştirilmiş ve enterpole edilmiş modeli yazdırır. Bu bir önizleme değildir. Compose'un üzerinde işlem yapacağı kesin girdidir; dolayısıyla çıktı beklentilerinizle uyuşmuyorsa, doğru olan çıktıdır.
docker compose -f compose.yaml -f compose.prod.yaml config
docker compose -f compose.yaml -f compose.prod.yaml config --no-interpolate
docker compose -f compose.yaml -f compose.prod.yaml config --services--no-interpolate, ${VAR} değerini genişletilmemiş halde bırakır. Çıktıyı herhangi bir yere yapıştırmadan önce bunu kullanın, çünkü düz config komutu çözümlenmiş her gizli veriyi (secret) açık metin olarak yazdırır. --services yalnızca servis isimlerini listeler; bu, include dosyasının beklediğiniz içeriği dahil edip etmediğini doğrulamanın hızlı bir yoludur.
Hata modları ve karşılaşacaklarınız
no configuration file provided: not found. Compose okuyacak bir şey bulamadı. Proje dizininin dışındasınız veya COMPOSE_FILE mevcut olmayan bir yolu işaret ediyor. Compose, varsayılan temel dosya için üst dizinleri tarar ancak kendi belirttiğiniz bir dosya için herhangi bir arama yapmaz.
WARN[0000] The "POSTGRES_PASSWORD" variable is not set. Defaulting to a blank string. Değişken genişletme (interpolation), proje .env dosyasına ve kabuk ortamına göre çözümlenir; buradaki proje dizini, ilk -f dosyasının bulunduğu dizindir. .env dosyasını barındıran dizinden farklı bir yerden dağıtım yapmak, bu uyarıyı almanıza ve ardından her bağlantıyı reddeden bir veritabanı ile karşılaşmanıza neden olur.
Geçersiz kılma (override) düzenlemeniz docker compose config içinde görünmüyor. Ya otomatik geçersiz kılma yüklemesini devre dışı bırakan -f parametresini geçtiniz ya da Compose, üst dizinlerden birinde compose.yaml buldu ve geçersiz kılma dosyanız onunla aynı konumda değil. docker compose config komutunu başka hiçbir argüman olmadan çalıştırmak, Compose'un gerçekte hangi modeli oluşturduğunu size gösterir.
Bir bind mount boş ve Docker istemediğiniz bir dizin oluşturdu. Göreli yol, ilk dosyanın dizinine göre çözümlendi. Yolu düzeltin, --project-directory parametresini geçin veya parçayı include arkasına taşıyın.
Container'lar yeni isimlerle geri geliyor ve bir volume boş görünüyor. Proje ismi değişti, çünkü proje ismi ilk dosyanın dizinini takip eder. Temel dosyaya üst seviyeden bir name: ekleyin; böylece isimlendirme değişmeyecektir. Eski volume hala eski önek altında mevcuttur ve docker volume ls bunu gösterecektir.
Geçersiz kılma dosyasında kaldırdığınız bir port hala açık. ports birleştirme işlemi, değiştirme yapmak yerine ekleme yaptı. docker compose config ile durumu doğrulayın, ardından ya !override kullanın ya da ports öğesini temel dosyadan çıkarın.
FAQ
Compose, compose.override.yaml dosyasını otomatik olarak yükler mi?
Evet, docker compose komutunu herhangi bir -f bayrağı olmadan çalıştırdığınızda yükler. Compose, çalışma dizininde ve üst dizinlerinde compose.yaml veya docker-compose.yaml dosyalarını arar; eğer yanında bir geçersiz kılma (override) dosyası varsa, bu dosya ikinci sırada yüklenir. Tanınan dosya adları compose.override.yaml, compose.override.yml, docker-compose.override.yml ve docker-compose.override.yaml şeklindedir. Herhangi bir -f kullanımı bu özelliği devre dışı bırakır, bu nedenle docker compose -f compose.yaml up yalnızca tek bir dosya okur.
Birden fazla -f dosyası hangi sırayla birleştirilir?
Soldan sağa doğru. Compose, yapılandırmayı dosyaları sağladığınız sırayla oluşturur; her dosya kendinden öncekileri geçersiz kılar ve onlara ekleme yapar. Bu nedenle, satırdaki son dosya herhangi bir çakışma durumunda geçerli olur. Aynı liste, o projedeki her komut için kullanılmalıdır; COMPOSE_FILE=compose.yaml:compose.prod.yaml parametresinin amacı budur.
Geçersiz kılmış olmama rağmen portum neden hala yayında?
Çünkü ports girdileri; ip, target, published ve protocol değerlerinin tamamı ile tanımlanır. Temel dosyadaki 8080:80 değerine karşı yapılan bir 127.0.0.1:8080:80 geçersiz kılması, ip kısmında farklılık gösterir. Bu yüzden Compose bunu ikinci bir port olarak değerlendirir ve her ikisini de tutar. docker compose config komutunu çalıştırdığınızda iki girdiyi de görebilirsiniz. Compose v2.24.4 veya daha yeni bir sürümde ports: !override kullanın ya da temel dosyada ports tanımına yer vermeyin; böylece birleştirilecek bir değer kalmaz.
include ve -f arasındaki fark nedir?
-f, birden fazla dosyayı tek bir uygulama üzerinde katmanlar ve her dosyadaki tüm göreli yollar, ilk dosyanın dizinine göre çözümlenir. include ise ayrı bir Compose uygulamasını içeri aktarır; dahil edilen her dosya kendi proje dizinini korur, bu nedenle göreli yollar kendi dizinlerine göre çözümlenir. Kendi yığınınızın ortam katmanları için -f, başka bir yerde tutulan bir parça için ise include kullanın. include, Compose v2.20.0 veya daha yeni bir sürüm gerektirir.
Temel dosyanın belirlediği bir değeri nasıl kaldırırım?
Compose v2.24 veya daha yeni sürümlerde !reset etiketini kullanın. Geçersiz kılma dosyasında ports: !reset [] veya MY_VAR: !reset null yazın; böylece öznitelik varsayılan değerine veya null değerine döner. Etikete verdiğiniz değer zorunludur ancak göz ardı edilir. Bir özniteliği temizlemek yerine değiştirmek istiyorsanız !override bunu sağlar; bu özellik v2.24.4 veya daha yeni bir sürüm gerektirir.