· Admin

Docker ile Geliştirme ve Deploy: Konteyner Dünyasına Giriş

Yillardir "bende calisiyor" problemini yasadim. Local'de her sey mukemmel, sunucuya atiyorsun PHP versiyonu farkli, extension eksik, config baska. Docker ile tanistiktan sonra bu sorunlar tamamen ortadan kalkti. Bu yazida Docker'i nasil kullandigimi, Laravel projelerinde Dockerfile ve docker-compose yapilandirmalarimi, production'a nasil tasadigimi paylasiyorum.

Docker Nedir ve Neden Kullanmaliyiz?

Docker, uygulamalari konteyner adini verdigimiz izole ortamlarda calistiran bir platform. Sanal makineden farki buyuk: VM tam bir isletim sistemi ayaga kaldirirken, konteyner host kernel'i paylasiyor. Bu yuzden cok daha hafif ve hizli.

Benim icin Docker'in en buyuk avantajlari sunlar:

  • Gelistirme ortami ile production ayni
  • Yeni bir developer ekibe katildiginda tek komutla ortam hazir
  • Birden fazla proje farkli PHP/Node versiyonlari ile sorunsuz calisiyor
  • Sunucu degisikligi yaptigimda migrasyon dakikalar icinde tamamlaniyor

Dockerfile Yazimi: PHP/Laravel Icin

Laravel projelerimde kullandigim temel Dockerfile su sekilde:

FROM php:8.3-fpm-alpine

# Sistem bagimliliklari
RUN apk add --no-cache \
    git curl zip unzip libpng-dev libjpeg-turbo-dev \
    freetype-dev libzip-dev icu-dev oniguruma-dev

# PHP extension'lari
RUN docker-php-ext-configure gd --with-freetype --with-jpeg \
    && docker-php-ext-install \
    pdo_mysql mbstring exif pcntl bcmath gd zip intl opcache

# Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer

# Calisma dizini
WORKDIR /var/www/html

# Uygulama dosyalari
COPY . .

# Bagimliliklari yukle
RUN composer install --no-dev --optimize-autoloader --no-interaction

# Izinler
RUN chown -R www-data:www-data /var/www/html/storage /var/www/html/bootstrap/cache

EXPOSE 9000
CMD ["php-fpm"]

Alpine imaji kullaniyorum cunku standart Debian tabanli imajlara gore yaklasik 3-4 kat daha kucuk. 800MB yerine 200MB civarinda bir imaj elde ediyorsunuz.

docker-compose.yml: Tam Stack Yapilandirma

Gelistirme ortamimda su docker-compose yapisini kullaniyorum:

version: '3.8'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: laravel-app
    restart: unless-stopped
    volumes:
      - .:/var/www/html
      - /var/www/html/vendor
    networks:
      - app-network
    depends_on:
      - mysql
      - redis

  nginx:
    image: nginx:alpine
    container_name: laravel-nginx
    restart: unless-stopped
    ports:
      - "8080:80"
    volumes:
      - .:/var/www/html
      - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
    networks:
      - app-network
    depends_on:
      - app

  mysql:
    image: mysql:8.0
    container_name: laravel-mysql
    restart: unless-stopped
    environment:
      MYSQL_DATABASE: ${DB_DATABASE}
      MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
      MYSQL_USER: ${DB_USERNAME}
      MYSQL_PASSWORD: ${DB_PASSWORD}
    ports:
      - "3306:3306"
    volumes:
      - mysql-data:/var/lib/mysql
    networks:
      - app-network

  redis:
    image: redis:7-alpine
    container_name: laravel-redis
    restart: unless-stopped
    ports:
      - "6379:6379"
    volumes:
      - redis-data:/data
    networks:
      - app-network

volumes:
  mysql-data:
  redis-data:

networks:
  app-network:
    driver: bridge

Dikkat edilmesi gereken birka noktaya degineyim. volumes kisminda .:/var/www/html ile local dosyalarinizi konteynere mount ediyorsunuz. Bu sayede kod degisiklikleriniz aninda yansir. Ancak /var/www/html/vendor satirina dikkat edin -- bu, konteyner icindeki vendor klasorunun local ile ezilmesini engelliyor.

Volume, Network ve Port Mapping

Docker'da uc temel kavram var ve bunlari iyi anlamak gerekiyor:

Volumes kalici veri depolamak icin kullanilir. MySQL veritabaninizi named volume ile tutmazsaniz, konteyner silindiginde verileriniz de gider:

volumes:
  mysql-data:
    driver: local

Networks konteynerler arasi iletisimi saglar. Ayni network'teki konteynerler birbirini isim ile bulabilir. Yukaridaki ornekte app konteyneri mysql hostuna direkt erisebilir.

Port mapping ise host portunu konteyner portuna yonlendirir. 8080:80 dedigimizde, local'de 8080 portuna gelen istekler konteynerin 80 portuna iletilir.

Multi-Stage Build ile Production Image

Development icin source code'u mount ediyoruz ama production'da her sey imajin icinde olmali. Multi-stage build ile hem build araclarini hem de nihai imaji ayri tutariz:

# Stage 1: Build
FROM composer:latest AS builder
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install --no-dev --optimize-autoloader --no-scripts
COPY . .
RUN composer dump-autoload --optimize

# Stage 2: Frontend
FROM node:20-alpine AS frontend
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build

# Stage 3: Production
FROM php:8.3-fpm-alpine
RUN apk add --no-cache libpng libjpeg-turbo freetype libzip icu \
    && docker-php-ext-configure gd --with-freetype --with-jpeg \
    && docker-php-ext-install pdo_mysql gd zip intl opcache

WORKDIR /var/www/html

COPY --from=builder /app/vendor ./vendor
COPY --from=frontend /app/public/build ./public/build
COPY . .

RUN chown -R www-data:www-data storage bootstrap/cache

EXPOSE 9000
CMD ["php-fpm"]

Bu yaklasimla son imajda ne Node.js ne de Composer bulunur. Imaj boyutu ciddi olcude kucuk kalir ve saldiri yuzeyi azalir.

Docker Hub vs GitHub Container Registry

Imajlarinizi nerede tutacaginiz projenize bagli. Docker Hub en populer secenektir ve public imajlar icin ucretsiz. Ancak private projeleriniz GitHub'daysa, GitHub Container Registry (GHCR) daha mantikli:

# Docker Hub'a push
docker build -t projeadi/app:latest .
docker push projeadi/app:latest

# GHCR'a push
docker build -t ghcr.io/kullanici/app:latest .
echo $GITHUB_TOKEN | docker login ghcr.io -u kullanici --password-stdin
docker push ghcr.io/kullanici/app:latest

GHCR'in avantaji GitHub Actions ile entegrasyonun cok kolay olmasi. CI/CD pipeline'iniz zaten GitHub'daysa, ayni token ile imaj push edebilirsiniz.

Development vs Production Farklari

Bu ikisi arasindaki farklari net bilmek gerekiyor:

Development ortaminda:

  • Source code volume olarak mount edilir (hot reload)
  • Xdebug aktif olabilir
  • APP_DEBUG=true
  • MySQL portu disariya acik (GUI ile erisim icin)
  • Composer dev bagimliliklari yuklu

Production ortaminda:

  • Source code imajin icinde, volume yok
  • Xdebug kesinlikle yok
  • APP_DEBUG=false, APP_ENV=production
  • Sadece gerekli portlar acik (genelde sadece 80/443)
  • OPcache agresif ayarlanmis
  • Health check tanimli

Bunu yonetmenin en temiz yolu ayri compose dosyalari kullanmak:

# Development
docker compose -f docker-compose.yml -f docker-compose.dev.yml up

# Production
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

dockerapi Projemde Ogrendiklerim

dockerapi projesinde Docker API'yi programatik olarak yonetmeyi ogrendim. Bu proje, uzak sunuculardaki Docker konteynerlerini bir REST API uzerinden yonetmeyi amacliyor. Gelistirme surecinde birkac onemli ders aldim.

Birincisi, port yonetimi sanildiginden karisik. UDP portlari icin ayri bir _parse_ports fonksiyonu yazmam gerekti cunku Docker'in port syntax'i TCP ve UDP icin farkli davranabiliyor.

Ikincisi, deploy surecinde gereksiz dosyalari tasimamak buyuk fark yaratiyor. 829MB'lik bir dizinde sadece service/ ve config klasorlerini kopyalayip node_modules, frontend ve gecici dosyalari atladigimda deploy suresi dramatik sekilde dustu.

Ucuncusu, Docker socket'ine erisim guvenlik acisindan hassas bir konu. /var/run/docker.sock mount etmek tum host'a root erisimi vermek anlamina gelebilir. Production'da bunu Docker API'nin TLS ile korunmus TCP endpoint'i uzerinden yapmak gerekiyor.

Sonuc

Docker artik modern yazilim gelistirmenin olmazsa olmazi. Baslangicta ogrenme egrisi biraz dik gelebilir ama bir kez alistiginizda geri donemezsiniz. Ozellikle birden fazla proje ile calisan, farkli PHP veya Node versiyonlarina ihtiyac duyan herkes icin hayat kurtarici bir teknoloji.

Tavsiyem: kucuk bir proje ile baslayin. Basit bir docker-compose.yml yazin, konteyner icinde calistirin, sonra adim adim karmasiklastirin. Docker dokumantasyonu da son derece iyi hazirlanmis, her konuda detayli ornekler bulabilirsiniz.