Compose'un Evrimi: docker-compose v1'den Compose v2 ve v5'e, Compose Specification ve 2024–2026 Özellikleri
Bugünkü Compose tek bir şemayı, Compose Specification'ı uygular: eski dosyalardaki version alanı yok sayılır, 2.x ve 3.x formatlarının alanları birlikte geçerlidir ve v5, Go SDK'sı ve Bake'e devredilen build'leriyle v2'nin devamıdır. Bookstore'un 2019 tarihli docker-compose.yml'ini docker-compose 1.25.5, 1.29.2 ve Compose 5.7.1 ile çalıştırıp farkları gerçek çıktılarla görüyor, 2024–2026 özelliklerinin haritasını ve bir geçiş kontrol listesini çıkarıyoruz.
Sürüm notu: Bu yazıdaki örnekler Docker Compose 5.7.1, docker-compose 1.29.2 ve 1.25.5 (Docker Hub'daki resmi docker/compose image'ları), Docker Engine 29.9.3 ve SQL Server 2019/2025 image'larıyla denenmiştir.
Önce Tahmin Et
Bookstore'un ilk sürümü 2019'da yazılmış bir docker-compose.yml ile başlıyordu: en üstte version: "3.8", API'de deploy.resources.limits.memory: 512M, bağımlılık için links ve kısa depends_on. O dönemin docker-compose'u bu memory limit'i container'a uyguluyor muydu? Bugünkü Compose 5.7.1 aynı dosyayı çalıştırır mı, çalıştırırsa limit uygulanır mı? Bir de: o günkü docker-compose binary'sini bugünkü Docker Engine 29'un yanına koysan ne olur?
Kısa Tarihçe: Araç Sürümleri ve Dosya Formatları
Compose'un geçmişinde iki ayrı numaralandırma birbirine karışıyor: aracın sürümü ve dosya formatının sürümü. Docker'ın Compose tarihçesi sayfası ve sürüm notlarından derlediğimiz özet:
| Dönem | Araç | Dosya | Not |
|---|---|---|---|
| 2014 | Compose v1 (Python), docker-compose | Format 1 (top-level services yok) | Bugünkü Compose bu dosyaları çalıştırmıyor |
| 2016 | v1 1.6 | Format 2.x | Tek makine; depends_on condition'ları ve mem_limit burada |
| 2017 | v1 1.10 | Format 3.x | Swarm odaklı; deploy geldi, condition'lar düştü |
| 2020–2022 | Compose v2 (Go), docker compose | Compose Specification | 2.x ve 3.x tek şemada birleşti; v2 Nisan 2022'de genel kullanıma açıldı (GA) |
| Haziran 2023 | v1 | v1'in desteği bitti | |
| Aralık 2025 | Compose v5 | Compose Specification | İşlevsel olarak v2'yle aynı; resmi Go SDK, build'ler Bake'te |
2.x ve 3.x formatlarının ayrışması yıllarca kafa karıştırdı. Format 2.x tek makinede çalışan uygulamalar içindi: depends_on'da condition: service_healthy ve mem_limit gibi container ayarları vardı. Format 3.x aynı dosyayı Docker Swarm'a (docker stack deploy) götürmek için tasarlandı: kaynak ve replica ayarları deploy altına taşındı, Swarm'da anlamı olmayan depends_on condition'ları kaldırıldı. Sonuç: tek makinede çalışan ekipler "yeni" olduğu için 3.x'e geçti, healthcheck koşullarını ve memory limit'lerini kaybetti ve README'lere wait-for-it script'leri girdi. Compose Specification bu ayrımı bitirdi: bugün tek bir şema var ve iki formattan gelen alanların hepsi geçerli.
Eski Bir Dosyayı Okumak
Bookstore'un 2019 tarihli varsayımsal dosyasını labs/compose/07-legacy/'e koyduk. Docker serisinin 15. yazısında aynı dönemin Dockerfile'ını okumuştuk; bu onun Compose tarafı:
# Bookstore'un 2019'daki başlangıcı: Compose dosya formatı 3.8, docker-compose (v1) ile çalıştırılıyor
version: "3.8"
services:
db:
image: mcr.microsoft.com/mssql/server:2019-latest
container_name: composeseries-legacy-db
environment:
- ACCEPT_EULA=Y
- SA_PASSWORD=${SA_PASSWORD}
ports:
- "1433:1433"
volumes:
- dbdata:/var/opt/mssql
api:
image: bookstore/api:2019
links:
- db
depends_on:
- db
environment:
- ASPNETCORE_URLS=http://+:80
- ConnectionStrings__Default=Server=db;Database=BookstoreDb;User Id=sa;Password=${SA_PASSWORD}
ports:
- "5000:80"
deploy:
resources:
limits:
memory: 512M
web:
image: bookstore/web:2019
depends_on:
- api
ports:
- "80:8080"
volumes:
dbdata:
Dosyanın her satırı o dönemin bir alışkanlığını taşıyor: version, liste biçiminde environment, container_name, links, port 80, sa ile bağlanan uygulama, şifrenin connection string'in içinde olması. Hepsine kontrol listesinde döneceğiz; önce dosyanın üç farklı Compose'da ne yaptığına bakalım.
v1 ile 2.x ve 3.x Farkı: Gerçek Çıktılarla
Docker'ın Docker Hub'daki resmi docker/compose image'ları eski sürümleri bir container'da çalıştırmayı sağlıyor; Docker socket'ini bağlayıp v1'in son sürümlerinden 1.25.5'i denedik:
V1="docker run --rm -v /var/run/docker.sock:/var/run/docker.sock -v $PWD:$PWD -w $PWD -e SA_PASSWORD docker/compose:1.25.5"
$V1 -p composeseries-legacy up --no-start
Some services (api) use the 'deploy' key, which will be ignored. Compose does not support 'deploy' configuration - use `docker stack deploy` to deploy to a swarm.
client version 1.38 is too old. Minimum supported API version is 1.40, please upgrade your client to a newer version
İki mesaj, iki ayrı tarih dersi. Birincisi ilk sorunun cevabı: 1.25.5, 3.x dosyasındaki deploy'u açıkça yok sayıyor. Production sunucusunda "API 512 MB ile sınırlı" sanan bir ekip aslında sınırsız bir container çalıştırıyordu. İkincisi üçüncü sorunun cevabı: 1.25.5 Docker API'sinin 1.38 sürümüyle konuşuyor, Engine 29 ise en az 1.40 istiyor; eski binary bugünkü bir sunucuda hiçbir şey başlatamıyor. Docker serisinin 15. yazısı Engine'in minimum API sürümünü ayrıntılı anlatıyor.
Aynı dosyada depends_on'u healthcheck koşuluyla yazınca 3.8 şeması reddediyor; aynı koşul version: "2.4"'te (ve deploy yerine mem_limit: 512m ile) config'ten sorunsuz geçti:
The Compose file './cond.yml' is invalid because:
services.api.depends_on contains an invalid type, it should be an array
v1'in son sürümü 1.29.2 ise farklı davrandı: aynı 3.8 dosyasıyla container'lar oluştu ve API'nin memory limit'i 536870912 byte, yani 512 MB'tı. v1, 1.27'den itibaren Compose Specification'ı uygulamaya başlamıştı; config çıktısında version alanı yoktu ve kısa depends_on uzun forma (condition: service_started) açılmıştı. Yani "3.x'te deploy tek makinede çalışmaz" bilgisi, hangi v1 sürümünü kullandığına göre doğru ya da yanlış.
Aynı Dosya Bugün: Compose 5.7.1
docker compose -p composeseries-legacy up --no-start
docker inspect composeseries-legacy-api-1 --format "memory={{.HostConfig.Memory}}"
WARN[0000] /home/user/bookstore/labs/compose/07-legacy/docker-compose.yml: the attribute `version` is obsolete, it will be ignored, please remove it to avoid potential confusion
Network composeseries-legacy_default Created
Container composeseries-legacy-db Created
Container composeseries-legacy-api-1 Created
Container composeseries-legacy-web-1 Created
memory=536870912
Dosya çalışıyor; tek uyarı version için. Limit uygulanıyor. Bir fark container adlarında: 1.29.2 composeseries-legacy_api_1 oluşturmuştu, Compose v2 ve sonrası composeseries-legacy-api-1. Ayıraç alt çizgiden tireye döndü; Docker'ın geçiş dokümanına göre amaç adları hostname olarak kullanılmaya uygun hâle getirmek, çünkü alt çizgi hostname'lerde geçerli bir karakter değil. Container adını grep'leyen eski izleme script'leri bu yüzden v2'ye geçişte sessizce bozuldu. container_name verilen db ise iki sürümde de aynı adı aldı: sabit bir ad, aynı makinede projenin ikinci bir kopyasının çalışmasını ve service'in ölçeklenmesini engelliyor.
v5: Neden 3 ve 4 Yok, SDK ve Bake
Compose v2'nin 2.40'tan sonraki sürümü Aralık 2025'te 5.0.0 olarak çıktı. Sürüm notlarına göre 3.0.0 bilerek atlandı, çünkü "Compose v3" adı eski 2.x ve 3.x dosya formatlarıyla karışacaktı; 4.0.0 da bu eski dönemden açıkça ayrışmak için atlandı. v5 işlevsel olarak v2'nin devamı; iki somut değişiklik var. Birincisi, Compose artık resmi bir Go SDK olarak da kullanılabiliyor: başka bir uygulama Compose projelerini CLI'ı çağırmadan yükleyip doğrulayabiliyor ve yönetebiliyor. İkincisi, Compose'un kendi içindeki BuildKit builder'ı kaldırıldı ve build'ler Bake'e devredildi. Bu geçiş kademeliydi: 2.37'de (Haziran 2025) Bake varsayılan oldu ve COMPOSE_BAKE=false ile kapatılabiliyordu, 2.39'da bu ayar deprecated oldu, 5.0'da tek yol Bake. Bu serideki bütün build çıktılarının docker buildx bake çıktısı gibi görünmesinin sebebi bu.
2024–2026 Özellik Haritası
Compose son iki yılda yalnız sürüm numarası değiştirmedi. Bookstore açısından önemli olanlar, Compose Specification'daki sürüm işaretleri ve GitHub sürüm notlarıyla:
| Özellik | Sürüm | Bookstore'da |
|---|---|---|
profiles | v1 1.28 (2021) | Redis, Mailpit, sqlcmd (3. yazı) |
include | 2.20 | compose.db.yaml (5. yazı) |
develop.watch, initial_sync | 2.22, 2.39.4 | API rebuild, Angular sync (4. yazı) |
!reset, !override | 2.24 | Port ve build sıfırlama (5. ve 6. yazı) |
provider service'leri | 2.36 | Kullanmadık |
models | 2.38 | Kullanmadık |
| Reconciliation plan | 5.2 | Arka planda; --dry-run ile görünür |
pre_start (init container) | 5.3 | Migration için alternatif (2. yazı) |
jobs (yalnız elle tetiklenen) | 5.6 | Yedekleme job'ı (6. yazı) |
Kullanmadıklarımız neden kullanılmadı? provider, bir service'i container yerine Compose'un dışarıdan çağırdığı bir binary'nin yönettiği bir kaynakla temsil etmeyi sağlıyor; Bookstore'un bütün parçaları zaten container. models, Docker Model Runner üzerinden bir LLM'i service'lere bağlıyor ve endpoint'ini environment variable olarak veriyor; Bookstore'da LLM kullanan bir özellik yok. Reconciliation plan ise görünmeyen ama hissedilen bir değişiklik: Compose 5.2'den beri up, mevcut durumla istenen durumu karşılaştırıp bir plan çıkarıyor ve 5.7'de create ile start adımları tek bir plan üzerinde çalışıyor. Planı uygulamadan görmek için --dry-run:
TAG=1.3.1 docker compose up -d --dry-run
Container composeseries-prod-db-1 Running
Container composeseries-prod-api-1 Recreate
Container composeseries-prod-api-1 Recreated
Container composeseries-prod-web-1 Recreate
Container composeseries-prod-web-1 Recreated
Container composeseries-prod-migrate-1 Starting
...
Container composeseries-prod-web-1 Started
Bir önceki yazıdaki 11,5 saniyelik kesintinin sebebi (web'in de yeniden oluşturulması ve migrate'in yeniden çalışması) deploy'dan önce bu çıktıda görünüyordu; komuttan sonra ps hâlâ 1.3.0'ı gösteriyordu. 5.5'teki image digest reconciliation değişikliği de not edilmeye değer: sürüm notları, güncellemeden sonraki ilk up'ta bazı container'ların bir kez yeniden oluşturulabileceğini söylüyor; production'da Compose'u güncellemeyi bir deploy penceresine denk getir.
Kontrol Listesi: docker-compose.yml'den compose.yaml'a
- Dosyayı
compose.yamlolarak yeniden adlandır,versionsatırını sil, gerekirsename:ekle; komutlarıdocker-compose'dandocker compose'a çevir ve container adını grep'leyen script'leri tire ayracına göre düzelt. links'i kaldır (aynı network'teki service adları zaten çözülüyor),container_name'i yalnız gerçekten sabit bir ad gerekiyorsa tut.- Kısa
depends_on'u healthcheck'li uzun forma çevir, wait-for-it script'lerini sil, şema adımını birmigrateservice'ine ya dapre_start'a taşı (2. yazı). SA_PASSWORD'u (Microsoft Learn'de deprecated)MSSQL_SA_PASSWORD'a, mümkünse secret dosyasına çevir; uygulamayısaile değil yetkisi daraltılmış bir login'le bağla, şifreleri environment'tansecrets:'a taşı (3. yazı).- .NET 8 ve sonrasında container port'u 80 değil 8080:
ASPNETCORE_URLS=http://+:80ve"5000:80"satırlarını güncelle (Docker serisinin 15. yazısı). mem_limitiledeploy.resources.limits'ten birini seç; ikisi birlikte yazılırsa tutarlı olmaları gerekiyor. Sonradocker inspectile limitin gerçekten uygulandığını doğrula.- Geliştirme eklerini
compose.override.yaml'a, ortam farklarını-fdosyalarına ayır; çözülmüş sonucudocker compose configile gözden geçir (5. yazı). Production'da image'ları immutable tag'le çek; digest'e sabitlemek içindocker compose config --lock-image-digestsbir override dosyası üretiyor.
Üretimde Dikkat
Eski bir sunucuyu devraldığında ilk iş, orada gerçekten hangi Compose'un çalıştığını görmek: docker compose version ve docker-compose version farklı binary'ler olabilir ve Ubuntu'nun kendi paket deposundan kurulmuş bir docker-compose hâlâ Python'lu v1 çıkabilir. Geçişte davranış farkı yaratan noktalar deploy'un (1.25 ve öncesinde) yok sayılmasından uygulanmasına geçiş (limitler birden devreye girer ve sınırın üstünde bellek kullanan bir uygulama OOM (Out of Memory; bellek limiti aşılınca kernel'in process'i öldürmesi) ile ölebilir), container adlarının değişmesi ve version'a bağlı davranışların kaybolması. v1'in başlattığı bir projede v2 ve sonrasının ilk up'ı container'ları yeni adlarla yeniden oluşturuyor, yani kısa bir kesinti bekle. Geçişi önce aynı sunucuda ayrı bir proje adıyla (-p) dene ve volume adlarının (<proje>_<ad>) eskisiyle aynı kalmasına dikkat et; proje adı değişirse Compose yeni, boş bir volume oluşturur ve veritabanı "silinmiş" görünür. CI'da Compose sürümünü sabitle: GitHub-hosted runner'lar ve Docker Desktop farklı sürümlerle gelir ve pre_start ya da jobs gibi yeni alanlar eski bir Compose'da şema hatası verir.
Sık Düşülen Tuzaklar
version: "3.8"yazınca Compose'un 3.8 kurallarını uyguladığını sanmak: alan yok sayılıyor, geçerli olan Compose Specification.- Eski bir blog yazısındaki "3.x'te
depends_oncondition yok" bilgisini bugüne taşımak: Compose Specification'da condition'lar geri geldi. - Bir Docker Desktop'ta çalışan dosyanın sunucuda da çalışacağını varsaymak: Docker Desktop 4.99 Compose 5.7.0 ile geliyor, sunucudaki Compose çok daha eski olabilir;
pre_start5.3,jobs5.6 gerektiriyor. - Proje adını değiştirip eski volume'ün kullanılmaya devam edeceğini beklemek: volume adı proje adıyla öneklenir.
- Tireli
docker-composekomutunun her yerde v1 olduğunu ya da her yerde v5 olduğunu varsaymak: Docker Desktop'ta ve resmi image'larda v5'e bağlı bir ad, eski bir Linux paketinde v1.
Küçük Bir Deneme
★☆☆ labs/compose/07-legacy/docker-compose.yml'i kontrol listesine göre bugünkü bir compose.yaml'a çevir ve docker compose config çıktısını iki dosya için karşılaştır. Hangi satırlar çıktıda hiç değişmedi?
★★☆ docker run docker/compose:1.29.2 ile çevirdiğin yeni dosyayı çalıştırmayı dene. secrets, include ya da develop alanlarından hangisi hata veriyor? Hata mesajlarından Compose Specification'ın hangi kısımlarının v1'e hiç girmediğini çıkar.
★★★ docker compose config --lock-image-digests ile production dosyaları için bir digest override'ı üret ve deploy script'ini bu dosyayı da -f ile kullanacak şekilde değiştir. 1.3.0 tag'ini registry'de başka bir image'a çevirip (Docker serisinin 4. yazısındaki deney) sunucunun hangi image'ı çalıştırdığını kontrol et.
Docker Compose serisinin sonuna geldik: Bookstore artık geliştirmede tek komutla ayağa kalkıyor, CI'da temiz bir veritabanıyla test ediliyor ve tek bir sunucuda immutable tag'lerle deploy ediliyor. Bir sunucunun yetmediği yer Kubernetes serisinin başladığı yer; aynı uygulamayı C# kodu üzerinden ayağa kaldırmak isteyenler için ilerideki Aspire ile Dağıtık Uygulamalar serisi, Compose'un AppHost karşılığını anlatacak.