Docker Compose, birden fazla konteyneri tek bir YAML dosyasında tanımlayıp tek komutla ayağa kaldırmanı sağlayan araçtır. Gerçek bir uygulama nadiren tek bir süreçten oluşur: bir web sunucusu, bir veritabanı ve çoğu zaman bir cache katmanı birlikte çalışır. Bunları tek tek docker run ile başlatmak hem yorucu hem de hataya açıktır. Bu yazıda web, PostgreSQL ve Redis servislerinden oluşan tipik bir yığını sıfırdan kuracak; servisler arası iletişim, kalıcı veri ve ortam değişkenleri gibi konuları pratik örneklerle göreceğiz.
Docker Compose neyi çözer?
Tek bir konteyner çalıştırmak kolaydır, ama gerçek projelerde işler hızla karmaşıklaşır. Her servisin kendi imajı, portu, ağı ve ortam değişkenleri vardır; üstelik bunların belirli bir sırada ayağa kalkması gerekir. Compose bu karmaşıklığı bildirimsel (declarative) bir dosyaya taşır:
- Tek kaynak: Tüm yığın
compose.yamliçinde tanımlıdır; ekibe arkadaşın repoyu klonlar vedocker compose upyazar. - Otomatik ağ: Compose servisler için ortak bir ağ kurar; konteynerler birbirine servis adıyla ulaşır.
- Tekrarlanabilirlik: Aynı dosya, geliştirici makinesinde de CI ortamında da aynı yığını üretir.
Not: Modern Docker sürümlerinde komut docker compose (boşluklu) biçimindedir; eski docker-compose (tireli) ikilisi hâlâ çalışsa da Compose V2 ile birlikte tireli sürüm yerini almıştır.
İlk compose.yaml dosyası
Bir klasör açıp içine compose.yaml oluşturalım. Önce sadece bir web servisi ve bir veritabanı tanımlayalım:
services:
web:
image: nginx:1.27-alpine
ports:
- "8080:80"
depends_on:
- db
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: secret
POSTGRES_DB: appdb
volumes:
- db-data:/var/lib/postgresql/data
volumes:
db-data:
Birkaç önemli ayrıntı: ports altındaki "8080:80" ifadesi, host makinedeki 8080 portunu konteynerin 80 portuna bağlar. depends_on web servisinin db'den sonra başlamasını sağlar (ancak db'nin hazır olmasını garanti etmez — buna birazdan döneceğiz). volumes ise veritabanı dosyalarını konteyner silinse bile koruyan kalıcı bir birim oluşturur.
Cache katmanı ekleme: Redis
Çoğu uygulama oturum, kuyruk veya önbellek için Redis kullanır. Üçüncü bir servis eklemek tek bir blok meselesidir:
services:
web:
image: nginx:1.27-alpine
ports:
- "8080:80"
depends_on:
- db
- cache
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: secret
POSTGRES_DB: appdb
volumes:
- db-data:/var/lib/postgresql/data
cache:
image: redis:7-alpine
command: ["redis-server", "--save", "60", "1"]
volumes:
db-data:
Burada güzel olan şu: web konteyneri içinden veritabanına db, Redis'e ise cache hostname'i üzerinden erişilir. Compose'un kurduğu dahili DNS, servis adını otomatik olarak doğru IP'ye çözer. Yani uygulama yapılandırmanda bağlantı dizesi postgres://app:secret@db:5432/appdb ve redis://cache:6379 biçiminde olur — sabit IP yazmana gerek yok.
Ortam değişkenleri ve .env dosyası
Parolaları YAML'a gömmek iyi bir fikir değildir. Compose, yanındaki .env dosyasını otomatik okur ve değişkenleri ${...} sözdizimiyle yerine koyar:
# .env
POSTGRES_PASSWORD=cok-gizli-bir-deger
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: appdb
.env dosyasını .gitignore'a ekleyerek sırların depoya girmesini engelle. Ekibin geri kalanı için örnek bir .env.example bırakmak iyi bir alışkanlıktır.
Sağlık kontrolü ile gerçek hazır olma
depends_on yalnızca başlatma sırasını belirler; veritabanının bağlantı kabul ettiğini garanti etmez. Web servisi, db daha açılırken bağlanmaya çalışıp hata verebilir. Çözüm bir healthcheck tanımlayıp depends_on'u condition: service_healthy ile birlikte kullanmaktır:
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: appdb
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d appdb"]
interval: 5s
timeout: 3s
retries: 5
web:
image: nginx:1.27-alpine
ports:
- "8080:80"
depends_on:
db:
condition: service_healthy
Artık web konteyneri, ancak pg_isready komutu başarılı döndükten sonra başlar. Bu, "veritabanı henüz hazır değil" hatalarının büyük kısmını ortadan kaldırır.
Günlük kullanım: temel komutlar
Yığını ayağa kaldırıp yönetmek için en sık kullanacağın komutlar şunlar:
docker compose up -d— tüm servisleri arka planda başlatır.docker compose ps— çalışan servisleri ve durumlarını listeler.docker compose logs -f web— belirli bir servisin günlüklerini canlı izler.docker compose exec db psql -U app appdb— çalışan db konteyneri içinde komut çalıştırır.docker compose down— servisleri durdurup kaldırır. Birimleri de silmek içindown -vkullan.
Bir imajda değişiklik yaptıysan (örneğin kendi Dockerfile'ını kullanıyorsan) docker compose up -d --build ile yeniden derleyebilirsin.
Sık Sorulan Sorular
docker-compose ile docker compose arasındaki fark nedir?
docker-compose (tireli) Python ile yazılmış eski V1 aracıdır. docker compose (boşluklu) ise Docker CLI'a entegre edilmiş Go tabanlı V2 eklentisidir. Güncel kurulumlarda boşluklu sürümü kullanman önerilir; YAML söz dizimi büyük ölçüde aynıdır.
Verilerim konteyner silinince kaybolur mu?
Hayır — volumes ile tanımlanan adlandırılmış birimler konteyner yaşam döngüsünden bağımsızdır. docker compose down konteynerleri kaldırsa da birim kalır; veriler bir sonraki up komutunda yine oradadır. Yalnızca down -v birimleri de siler.
Compose üretim ortamı için uygun mu?
Tek sunuculu küçük ve orta ölçekli kurulumlar için gayet uygundur. Ancak çok düğümlü, otomatik ölçeklenen ve kendini onaran sistemler için Kubernetes gibi bir orkestratör daha doğru tercihtir. Compose'u geliştirme ve sade üretim senaryoları için, Kubernetes'i büyük ölçek için düşün.
Yığınını tek dosyada toplamak mı istiyorsun? Docker Compose ile geliştirme ve dağıtım ortamını sadeleştirmek için benimle iletişime geç — projenin altyapısını birlikte planlayalım.