Docker Compose ile Çoklu Konteyner Geliştirme Ortamı Kurulumu: Adım Adım Rehber

Modern yazılım projelerinde uygulamalar artık tek başına çalışan monolitler değil; birbirleriyle konuşan, veri paylaşan ve birlikte çalışan servislerden oluşuyor. Bir web uygulamasını düşünün: arka planda bir veritabanı, oturumları yöneten bir cache mekanizması, belki asenkron işleri işleyen bir kuyruk sistemi… Her birini ayrı ayrı Docker container'ları ile çalıştırmak mümkün, peki ya bu parçaları tek bir komutla, tanımlı bir düzen içinde ayağa kaldırmak istersek? İşte tam bu noktada Docker Compose devreye giriyor. Bu Docker Compose kurulum rehberi, size çoklu konteynerli geliştirme ortamlarını nasıl kolayca yönetebileceğinizi, adım adım ve uygulamalı bir örnekle gösterecek.

Docker temellerine hakimseniz, yani image, container ve Dockerfile kavramlarını biliyorsanız, bu yazı sizin için doğal bir sonraki adım olacak. Signalique’in daha önce yayınladığı "Docker ile Konteynerleştirme" başlangıç rehberini okuduysanız, şimdi öğrendiklerinizi çoklu konteyner dünyasına taşımanın tam zamanı.

Docker Compose Nedir ve Neden Kullanmalısınız?

Docker Compose, Docker ile birlikte gelen resmi bir araçtır. Temel amacı, birden fazla container’dan oluşan uygulamaları tanımlamak ve yönetmek için gereken konfigürasyonu tek bir YAML dosyasında (compose.yml ya da eski adıyla docker-compose.yml) toplamaktır. V2 sürümüyle birlikte artık docker compose komutu (tire işareti olmadan) standart hale gelmiştir ve bu yazıda da V2 sözdizimini kullanacağız.

Peki neden tek tek docker run komutları vermek yerine Compose kullanalım?

  • Tutarlılık: Tüm servislerin nasıl başlayacağı, hangi portları kullanacağı, hangi çevre değişkenlerine sahip olacağı bir dosyada tanımlanır. Ekibinizdeki herkes aynı dosyayı kullanarak birebir aynı ortamı ayağa kaldırabilir.
  • Bağımlılık Yönetimi: Hangi servisin hangi sırayla başlayacağını tanımlayabilirsiniz. Örneğin, uygulamanızın veritabanı hazır olmadan çalışmaya başlamasını istemezsiniz.
  • Tek Komutla Yönetim: docker compose up diyerek tüm sistemi başlatabilir, docker compose down ile tamamen durdurabilirsiniz.
  • Geliştirme Hızı: Lokal geliştirme ortamlarını saniyeler içinde ayağa kaldırabilir, yeni katılan ekip arkadaşlarının onboarding süresini ciddi ölçüde kısaltabilirsiniz.

Özetle: Tek bir container’la çalışırken Docker komutları yeterli olabilir. Ancak sisteminiz büyüdükçe, konteynerleri bir orkestra şefi gibi yönetecek bir araca ihtiyacınız olur. İşte Compose tam olarak bunu yapar.

Compose Dosyasının Anatomisi: compose.yml

Bir Compose dosyası üç ana yapı taşından oluşur: services, networks ve volumes. Gelin her birini yakından inceleyelim.

  • services: Sisteminizi oluşturan her bir container bir servis olarak tanımlanır. Hangi imajın kullanılacağı, hangi portların eşleneceği, çevre değişkenleri gibi tüm ayarlar bu bölümde yer alır.
  • networks: Servisler arasındaki iletişimi sağlayan sanal ağları tanımlar. Varsayılan olarak Compose, tüm servislerin birbiriyle servis adları üzerinden haberleşebileceği ortak bir ağ oluşturur. Özel ağ ihtiyaçlarınız varsa burada tanımlayabilirsiniz.
  • volumes: Verilerin kalıcı olması gereken durumlar için kullanılır. Örneğin bir veritabanı container’ını yeniden başlattığınızda verilerin kaybolmaması için bir volume’e ihtiyacınız olur.

Basit bir compose.yml dosyası şu şekilde görünebilir:

services:
  web:
    image: nginx:latest
    ports:

      - "8080:80"
  db:
    image: postgres:15
    environment:
      POSTGRES_PASSWORD: sifre123
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  pgdata:

Adım Adım Örnek Senaryo: Node.js + PostgreSQL + Redis

Şimdi teoriden pratiğe geçelim. Gerçek bir senaryo üzerinden ilerleyelim: Bir Node.js uygulaması, arka planda PostgreSQL veritabanı ve oturum/önbellek yönetimi için Redis kullanan üçlü bir mimariyi Docker Compose ile ayağa kaldıracağız.

1. Proje Yapısını Oluşturun

Öncelikle basit bir proje dizini oluşturalım:

myapp/
├── app/
│   ├── package.json
│   └── server.js
├── Dockerfile
└── compose.yml

app/server.js – Express ile basit bir API:

const express = require('express');
const { Pool } = require('pg');
const redis = require('redis');

const app = express();
const port = 3000;

const pool = new Pool({
  host: process.env.DB_HOST || 'db',
  user: process.env.DB_USER || 'postgres',
  password: process.env.DB_PASSWORD || 'postgres',
  database: process.env.DB_NAME || 'myapp',
});

const redisClient = redis.createClient({
  url: `redis://${process.env.REDIS_HOST || 'cache'}:6379`,
});
redisClient.connect();

app.get('/', async (req, res) => {
  const visits = await redisClient.incr('visits');
  res.send(`Merhaba Dünya! Bu sayfa ${visits} kez ziyaret edildi.`);
});

app.listen(port, () => {
  console.log(`Uygulama http://localhost:${port} adresinde çalışıyor`);
});

package.json içinde express, pg ve redis bağımlılıklarını ekleyin.

2. Dockerfile Hazırlayın

Dockerfile uygulamamızın imajını oluşturacak:

FROM node:18-alpine
WORKDIR /app
COPY app/package*.json ./
RUN npm install
COPY app/ .
CMD ["node", "server.js"]
EXPOSE 3000

3. compose.yml Dosyasını Yazın

Gelelim işin kalbine. Üç servisi tanımlayalım:

services:
  web:
    build: .
    ports:

      - "3000:3000"
    environment:
      DB_HOST: db
      DB_USER: postgres
      DB_PASSWORD: postgres
      DB_NAME: myapp
      REDIS_HOST: cache
    depends_on:
      db:
        condition: service_healthy
      cache:
        condition: service_started

  db:
    image: postgres:15-alpine
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: myapp
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 5

  cache:
    image: redis:7-alpine
    ports:
      - "6379:6379"

volumes:
  pgdata:

Burada dikkat etmeniz gereken birkaç önemli nokta var:

  • depends_on ile condition kullanımı: Servis bağımlılıklarını tanımlarken depends_on tek başına container’ın başladığını garanti etse de, uygulamanın hazır olduğunu garanti etmez. condition: service_healthy kullanarak, web servisimizin ancak PostgreSQL servisi healthcheck’ten başarıyla geçtikten sonra başlamasını sağlıyoruz. Bu, “veritabanı bağlantısı reddedildi” gibi hataların önüne geçer.
  • Servis isimleri ağ adresi olur: Uygulama kodunda veritabanına db host adıyla, Redis’e ise cache host adıyla bağlanıyoruz. Compose, her servis adını otomatik olarak DNS’e ekler, böylece IP adresiyle uğraşmanıza gerek kalmaz.

4. Sistemi Başlatın ve Test Edin

Artık her şey hazır. Tek bir komutla tüm sistemi başlatabilirsiniz:

docker compose up -d

Bu komut, önce imajları oluşturacak (ya da hazır imajları indirecek), ardından tüm servisleri doğru sırayla başlatacak ve container’lar arası ağ iletişimini otomatik olarak kuracaktır. Tarayıcınızdan http://localhost:3000 adresine gittiğinizde ziyaret sayacının her yenilemede arttığını göreceksiniz. Redis sayesinde bu sayaç container’ı yeniden başlatsanız bile sıfırlanmaz (volume kullanırsanız).

Sık Kullanılan Docker Compose Komutları

Compose ile çalışırken en sık ihtiyaç duyacağınız komutları bilmek, iş akışınızı hızlandırır. İşte elinizin altında bulunması gerekenler:

Komut Görevi
docker compose up -d Tüm servisleri arka planda başlatır.
docker compose down Tüm container’ları durdurur ve siler. Varsayılan olarak volume’leri silmez; silmek için -v ekleyin.
docker compose build Servislerin imajlarını yeniden oluşturur. Kod değişikliğinden sonra --build bayrağı ile birlikte kullanılır.
docker compose logs -f Tüm servislerin loglarını canlı olarak takip eder. Belirli bir servis için docker compose logs -f web kullanabilirsiniz.
docker compose exec web sh Belirtilen servis container’ı içinde interaktif bir kabuk açar. Hata ayıklama için idealdir.
docker compose ps Çalışan container’ların durumunu listeler.
docker compose restart cache Sadece belirtilen servisi yeniden başlatır.
docker compose stop Servisleri durdurur ancak container’ları silmez. start ile tekrar başlatabilirsiniz.

Docker Compose V2 Özellikleri ve İpuçları

Compose V2, klasik docker-compose (Python ile yazılmış) yerine Go ile yazılmış ve Docker CLI’a tam entegre olmuş yeni nesildir. Bu geçişin getirdiği bazı avantajlar şunlardır:

  • docker compose komutu: Artık tire işareti olmadan, doğrudan Docker komutunun bir alt komutu olarak çalışır. Eski docker-compose hala desteklenir, ancak yeni projelerde V2 kullanmanız önerilir.
  • watch modu (deneysel): Compose V2.22 ile gelen bu özellik, dosya değişikliklerini izleyerek container içinde otomatik güncelleme yapmanıza olanak tanır. Örneğin bir Node.js projesinde kaynak kodu değiştiğinde container’ı yeniden başlatmadan değişiklikleri senkronize edebilirsiniz. compose.yml içinde şu şekilde tanımlanır:
services:
  web:
    build: .
    develop:
      watch:

        - action: sync
          path: ./app
          target: /app
        - action: rebuild
          path: package.json

Bu yapılandırma ile docker compose watch komutunu çalıştırdığınızda, ./app dizinindeki değişiklikler container içine anında yansır, package.json değişirse container yeniden build edilir. Neredeyse hot-reload benzeri bir deneyim sunar.

Sonuç

Docker Compose ile çoklu konteyner geliştirme ortamı kurmak, başlangıçta karmaşık görünse de bir kez kavrandığında yazılım geliştirme sürecinizi kökten değiştirecek bir beceridir. Geliştirme ortamlarının ekip içinde tutarlı olması, yeni katılan geliştiricilerin dakikalar içinde kod yazmaya başlayabilmesi ve "benim bilgisayarımda çalışıyordu" problemine kesin çözüm getirmesi, Compose'u vazgeçilmez kılar.

Bu yazıda öğrendikleriniz, Docker yolculuğunuzda önemli bir kilometre taşıdır. Eğer Docker temellerini henüz tazelemek isterseniz, Signalique’in "Docker ile Konteynerleştirme: Adım Adım Başlangıç Rehberi" başlıklı yazısına göz atabilir, oradan kaldığınız yerden bu anlatılanları daha da pekiştirebilirsiniz.

Şimdi sıra sizde: İlk compose.yml dosyanızı oluşturun, docker compose up deyin ve çoklu konteyner dünyasının kapılarını aralayın!