Merhabalar. Bugünkü yazımızda Docker Compose ile birden fazla konteyneri nasıl tek bir komutla yöneteceğinizi anlatacağım. Eğer Docker’ı biliyorsunuz ama her seferinde docker run ile ayrı ayrı konteyner başlatmaktan yorulduysanız, bu yazı tam size göre.
Docker Compose Nedir?
Diyelim ki bir web uygulamanız var. Bu uygulamanın bir backend servisi (mesela Python FastAPI), bir veritabanı (PostgreSQL) ve bir önbellek (Redis) servisine ihtiyacı var. Bunların her birini ayrı ayrı docker run komutlarıyla başlatıp, ağlarını ayarlayıp, sıralarını takip etmek oldukça zahmetli olur. İşte Docker Compose tam bu noktada devreye girer.
Docker Compose, tüm servislerinizi tek bir YAML dosyasında tanımlamanızı sağlar. Bu dosyada hangi imajları kullanacağınızı, hangi portları açacağınızı, hangi ortam değişkenlerini tanımlayacağınızı, verileri nereye kaydedeceğinizi yazarsınız ve tek bir komutla hepsini başlatırsınız.
Önemli bir not: 2025’in başından itibaren Docker Compose v1 (docker-compose komutu) tamamen kaldırıldı. Artık docker compose (tire yok) olarak kullanıyoruz. Ayrıca compose.yaml dosyasında artık version: alanı belirtmenize gerek yok — Compose spesifikasyonu v5.0.0 ile bu alan kullanımdan kaldırıldı.
Neden Docker Compose Kullanmalısınız?
- Tek komut, tüm yığın:
docker compose upile tüm servisleri başlatın,docker compose downile hepsini durdurun - Ağ yönetimi otomatik: Tüm servisler otomatik olarak aynı ağa konur ve birbirlerini isimle bulabilirler
- Veri kalıcılığı: Volume tanımları ile veritabanı verileriniz konteyner durdurulsa bile kaybolmaz
- Taşınabilirlik:
compose.yamldosyasını paylaşın, başkası aynı ortamı tek komutla kursun - Geliştirme kolaylığı:
docker compose watchile kod değiştikçe otomatik senkronizasyon yapılır
1. Kurulum
Docker Compose, Docker Desktop ile birlikte otomatik olarak gelir. GNU/Linux’ta Docker’ı kurduğunuzda eklenti olarak yüklenir. Kurulu olup olmadığını kontrol edelim:
docker compose version
Çıktınız şuna benzer olmalı:
Docker Compose version v2.40.2
Eğer Docker yüklü değilse, Docker’ın resmi sitesinden işletim sisteminize uygun kurulumu yapın.
2. Proje Yapısını Oluşturma
Basit bir blog uygulaması hayal edelim: Python (FastAPI) backend, PostgreSQL veritabanı ve Redis önbellek. Proje klasörümüzü oluşturalım:
mkdir blog-uygulamasi
cd blog-uygulamasi
İçinde şu dosyaları oluşturacağız:
blog-uygulamasi/
├── compose.yaml
├── app/
│ ├── main.py
│ └── requirements.txt
3. compose.yaml Dosyasını Yazma
Projenin kalbi compose.yaml dosyasıdır. (Eski adıyla docker-compose.yml — ikisi de çalışır ama yeni standart compose.yaml‘dır.) Bu dosyada üç servis tanımlayacağız:
services:
web:
build: ./app
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgresql://kullanici:sifre@db:5432/blogdb
- REDIS_URL=redis://cache:6379
depends_on:
db:
condition: service_healthy
cache:
condition: service_started
develop:
watch:
- action: sync
path: ./app
target: /app
restart: unless-stopped
db:
image: postgres:17-alpine
environment:
- POSTGRES_USER=kullanici
- POSTGRES_PASSWORD=sifre
- POSTGRES_DB=blogdb
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U kullanici -d blogdb"]
interval: 5s
timeout: 5s
retries: 5
restart: unless-stopped
cache:
image: redis:8-alpine
restart: unless-stopped
volumes:
postgres_data:
Bu dosyada neler yaptık adım adım açıklayalım:
web servisi: Uygulamamızın kendisi. build: ./app ile app klasöründeki Dockerfile ile imaj oluşturulur. Port 8000 dışa açılır. depends_on ile veritabanı sağlıklı olmadan başlamamasını sağladık. develop.watch kısmı ise 2025’te production-ready olan watch mode özelliği — kodu değiştirdiğinizde otomatik konteynere senkronlar.
db servisi: PostgreSQL 17’nin Alpine sürümünü kullanıyoruz. Alpine imajlar çok daha küçüktür (yaklaşık 100MB tasarruf). healthcheck ile veritabanının hazır olup olmadığını kontrol ediyoruz. volumes kısmında postgres_data volume’una verileri kaydediyoruz — konteyner silinse bile veriler kaybolmaz.
cache servisi: Redis 8’in Alpine sürümü. Önbellekleme için. En basit servis — sadece imajı çekip çalıştırıyoruz.
4. Uygulama Kodunu Hazırlama
app/main.py dosyasını oluşturalım. Bu basit bir FastAPI uygulaması olacak:
from fastapi import FastAPI
import os
import psycopg2
import redis
import json
app = FastAPI()
# Redis bağlantısı
redis_client = redis.Redis(host="cache", port=6379, decode_responses=True)
# PostgreSQL bağlantısı
db_url = os.environ.get("DATABASE_URL")
@app.get("/")
def ana_sayfa():
# Önce önbellekten kontrol et
cached = redis_client.get("ziyaret_sayisi")
if cached:
return {"ziyaret": int(cached), "kaynak": "redis"}
# Veritabanından oku (basit örnek)
sayi = 0
try:
conn = psycopg2.connect(db_url)
cur = conn.cursor()
cur.execute("CREATE TABLE IF NOT EXISTS sayac (id SERIAL PRIMARY KEY, deger INTEGER DEFAULT 0)")
cur.execute("INSERT INTO sayac (deger) VALUES (1)")
cur.execute("SELECT COUNT(*) FROM sayac")
sayi = cur.fetchone()[0]
conn.commit()
cur.close()
conn.close()
except Exception as e:
return {"hata": str(e)}
# Önbelleğe kaydet (60 saniye TTL)
redis_client.setex("ziyaret_sayisi", 60, sayi)
return {"ziyaret": sayi, "kaynak": "postgres"}
app/requirements.txt dosyasına bağımlılıkları yazalım:
fastapi==0.115.0
uvicorn==0.32.0
psycopg2-binary==2.9.10
redis==5.2.0
app/Dockerfile dosyasını da oluşturalım:
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
5. Uygulamayı Başlatma
Artık her şey hazır. Terminalde proje kök dizinine gidin ve şu komutu çalıştırın:
docker compose up -d
-d bayrağı servisleri arkaplanda başlatır. Çıktınız şuna benzer olmalı:
[+] Running 4/4
✔ Network blog-uygulamasi_default Created
✔ Volume blog-uygulamasi_postgres_data Created
✔ Container blog-uygulamasi-db-1 Healthy
✔ Container blog-uygulamasi-cache-1 Started
✔ Container blog-uygulamasi-web-1 Started
Docker Compose otomatik olarak:
- Bir ağ oluşturdu (blog-uygulamasi_default)
- PostgreSQL için bir volume oluşturdu
- Önce veritabanını ve Redis'i başlattı, sağlıklı olduklarını doğruladı, sonra web servisini başlattı
Servislerin durumunu kontrol edin:
docker compose ps
Çıktı:
NAME STATUS PORTS
blog-uygulamasi-web-1 Up 3 seconds 0.0.0.0:8000->8000/tcp
blog-uygulamasi-db-1 Up 10 seconds (healthy) 5432/tcp
blog-uygulamasi-cache-1 Up 10 seconds 6379/tcp
Tarayıcınızda http://localhost:8000 adresine gidin. Şöyle bir yanıt görmelisiniz:
{"ziyaret": 1, "kaynak": "postgres"}
Sayfayı yenileyin. Bu sefer Redis’ten gelecek:
{"ziyaret": 1, "kaynak": "redis"}
6. Logları İnceleme
Servislerin loglarını görmek için:
docker compose logs -f
Sadece belirli bir servisin loglarını görmek için:
docker compose logs -f web
7. Uygulamayı Durdurma ve Temizleme
İşiniz bittiğinde tüm servisleri durdurmak için:
docker compose down
Bu komut konteynerleri durdurur ve ağları kaldırır. Volume’lar korunur — verileriniz kaybolmaz. Eğer verileri de silmek isterseniz:
docker compose down -v
Yaygın Hatalar ve Çözümleri
Hata: “port is already allocated”
8000 portu başka bir uygulama tarafından kullanılıyor olabilir. compose.yaml dosyasında portu değiştirin, örneğin "8001:8000" yapın. Hangi port kullanımda olduğunu görmek için:
sudo lsof -i :8000
Hata: “db container is unhealthy”
Veritabanı sağlıksız olarak işaretleniyorsa, healthcheck süresini artırın. Bazen yavaş sistemlerde PostgreSQL’in başlaması daha uzun sürer:
healthcheck:
test: ["CMD-SHELL", "pg_isready -U kullanici -d blogdb"]
interval: 10s
timeout: 10s
retries: 10
Hata: “Connection refused” web servisinde
Bu genellikle depends_on ile veritabanının hazır olmasını beklemediğinizde olur. condition: service_healthy kullandığınızdan emin olun. Eğer direkt depends_on: ["db"] yazarsanız Docker sadece konteynerin başlamasını bekler, servisin hazır olmasını değil.
Hata: “Cannot connect to the Docker daemon”
Docker servisi çalışmıyor olabilir:
sudo systemctl start docker
macOS’ta Docker Desktop’ı açın.
İpuçları
.envdosyası kullanın: Şifrelericompose.yamliçine yazmak yerine.envdosyasına koyun. Compose otomatik olarak okur:
.env dosyası:
POSTGRES_USER=kullanici
POSTGRES_PASSWORD=cokGuvenliSifre
compose.yaml'da:
environment:
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
docker initile hızlı başlangıç: Docker’ın 2025’te eklenendocker initkomutu projenizi tarayıp otomatikcompose.yaml,Dockerfileve.dockerignoreoluşturur. Sıfırdan başlıyorsanız çok zaman kazandırır.
- Watch mode aktif kullanın:
docker compose watchkomutunu çalıştırın. Kodunuzu değiştirdiğinizde değişiklikler otomatik olarak konteynere senkronlanır, imajı yeniden oluşturmanıza gerek kalmaz. 2025’in ortasından itibareninitial_syncözelliği de eklendi —watchbaşladığı anında tüm dosyaları bir kere senkronlar.
Sonuç
Docker Compose, çoklu konteynerli uygulamaları yönetmenin en pratik yoludur. Tek bir YAML dosyası ile tüm servislerinizi tanımlar, tek komutla başlatır ve durdurursunuz. compose.yaml dosyası projenizin nasıl çalıştığını belgeleyen bir tarif gibidir — başkası projeyi indirdiğinde ne yapacağını hemen anlar.
Bu yazıda FastAPI, PostgreSQL ve Redis’den oluşan basit bir yığın kurduk. Kendi projelerinizde farklı servisler ekleyebilir, daha karmaşık ağ yapıları kurabilir, GPU desteği (yeni gpus: sözdizimi ile) ekleyebilirsiniz. Okuduğunuz için teşekkür ederim!