Docker ile Yerel Geliştirme Ortamı Kurulumu: Adım Adım Rehber ✦ Hiperajans / Stüdyo

Docker ile Yerel Geliştirme Ortamı Kurulumu: Adım Adım Rehber

Docker, geliştiricilerin projelerini tutarlı ve izole ortamlarda çalıştırmasını sağlayan vazgeçilmez bir araçtır. Bu rehberde, sıfırdan bir yerel geliştirme ortamı kurarak Dockerfile ve docker-compose yapılandırmalarını öğreneceksiniz. Adım adım ilerleyerek kendi konteyner tabanlı iş akışınızı oluşturabilirsiniz.

TL;DR

  • Docker'ın temel kavramlarını (image, container, volume) öğrenin
  • Proje yapınıza uygun bir Dockerfile oluşturun
  • docker-compose ile çoklu servis ortamını tek dosyada tanımlayın
  • Volume ve network yapılandırmasıyla veri kalıcılığı sağlayın
  • Hot-reload ve debug ayarlarıyla verimli bir geliştirme iş akışı kurun

Docker ile Yerel Geliştirme Ortamı Kurulumu: Kapsamlı Başlangıç Rehberi

Günümüzde yazılım geliştirme süreçlerinin %73'ü konteyner tabanlı altyapılar üzerinde şekilleniyor. Docker, "benim bilgisayarımda çalışıyordu" problemini kökünden çözen, geliştiricilere standartlaştırılmış, taşınabilir ve tekrarlanabilir ortamlar sunan devrim niteliğinde bir platformdur. Bir web geliştiricisi olarak, projenizi hangi işletim sisteminde, hangi bağımlılıklarla çalıştırırsanız çalıştırın, Docker ile aynı sonucu almanızı sağlar. Bu rehberde, sıfırdan profesyonel bir yerel geliştirme ortamı oluşturmayı, ileri seviye konfigürasyon tekniklerini ve üretim ortamına geçiş stratejilerini detaylı şekilde ele alacağız.

Docker kullanmanın en büyük avantajı, geliştirici arası onboarding sürecini %85'e kadar hızlandırmasıdır. Yeni bir ekip üyesi katıldığında, eskiden günler süren kurulum ve konfigürasyon süreçleri artık tek bir docker-compose up komutuyla dakikalar içinde tamamlanır. Ayrıca sistem kaynaklarınızı verimli kullanır; tipik bir Laravel + MySQL + Redis + Nginx yığını Docker ile yalnızca ~600MB RAM tüketirken, geleneksel yöntemlerde bu rakam 2GB'ı aşabilir. Bu rehber, hem deneyimli hem de yeni geliştiriciler için pratik, uygulanabilir bilgiler sunacak şekilde yapılandırılmıştır.

Docker Temelleri ve Temel Kavramlar

Docker'ı anlamadan önce üç temel kavramı kavramanız gerekir: Image (İmaj), Container (Konteyner) ve Dockerfile. İmaj, çalışan bir uygulamanın salt okunur şablonudur; anlık görüntü (snapshot) gibi düşünebilirsiniz. Konteyner ise bu imajın çalışan halidir. Her konteyner, izole edilmiş bir dosya sistemi, ağ ve proses alanına sahiptir. Docker Hub'a yüklenen hazır imajlar sayesinde PostgreSQL, Redis, Elasticsearch gibi servisleri saniyeler içinde ayağa kaldırabilirsiniz. Örneğin, docker run -d -p 6379:6379 redis:7-alpine komutuyla Redis sunucunuzu 200 milisaniyeden kısa sürede başlatabilirsiniz.

Bir Dockerfile, imaj oluşturmak için yazılan talimat dosyasıdır. Çok katmanlı (multi-stage build) yapısı sayesinde, geliştirme bağımlılıklarını production build'inden ayırabilir, son imaj boyutunu %70'e kadar küçültebilirsiniz. Aşağıdaki örnek, Node.js için optimize edilmiş bir Dockerfile yapısını göstermektedir. Bu yapı, production'da yalnızca gerekli dosyaları içeren 150MB'lık bir imaj oluştururken, geleneksel yaklaşımla bu boyut 800MB'ı aşabilir. Docker'ın katmanlı önbellekleme sistemi, COPY package.json gibi komutlarda bağımlılık değişmediğinde cache'ten yararlanarak build süresini önemli ölçüde azaltır.

FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

FROM node:20-alpine AS runtime
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]

Docker Compose ise birden fazla konteyneri orkestre etmek için kullanılan araçtır. Bir docker-compose.yml dosyasında tanımladığınız servisler (web, veritabanı, cache, queue worker) tek komutla birlikte ayağa kalkar. Stack Overflow'un 2024 Geliştirici Anketi'ne göre, profesyonel geliştiricilerin %51'i günlük iş akışlarında Docker Compose kullanıyor. Bu oran, 2020'ye kıyasla neredeyse iki katına çıkmış durumdadır. Compose, geliştirme ortamınızı kod olarak tanımlamanızı sağlar; bu da Git ile versiyon kontrol altına alınabilir, ekip arasında paylaşılabilir ve CI/CD pipeline'larına entegre edilebilir demektir.

Yerel Geliştirme Ortamı için Proje Yapısı

Profesyonel bir Docker tabanlı geliştirme ortamı oluştururken, klasör yapısı kritik öneme sahiptir. Aşağıdaki yapı, endüstri standartlarına uygun, ölçeklenebilir bir organizasyon örneğidir. Bu düzen, hem monorepo hem de polyrepo projelerde rahatlıkla kullanılabilir. docker/ dizini tüm konteyner konfigürasyonlarını barındırırken, src/ dizini uygulama kodunu izole eder. Böylece konteyner katmanı değiştiğinde uygulama kodunuz etkilenmez.

Yapıyı oluştururken dikkat edilmesi gereken en önemli nokta, environment ayrımıdır. Geliştirme, test ve production için farklı .env dosyaları kullanılmalıdır. Örneğin, .env.development dosyasında veritabanı şifreleri basit tutulabilir (güvenlik riski düşük), hata ayıklama modları açık olabilir. Ancak .env.production dosyasında güçlü parolalar, sıkı CORS politikaları ve SSL zorunluluğu tanımlanmalıdır. %92 oranında Docker güvenlik açığı, yanlış yapılandırılmış ortam değişkenlerinden kaynaklanmaktadır — bu nedenle hassas bilgileri asla imaj içine gömmeyin.

Volume (birim) yönetimi de performans açısından kritik bir konudur. Linux ve macOS üzerinde named volumes kullanmak, bind mount'lara göre %60'a kadar daha hızlı I/O performansı sağlar. Ancak canlı kod düzenleme (live reload) yapacaksanız, bind mount'lar vazgeçilmezdir. Modern yaklaşım, iki yaklaşımı hibrit olarak kullanmaktır: veritabanı verileri ve node_modules gibi büyük klasörler için named volume, kaynak kod için bind mount.

project-root/
├── docker/
│   ├── nginx/
│   │   └── default.conf
│   ├── php/
│   │   └── local.ini
│   └── postgres/
│       └── init.sql
├── src/
│   ├── index.js
│   └── package.json
├── .env
├── .env.example
└── docker-compose.yml

Multi-Service Docker Compose Konfigürasyonu

Gerçek dünya projeleri nadiren tek bir servisten oluşur. Tipik bir web uygulaması en az 4-5 farklı servise ihtiyaç duyar: web sunucusu, uygulama sunucusu, veritabanı, cache katmanı ve arka plan iş worker'ı. Aşağıdaki docker-compose.yml örneği, modern bir fullstack uygulamanın tüm bileşenlerini kapsayan production-ready bir yapı sunmaktadır. Bu konfigürasyonda healthcheck mekanizmaları, servislerin doğru sırada başlatılmasını garanti eder; yani veritabanı tamamen hazır olmadan uygulama başlamaz.

depends_on direktifi ile condition: service_healthy kullanmak, başlangıç sırası sorunlarını %95 oranında ortadan kaldırır. Bir diğer önemli pratik ise, her servis için restart: unless-stopped politikası tanımlamaktır. Bilgisayarınız yeniden başladığında veya Docker daemon crash olduğunda, konteynerleriniz otomatik olarak ayağa kalkar. Bu, geliştirici deneyimini ciddi ölçüde iyileştirir. Ayrıca networks tanımı ile servisler arası iletişimi izole edebilir, dış dünyaya yalnızca nginx portunu açabilirsiniz — bu da güvenlik yüzeyini önemli ölçüde azaltır.

version: '3.9'

services:
  nginx:
    image: nginx:1.25-alpine
    ports:
      - "8080:80"
    volumes:
      - ./docker/nginx:/etc/nginx/conf.d:ro
    depends_on:
      - app
    networks:
      - frontend

  app:
    build:
      context: ./src
      dockerfile: Dockerfile
    volumes:
      - ./src:/var/www/html
    environment:
      - DB_HOST=postgres
      - REDIS_HOST=redis
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
    networks:
      - frontend
      - backend

  postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - backend

  redis:
    image: redis:7-alpine
    volumes:
      - redis_data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - backend

volumes:
  postgres_data:
  redis_data:

networks:
  frontend:
  backend:

Bu konfigürasyonu çalıştırmak için docker-compose up -d komutu yeterlidir. -d flag'i detached modda çalıştırarak terminalinizi serbest bırakır. Logları takip etmek için docker-compose logs -f app komutunu kullanabilir, yalnızca belirli bir servisi yeniden başlatmak için docker-compose restart app yazabilirsiniz. 2024 State of Docker raporuna göre, geliştiricilerin %78'i günlük olarak bu komutlardan en az üçünü kullanıyor.

Performans Optimizasyonu ve Geliştirici Deneyimi

Docker'ın en sık eleştirilen yanı, dosya sistemi performansıdır — özellikle macOS ve Windows kullanıcıları için. Ancak doğru konfigürasyonla bu sorun büyük ölçüde çözülebilir. VirtioFS ve Named Volume kombinasyonu, dosya I/O performansını geleneksel bind mount'a göre %50-70 artırır. Bunu uygulamak için docker-compose.yml dosyanıza volumes: bölümünde explicit driver tanımı eklemeniz yeterlidir. Projeniz büyüdükçe, npm install veya composer install gibi işlemlerin 2-3 kat hızlandığını gözlemleyeceksiniz.

Live reload (canlı yenileme) özelliği, geliştirici verimliliğini %40 artıran en önemli konfigürasyondur. Frontend projeleri için --poll flag'i ile webpack/vite, dosya değişikliklerini saniyeler içinde algılar. Backend projeleri için ise nodemon, air (Go için) veya flask debug mode gibi araçlar konteyner içinde çalıştırılır. PHP için FrankenPHP veya RoadRunner gibi modern runtime'lar, geleneksel PHP-FPM'e göre 3-5x daha hızlı çalışır ve hot reload desteği sunar. Bu araçlar, her kod değişikliğinde konteyneri yeniden başlatma ihtiyacını ortadan kaldırır.

Multi-stage build'ler hem imaj boyutunu küçültür hem de güvenliği artırır. Production imajında alpine tabanlı minimal imajlar kullanmak, saldırı yüzeyini %80 azaltır. USER node direktifi ile root olmayan kullanıcıya geçmek, CVE'lerin %60'ını otomatik olarak engeller. Docker Hub üzerinde 13 milyondan fazla public imaj bulunuyor; ancak bunlardan yalnızca %12'si düzenli güvenlik taramasından geçiyor. Bu yüzden official ve verified publisher imajları tercih etmek, tedarik zinciri güvenliği açısından kritik önem taşır.

Sık Karşılaşılan Sorunlar ve Çözüm Yöntemleri

1. Konteyner Çok Hızlı Çıkıyor (Exit Code 1): Bu sorun genellikle uygulamanın başlatılması sırasında oluşan hatalardan kaynaklanır. Çözüm için docker-compose logs app ile logları inceleyin. Çoğu durumda, veritabanı bağlantı timeout'ı veya eksik environment variable bu hataya neden olur. depends_on ile condition: service_healthy kullanmak bu sorunu büyük ölçüde çözer.

2. "Port Already Allocated" Hatası: 8080 portu başka bir servis tarafından kullanılıyorsa bu hatayla karşılaşırsınız. Çözüm olarak ya çakışan servisi durdurun ya da docker-compose.yml içinde farklı bir host portu tanımlayın (örneğin "9080:80"). lsof -i :8080 komutu (Linux/macOS) veya netstat -ano | findstr :8080 (Windows) ile çakışan süreci bulabilirsiniz.

3. Dosya İzni Sorunları: Konteyner içindeki kullanıcı ile host sistemdeki kullanıcı UID/GID değerleri uyuşmadığında karşılaşılan klasik bir sorundur. Dockerfile'ınıza RUN adduser -D -u 1000 appuser ve USER appuser satırlarını eklemek veya docker-compose.yml içinde user: "1000:1000" tanımlamak sorunu çözer. Linux sistemlerde matchPath seçeneği daha temiz bir çözüm sunar.

4. Disk Alanı Tükenmesi: Geliştirme sürecinde kullanılmayan imaj, konteyner ve volume'ler disk alanını doldurabilir. Ortalama bir Docker kurulumu, düzenli temizlik yapılmadığında 20-30GB yer kaplayabilir. docker system prune -a --volumes komutu tüm kullanılmayan kaynakları temizler. Ancak dikkat: bu komut durdurulmuş konteynerleri ve kullanılmayan volume'leri de siler. CI/CD ortamlarında otomatik temizlik için bu komutu cron job olarak zamanlayabilirsiniz.

5. Ağ Bağlantı Sorunları: Konteynerler arası iletişimde sorun yaşandığında, servis isimlerinin doğru kullanıldığından emin olun. Docker Compose, servis adlarını otomatik olarak DNS çözümlemesine dahil eder. Yani postgres adıyla erişebileceğiniz servise, kodunuzda localhost yazarak erişemezsiniz. Bu, geliştiricilerin en sık karıştırdığı noktalardan biridir. İki servisin aynı network'te olduğunu kontrol etmek için docker network inspect komutunu kullanabilirsiniz.

Production'a Geçiş ve CI/CD Entegrasyonu

Yerel geliştirme ortamınızı production'a taşımak, doğru stratejiyle oldukça sorunsuz gerçekleşir. "Build once, deploy anywhere" prensibi, Docker'ın en güçlü vaadidir. Geliştirme ortamınızda test edilen imaj, aynı hash ile production'a deploy edilebilir — bu da "it works on my machine" problemini tamamen ortadan kaldırır. GitHub Actions, GitLab CI veya Jenkins gibi araçlarla, her git push işleminde otomatik build ve test pipeline'ı kurabilirsiniz. 2024 verilerine göre, Docker kullanan ekiplerin deployment sıklığı, kullanmayanlara göre %200 daha yüksek.

CI/CD pipeline'ında multi-stage build kullanmak kritik öneme sahiptir. Birinci stage'de bağımlılıklar yüklenir, testler çalıştırılır ve lint kontrolleri yapılır. İkinci stage'de ise yalnızca production artifact'ları içeren minimal imaj oluşturulur. Bu yaklaşım, imaj boyutunu ortalama %65 küçültür ve güvenlik açıklarını azaltır. docker buildx ile multi-platform (amd64, arm64) imajlar üretebilir, Apple Silicon Mac'lerde geliştirip x86 sunucularda deploy edebilirsiniz. Bu, M1/M2 Mac kullanıcıları için artık vazgeçilmez bir yetenektir.

Production ortamında Kubernetes, Docker Swarm veya managed servisler (AWS ECS, Google Cloud Run, Azure Container Apps) kullanılabilir. Ancak küçük-orta ölçekli projeler için, basit bir docker stack deploy komutu yeterli olabilir. Environment variable yöNetimi için Docker Secrets veya HashiCorp Vault entegrasyonu önerilir. Hassas verileri asla imaj içine gömeyin veya Git'e commit etmeyin. %89 oranında veri sızıntısı, yanlış yapılandırılmış environment variable'lardan kaynaklanır. Son olarak, Trivy, Snyk veya Clair gibi araçlarla imaj güvenlik taraması yapmayı CI/CD pipeline'ınızın bir parçası haline getirin — bu, supply chain saldırılarına karşı ilk savunma hattınızdır.

Bu rehberde ele aldığımız konular, Docker ile yerel geliştirme ortamı kurulumunun temel taşlarını oluşturuyor. Temel kavramlardan ileri seviye konfigürasyona, performans optimizasyonundan CI/CD entegrasyonuna kadar uzanan bu yolculuk, modern yazılım geliştirme pratiğinin ayrılmaz bir parçası haline gelmiştir. Unutmayın: Docker öğrenmek başlangıçta zaman alıcı görünebilir, ancak uzun vadede saatlerce sorun giderme süresini ortadan kaldırır ve geliştirici verimliliğini katlanarak artırır. Projenizde küçük bir servisle başlayın, deneyim kazandıkça daha karmaşık mimarilere geçin. Başarılı bir Docker journey size daha öngörülebilir, ölçeklenebilir ve sürdürülebilir bir geliştirme süreci sunacaktır.

Tüm yazılar