Docker Compose ile Yerel Ortamda Birden Fazla Servisi Çalıştırmak
Modern yazılım geliştirme süreçlerinde, uygulamalar genellikle tek bir monolitik yapıdan ziyade, birbirleriyle iletişim kuran birden fazla bağımsız servisten oluşur. Bu mikroservis mimarileri, esneklik ve ölçeklenebilirlik gibi önemli avantajlar sunsa da, yerel geliştirme ortamında bu servislerin tümünü aynı anda yönetmek ve çalıştırmak karmaşık bir hal alabilir. İşte tam bu noktada Docker Compose devreye girerek, çok servisli uygulamaların tek bir komutla kolayca tanımlanmasını, izole edilmesini ve çalıştırılmasını sağlayan güçlü bir araç haline gelir. Bu makalede, Docker Compose’un ne olduğunu, neden bu kadar önemli olduğunu ve yerel geliştirme ortamınızda birden fazla servisi nasıl verimli bir şekilde yöneteceğinizi detaylı bir şekilde inceleyeceğiz.
Docker Compose Nedir ve Neden İhtiyaç Duyarız?
Docker Compose, Docker ile birlikte gelen, çok kapsayıcılı Docker uygulamalarını tanımlamak ve çalıştırmak için kullanılan bir araçtır. Tek bir YAML dosyası kullanarak, uygulamanızın tüm servislerini, ağlarını ve birimlerini (volumes) yapılandırabilirsiniz. Bu sayede, karmaşık bağımlılıkları olan uygulamaları tek bir komutla ayağa kaldırabilir, durdurabilir ve yönetebilirsiniz.
Tek Servisli Uygulamalardan Çok Servisli Mimarilere Geçiş
Geleneksel olarak, birçok uygulama tek bir bütün (monolit) olarak geliştirilirdi. Ancak günümüzün büyük ve karmaşık sistemleri genellikle veritabanı, ön uç, arka uç API’leri, mesaj kuyrukları gibi farklı bileşenlerden oluşur. Her bir bileşen kendi Docker kapsayıcısında çalıştırıldığında, bu servislerin birbirleriyle uyumlu bir şekilde çalışmasını sağlamak manuel olarak oldukça zahmetli olabilir.
Geliştirme ve Test Süreçlerindeki Zorluklar
Birden fazla servisi manuel olarak başlatmak, doğru sırayla çalıştırmak, bağımlılıkları yönetmek ve her birinin loglarını takip etmek geliştirme sürecini yavaşlatabilir ve hatalara yol açabilir. Özellikle yeni bir geliştiricinin projeye dahil olması durumunda, tüm bu servisleri kendi makinesinde doğru şekilde kurup çalıştırması ciddi bir öğrenme eğrisi gerektirebilir.
Docker Compose’un Sağladığı Avantajlar
Docker Compose, bu zorlukları ortadan kaldırarak geliştiricilere önemli avantajlar sunar:
- Kolay Kurulum: Tüm servisleri tek bir
docker-compose.ymldosyası ile tanımlayarak, projenin kurulumunu basitleştirir. - İzole Ortamlar: Her servis kendi kapsayıcısında çalıştığı için, bağımlılık çakışmaları ortadan kalkar.
- Tekrarlanabilirlik: Geliştirme, test ve hatta hazırlık (staging) ortamları arasında tutarlı bir yapı sağlar.
- Verimli Yönetim: Tek komutla tüm servisleri başlatma, durdurma, yeniden oluşturma gibi işlemleri kolaylaştırır.
- Hızlı Geliştirme: Değişikliklerin hızlı bir şekilde test edilmesini ve entegrasyonunu sağlar.
Docker Compose Kurulumu ve Temel Kavramlar
Docker Compose’u kullanmaya başlamadan önce, sisteminizde Docker’ın kurulu olması gerekmektedir. Docker Desktop (Windows ve macOS için) genellikle Docker Compose’u da beraberinde kurar. Linux kullanıcıları için ise ayrı bir kurulum adımı gerekebilir.
İşletim Sistemine Göre Kurulum Adımları
Windows ve macOS: Docker Desktop’ı indirip kurduğunuzda Docker Compose otomatik olarak yüklenecektir. Kurulumu doğrulamak için terminalde docker compose version komutunu çalıştırabilirsiniz.
docker compose version
Linux: Çoğu Linux dağıtımında Docker Compose ayrı bir paket olarak kurulmalıdır. Genellikle aşağıdaki gibi bir komutla yüklenebilir (bu komut dağıtıma göre değişebilir):
sudo apt-get update
sudo apt-get install docker-compose-plugin # Yeni versiyonlar için
# veya eski versiyonlar için
# sudo curl -L "https://github.com/docker/compose/releases/download/v2.17.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
# sudo chmod +x /usr/local/bin/docker-compose
Servisler, Ağlar ve Birimler (Volumes)
Docker Compose'un temelini oluşturan üç ana kavram vardır:
- Servisler (Services): Uygulamanızın her bir bileşenini temsil eder. Örneğin, bir web sunucusu, bir veritabanı veya bir API servisi birer servis olabilir. Her servis, bir Docker imajından oluşturulur ve kendi yapılandırmasına (portlar, ortam değişkenleri vb.) sahiptir.
- Ağlar (Networks): Servislerin birbirleriyle ve dış dünya ile iletişim kurmasını sağlar. Docker Compose, varsayılan olarak tüm servisler için özel bir ağ oluşturur, ancak özel ağlar da tanımlayabilirsiniz.
- Birimler (Volumes): Kapsayıcılar arasında veya kapsayıcı ile ana makine arasında veri kalıcılığını sağlar. Örneğin, bir veritabanının verilerini bir birimde saklayarak, kapsayıcı silinse bile verilerin kaybolmamasını sağlayabilirsiniz.
docker-compose.yml Dosyasının Rolü
docker-compose.yml dosyası, Docker Compose'un tüm sihrini barındıran merkezi yapılandırma dosyasıdır. Bu YAML formatındaki dosya, uygulamanızın servislerini, ağlarını, birimlerini ve diğer tüm ayarlarını deklaratif bir şekilde tanımlar. Docker Compose bu dosyayı okuyarak belirtilen tüm kaynakları oluşturur ve yönetir.
Basit Bir Çok Servisli Uygulama Örneği
Şimdi, basit bir web uygulaması (örneğin bir Python Flask uygulaması) ve bir PostgreSQL veritabanından oluşan çok servisli bir uygulamayı Docker Compose ile nasıl çalıştıracağımıza dair bir örnek oluşturalım.
Örnek Uygulama Mimarisi: Web Uygulaması ve Veritabanı
Uygulamamız iki ana servisten oluşacak:
- Web Servisi: Flask tabanlı, basit bir HTTP API sunacak ve veritabanına bağlanacak.
- Veritabanı Servisi: PostgreSQL veritabanı.
docker-compose.yml Dosyasının Oluşturulması
Öncelikle, projemiz için bir dizin oluşturalım ve içine app.py (Flask uygulaması), requirements.txt (Python bağımlılıkları) ve Dockerfile (web servisi için) dosyalarını ekleyelim. Ardından, en önemli dosyamız olan docker-compose.yml'yi oluşturacağız.
app.py:
from flask import Flask
import psycopg2
import os
app = Flask(__name__)
@app.route('/')
def hello():
try:
conn = psycopg2.connect(
host=os.environ.get("DB_HOST", "db"),
database=os.environ.get("DB_NAME", "mydatabase"),
user=os.environ.get("DB_USER", "user"),
password=os.environ.get("DB_PASSWORD", "password")
)
cur = conn.cursor()
cur.execute("SELECT 1")
conn.close()
return "Hello from Flask! Connected to PostgreSQL successfully!"
except Exception as e:
return f"Hello from Flask! Failed to connect to PostgreSQL: {e}"
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
requirements.txt:
Flask
psycopg2-binary
Dockerfile (web servisi için):
FROM python:3.9-slim-buster
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["python", "app.py"]
Şimdi docker-compose.yml dosyamızı oluşturalım:
version: '3.8'
services:
web:
build: .
ports:
- "5000:5000"
environment:
DB_HOST: db
DB_NAME: mydatabase
DB_USER: user
DB_PASSWORD: password
depends_on:
- db
db:
image: postgres:13
environment:
POSTGRES_DB: mydatabase
POSTGRES_USER: user
POSTGRES_PASSWORD: password
volumes:
- db_data:/var/lib/postgresql/data
volumes:
db_data:
Uygulamayı Çalıştırma ve Test Etme
Tüm bu dosyaları aynı dizine kaydettikten sonra, terminali bu dizinde açın ve aşağıdaki komutu çalıştırın:
docker compose up -d
Bu komut, docker-compose.yml dosyasını okuyacak, gerekli Docker imajlarını indirecek (veya oluşturacak), servisleri başlatacak ve onları arka planda çalıştıracaktır (-d bayrağı "detached" mod anlamına gelir).
Servisler başladıktan sonra, web tarayıcınızda http://localhost:5000 adresine giderek uygulamanın çalıştığını ve veritabanına başarıyla bağlandığını görebilirsiniz.
Servislerin durumunu kontrol etmek için:
docker compose ps
Servislerin loglarını görmek için:
docker compose logs
Uygulamayı durdurmak ve kapsayıcıları kaldırmak için:
docker compose down
docker-compose.yml Dosyasının Detaylı İncelenmesi
docker-compose.yml dosyası, Docker Compose'un kalbidir. Her bir anahtarın ne anlama geldiğini anlamak, karmaşık uygulamaları yönetmek için kritik öneme sahiptir.
version Anahtarı ve Anlamı
version anahtarı, Compose dosya formatının sürümünü belirtir. Genellikle en son kararlı sürüm olan '3.8' veya daha yenisi kullanılır. Farklı sürümler, farklı özellik setlerini ve sözdizimlerini destekler.
version: '3.8' # Dosya formatı sürümü
services Bloğu ve Servis Tanımlamaları
services bloğu, uygulamanızdaki her bir servisi tanımladığınız yerdir. Her servis adı (örneğin web, db) benzersiz olmalıdır ve bu isimler, servislerin birbirleriyle iletişim kurarken kullanacağı DNS isimleri olarak işlev görür.
Her servis tanımı içinde kullanabileceğiniz bazı yaygın anahtarlar:
build: Servisin imajını oluşturmak için birDockerfile'ın yolunu belirtir.image: Mevcut bir Docker imajını (Docker Hub'dan veya yerelden) kullanır.ports: Kapsayıcı portlarını ana makine portlarına eşler ("ANA_MAKİNE_PORTU:KAPSAYICI_PORTU").environment: Kapsayıcı içinde kullanılacak ortam değişkenlerini ayarlar.volumes: Kapsayıcıya birimler bağlar (veri kalıcılığı için).depends_on: Servisler arasındaki bağımlılıkları belirtir. Örneğin,webservisi başlamadan öncedbservisinin başlamasını sağlar.networks: Servisin hangi ağlara bağlanacağını belirtir.restart: Kapsayıcının ne zaman yeniden başlatılacağını kontrol eder (örneğinalways,on-failure).
services:
web:
build: . # Mevcut dizindeki Dockerfile'ı kullan
ports:
- "5000:5000" # Ana makine 5000 -> Kapsayıcı 5000
environment:
DB_HOST: db # db servisine bağlan
depends_on:
- db # db servisi başlamadan web başlamasın
db:
image: postgres:13 # postgres:13 imajını kullan
volumes:
- db_data:/var/lib/postgresql/data # db_data birimini bağla
volumes ve networks Bloğunun Kullanımı
volumes bloğu, uygulamanızda kullanacağınız birimleri tanımladığınız yerdir. Bu birimler, kapsayıcılar silinse bile verilerin kalıcı olmasını sağlar. İsimli birimler (named volumes) en yaygın kullanılan yöntemdir.
volumes:
db_data: # db_data adında bir birim tanımla
networks bloğu ise özel ağlar tanımlamanıza olanak tanır. Varsayılan olarak Docker Compose, tüm servisler için otomatik bir ağ oluşturur, ancak daha karmaşık senaryolar için özel ağlar tanımlayabilirsiniz.
networks:
app_network:
driver: bridge # Varsayılan köprü ağı tipi
Ardından servislerinizde bu ağı kullanabilirsiniz:
services:
web:
networks:
- app_network
db:
networks:
- app_network
Servisler Arası İletişim ve Ağ Yapılandırması
Docker Compose, servislerin birbirleriyle kolayca iletişim kurabilmesi için güçlü bir ağ altyapısı sağlar.
Varsayılan Ağ Yapılandırması
Bir docker-compose.yml dosyası çalıştırdığınızda, Docker Compose otomatik olarak projeniz için varsayılan bir köprü ağı (bridge network) oluşturur. Bu ağdaki tüm servisler, birbirlerine servis adlarını kullanarak erişebilirler. Örneğin, web servisi, db servisine db hostname'i üzerinden bağlanabilir.
# app.py içinde
host=os.environ.get("DB_HOST", "db") # 'db' hostname'i kullanılıyor
Özel Ağlar Oluşturma ve Kullanma
Daha fazla kontrol veya izolasyon gerektiğinde özel ağlar tanımlayabilirsiniz. Bu, farklı servis gruplarını birbirinden ayırmak veya belirli ağ yapılandırmalarını uygulamak için kullanışlıdır.
version: '3.8'
services:
frontend:
# ...
networks:
- web_network
backend:
# ...
networks:
- web_network
- db_network
database:
# ...
networks:
- db_network
networks:
web_network:
driver: bridge
db_network:
driver: bridge
Bu örnekte, frontend ve backend servisleri web_network üzerinden, backend ve database servisleri ise db_network üzerinden iletişim kurabilir. frontend doğrudan database ile iletişim kuramaz, bu da güvenlik ve mimari ayrımı sağlar.
Servis İsimleriyle İletişim Kurma
Docker Compose'un en büyük kolaylıklarından biri, servislerin birbirlerine IP adresleri yerine tanımladıkları servis adlarıyla erişebilmesidir. Docker'ın dahili DNS çözümü sayesinde, db servisine bağlanmak için sadece db host adını kullanmak yeterlidir. Bu, IP adreslerinin değişebileceği dinamik ortamlarda büyük bir avantajdır.
Geliştirme Ortamında Docker Compose Kullanımı İçin İpuçları
Docker Compose, geliştirme süreçlerini hızlandırmak ve kolaylaştırmak için birçok özellik sunar.
Hot Reloading ve Kod Değişikliklerini Yansıtma
Geliştirme sırasında kod değişikliklerinin anında kapsayıcıya yansıması önemlidir. Bunu sağlamak için bind mount adı verilen birim türünü kullanırız. Bu, ana makinedeki bir dizini kapsayıcı içindeki bir dizine bağlar.
services:
web:
build: .
ports:
- "5000:5000"
volumes:
- ./app:/app # Ana makinedeki ./app dizinini kapsayıcıdaki /app dizinine bağla
environment:
FLASK_ENV: development # Flask'ın hot reloading'ini etkinleştir
# ... diğer ortam değişkenleri
Bu yapılandırma ile ana makinedeki app dizininde yaptığınız değişiklikler anında kapsayıcıdaki /app dizinine yansıyacak ve Flask'ın geliştirme sunucusu otomatik olarak yeniden yüklenecektir.
Ortam Değişkenleri ile Yapılandırma
Hassas bilgiler (API anahtarları, veritabanı şifreleri) veya ortama özgü ayarlar (geliştirme, test, üretim) için ortam değişkenleri kullanmak en iyi uygulamadır. Docker Compose, .env dosyalarını otomatik olarak yükleyerek bu süreci kolaylaştırır.
Proje kök dizininizde bir .env dosyası oluşturun:
DB_HOST=db
DB_NAME=my_dev_db
DB_USER=dev_user
DB_PASSWORD=dev_password
Ardından docker-compose.yml dosyasında bu değişkenleri kullanabilirsiniz:
services:
web:
# ...
environment:
DB_HOST: ${DB_HOST} # .env dosyasından DB_HOST değerini al
DB_NAME: ${DB_NAME}
DB_USER: ${DB_USER}
DB_PASSWORD: ${DB_PASSWORD}
Bu sayede hassas bilgileri docker-compose.yml dosyasına doğrudan yazmaktan kaçınır, farklı ortamlar için farklı .env dosyaları kullanabilirsiniz.
Farklı Ortamlar İçin Birden Fazla docker-compose.yml Dosyası
Geliştirme, test ve üretim ortamları genellikle farklı yapılandırmalar gerektirir. Docker Compose, birden fazla Compose dosyasını birleştirme yeteneği sunar. Örneğin, bir temel docker-compose.yml dosyanız olabilir ve geliştirme için docker-compose.override.yml gibi ek bir dosya kullanabilirsiniz.
docker-compose.yml (temel yapılandırma):
version: '3.8'
services:
web:
build: .
ports:
- "80:5000"
db:
image: postgres:13
# ...
docker-compose.override.yml (geliştirme için ek ayarlar):
version: '3.8'
services:
web:
volumes:
- ./app:/app
environment:
FLASK_ENV: development
ports:
- "5000:5000" # Geliştirme için farklı port
Varsayılan olarak docker compose up komutu hem docker-compose.yml hem de docker-compose.override.yml dosyalarını birleştirir. Farklı bir dosya kullanmak için -f bayrağını kullanabilirsiniz: docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d.
Docker Compose Komutları ve Yaşam Döngüsü Yönetimi
Docker Compose, servislerin yaşam döngüsünü yönetmek için bir dizi kullanışlı komut sunar.
docker compose up ve down Komutları
Bu iki komut, Docker Compose ile çalışırken en sık kullanacaklarınızdır:
docker compose up:docker-compose.ymldosyasında tanımlanan tüm servisleri oluşturur ve başlatır. Gerekirse imajları indirir veya oluşturur. Varsayılan olarak logları terminale basar.docker compose up -d: Servisleri arka planda (detached mode) başlatır.docker compose down: Servisleri durdurur ve kapsayıcıları, ağları ve varsayılan birimleri kaldırır.docker compose down --volumes: Servisleri durdurur ve tanımlanan birimler dahil her şeyi kaldırır. Bu, temiz bir başlangıç yapmak için kullanışlıdır.
docker compose ps, logs, exec
Diğer önemli komutlar şunlardır:
docker compose ps: Çalışan servislerin durumunu listeler.docker compose logs [SERVİS_ADI]: Belirli bir servisin veya tüm servislerin loglarını gösterir.-fbayrağı ile logları canlı olarak takip edebilirsiniz.docker compose exec [SERVİS_ADI] [KOMUT]: Çalışan bir servisin kapsayıcısı içinde bir komut çalıştırır. Örneğin,docker compose exec web bashile web kapsayıcısının içine girip bash kabuğunu kullanabilirsiniz.docker compose build [SERVİS_ADI]: Sadece belirli bir servisin imajını yeniden oluşturur.docker compose restart [SERVİS_ADI]: Belirli bir servisi veya tüm servisleri yeniden başlatır.
Servisleri Yeniden Başlatma ve Güncelleme
Kodunuzda veya docker-compose.yml dosyanızda bir değişiklik yaptığınızda, genellikle servisleri yeniden başlatmanız gerekir. Eğer sadece kodunuzu değiştirdiyseniz ve birim bağlaması kullanıyorsanız, bazen sadece uygulamanın kendi hot-reloading özelliği yeterli olabilir. Ancak Dockerfile'da veya docker-compose.yml dosyasında değişiklik yaptıysanız:
docker compose up --build -d: Değişiklik yapılan imajları yeniden oluşturur ve servisleri günceller.docker compose restart [SERVİS_ADI]: Sadece belirtilen servisi yeniden başlatır.
Sonuç
Docker Compose, çok servisli uygulamaların yerel geliştirme ortamında yönetimini ve çalıştırılmasını inanılmaz derecede basitleştiren güçlü ve vazgeçilmez bir araçtır. Tek bir YAML dosyasıyla tüm uygulama yığınınızı tanımlama, tek komutla başlatma ve durdurma yeteneği, geliştirme süreçlerini hızlandırır, tutarlılığı artırır ve yeni ekip üyelerinin projeye adaptasyonunu kolaylaştırır. İster küçük bir mikroservis projesi üzerinde çalışıyor olun, ister karmaşık bir sistem geliştiriyor olun, Docker Compose size zaman kazandıracak ve geliştirme deneyiminizi önemli ölçüde iyileştirecektir. Bu makalede öğrendiğiniz temel kavramlar ve pratik örneklerle, kendi çok servisli Docker Compose projelerinizi başarıyla yönetmeye hazırsınız.
SSS (Sık Sorulan Sorular)
Docker ve Docker Compose arasındaki fark nedir?
Docker, kapsayıcıları oluşturmak, çalıştırmak ve yönetmek için kullanılan temel platformdur. Tek bir kapsayıcıyı yönetmek için tasarlanmıştır. Docker Compose ise, birden fazla Docker kapsayıcısını içeren çok servisli uygulamaları tanımlamak ve tek bir birim olarak yönetmek için Docker'ın üzerine inşa edilmiş bir araçtır.
docker-compose.yml dosyasını nerede saklamalıyım?
Genellikle projenizin kök dizininde saklamanız önerilir. Bu, tüm servislerin ve ilgili dosyaların tek bir yerde kolayca bulunmasını sağlar.
Birden fazla docker-compose.yml dosyası kullanabilir miyim?
Evet, Docker Compose birden fazla Compose dosyasını birleştirme yeteneği sunar. Bu, farklı ortamlar (geliştirme, test, üretim) için farklı yapılandırmalar tanımlamanıza olanak tanır. docker compose -f docker-compose.yml -f docker-compose.override.yml up komutu ile birden fazla dosyayı kullanabilirsiniz.
Docker Compose ile üretim ortamında uygulama çalıştırabilir miyim?
Docker Compose, genellikle yerel geliştirme ve test ortamları için tasarlanmıştır. Üretim ortamları için Kubernetes gibi daha gelişmiş kapsayıcı orkestrasyon araçları tercih edilir. Ancak, küçük ölçekli uygulamalar için veya başlangıç aşamasında Docker Compose, basit üretim dağıtımları için de kullanılabilir, ancak ölçeklenebilirlik, yüksek erişilebilirlik ve otomatik iyileşme gibi konularda sınırlı kalabilir.
Performans sorunları yaşarsam ne yapmalıyım?
Docker Compose ile performans sorunları yaşıyorsanız, şunları kontrol edebilirsiniz:
- Kaynak Tahsisi: Docker Desktop ayarlarından Docker'a ayrılan CPU ve RAM miktarını artırın.
- Birim Bağlamaları (Volumes): Özellikle macOS ve Windows'ta bind mount'lar performans sorunlarına yol açabilir. Verimli dosya senkronizasyonu için Docker Desktop'ın "VirtioFS" veya "gRPC FUSE" gibi yeni dosya paylaşım motorlarını kullanmayı deneyin.
- İmaj Boyutları: Daha küçük temel imajlar kullanın (örneğin
alpinetabanlı imajlar). - Gereksiz Servisler: Sadece ihtiyacınız olan servisleri çalıştırın.
