Docker, geliştirme ortamını ekip genelinde standartlaştırmanın en etkili yoludur. "Bende çalışıyordu" sorununun kalıcı çözümüdür. Bu makalede gerçek bir fullstack proje için tam geliştirme ortamı kuruyoruz.
Neden Docker ile Geliştirme?
Klasik sorunlar:
▸ Farklı Node.js sürümleri (14, 18, 20, 22...)
▸ PostgreSQL'in local kurulumu vs. container davranış farkı
▸ "Ben Windows'tayım, o Mac'te" uyumsuzlukları
▸ CI'da çalışıyor, local'de çalışmıyor
Docker bu sorunları kökten çözer.
Proje Yapısı
blog-projesi/
├── backend/
│ ├── src/
│ ├── Dockerfile
│ └── package.json
├── frontend/
│ ├── src/
│ ├── Dockerfile
│ └── package.json
├── nginx/
│ └── default.conf
├── docker-compose.yml
├── docker-compose.dev.yml
└── .env.example KOPYALA
Production Dockerfile (Node.js)
# backend/Dockerfile
FROM node:22-alpine AS base
WORKDIR /app
COPY package*.json ./
FROM base AS deps
RUN npm ci --only=production
FROM base AS build
RUN npm ci
COPY . .
RUN npm run build
FROM node:22-alpine AS runner
WORKDIR /app
RUN addgroup -S app && adduser -S app -G app
COPY --from=deps /app/node_modules ./node_modules
COPY --from=build /app/dist ./dist
COPY --from=build /app/package.json ./
USER app
EXPOSE 3000
CMD [ "node" , "dist/server.js" ] KOPYALA
Geliştirme Compose
# docker-compose.dev.yml
services :
backend :
build :
context : ./backend
target : build # Dev bağımlılıkları yükle
command : npm run dev # ts-node-dev ile hot reload
volumes :
- ./backend/src:/app/src # Kaynak bind mount
- /app/node_modules # Container'ın kendi node_modules
environment :
NODE_ENV : development
DATABASE_URL : postgresql://dev:dev@db:5432/blogdev
REDIS_URL : redis://cache:6379
ports :
- "3000:3000"
- "9229:9229" # Debugger
depends_on :
db :
condition : service_healthy
frontend :
build :
context : ./frontend
target : build
command : npm run dev
volumes :
- ./frontend/src:/app/src
- /app/node_modules
environment :
NEXT_PUBLIC_API_URL : http://localhost:3000
ports :
- "3001:3000"
db :
image : postgres:16-alpine
environment :
POSTGRES_USER : dev
POSTGRES_PASSWORD : dev
POSTGRES_DB : blogdev
volumes :
- postgres-dev:/var/lib/postgresql/data
- ./backend/prisma/seed.sql:/docker-entrypoint-initdb.d/seed.sql
ports :
- "5432:5432" # Local DB araçları için
healthcheck :
test : [ "CMD-SHELL" , "pg_isready -U dev -d blogdev" ]
interval : 5s
timeout : 3s
retries : 10
cache :
image : redis:7-alpine
ports :
- "6379:6379"
mailpit :
image : axllent/mailpit
ports :
- "1025:1025" # SMTP
- "8025:8025" # Web UI
volumes :
postgres-dev : KOPYALA
Makefile ile Kısayollar
# Makefile
.PHONY : dev prod down logs shell db-cli
dev :
docker compose -f docker-compose.yml -f docker-compose.dev.yml up
prod :
docker compose up -d
down :
docker compose down
logs :
docker compose logs -f backend
shell :
docker compose exec backend sh
db-cli :
docker compose exec db psql -U dev -d blogdev
db-reset :
docker compose down -v
docker compose -f docker-compose.yml -f docker-compose.dev.yml up db -d
sleep 5
docker compose exec backend npx prisma db push
docker compose exec backend npx prisma db seed
test :
docker compose exec backend npm test KOPYALA
Ortam Değişkenleri Yönetimi
# .env.example — commit'lenir, değerler gizlenmez
DATABASE_URL = postgresql://user:password@db:5432/blogdb
REDIS_URL = redis://cache:6379
JWT_SECRET = your-super-secret-jwt-key-here
CORS_ORIGIN = http://localhost:3001
NODE_ENV = development
# .env — commit'lenmez (.gitignore'da)
DATABASE_URL = postgresql://dev:dev@localhost:5432/blogdev
JWT_SECRET = localdevelopmentonly-change-in-prod KOPYALA
# docker-compose.yml — ortam dosyasını kullan
services :
backend :
env_file :
- .env
# veya tekil değişkenler
environment :
- NODE_ENV=${NODE_ENV:-development} KOPYALA
Layer Cache Optimizasyonu
# KÖTÜ — her değişiklikte npm install çalışır
COPY . .
RUN npm install
# İYİ — package.json değişmediğinde cache'den alır
COPY package*.json ./
RUN npm ci # Cache katmanı
COPY . . # Kaynak kod (sık değişir)
RUN npm run build KOPYALA
Bu optimizasyon, kaynak değiştikçe npm install 'ın tekrar çalışmasını engeller. Büyük projelerde dakikalarca zaman kazandırır.
Production için Güvenlik Kontrol Listesi
# Image'ı güvenlik açıkları için tara
docker scout cves myapp:latest
# veya
trivy image myapp:latest
# Root olmayan kullanıcı kontrolü
docker inspect myapp:latest | jq '.[0].Config.User'
# "app" çıkmalı, "" (root) değil
# Gereksiz kapı açık mı?
docker inspect mycontainer | jq '.[0].NetworkSettings.Ports'
# Read-only filesystem
docker run --read-only --tmpfs /tmp myapp:latest KOPYALA
CI/CD Entegrasyonu
# .github/workflows/docker.yml
name : Docker Build & Push
on :
push :
branches : [ main ]
jobs :
build :
runs-on : ubuntu-latest
steps :
- uses : actions/checkout@v4
- name : Docker meta
id : meta
uses : docker/metadata-action@v5
with :
images : ghcr.io/${{ github.repository }}
tags : |
type=sha
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }}
- name : Build ve Push
uses : docker/build-push-action@v5
with :
context : ./backend
push : true
tags : ${{ steps.meta.outputs.tags }}
cache-from : type=gha
cache-to : type=gha,mode=max KOPYALA
Sonuç
Docker ile geliştirme ortamı; yeni bir ekip üyesinin dakikalar içinde çalışmaya başlamasını, "bende çalışıyor" sorununu yok etmeyi ve production ile geliştirme arasındaki uçurumu kapatmayı sağlar. Makefile kısayolları ile günlük komutları sadeleştirebilir, layer cache optimizasyonu ile build sürelerini dramatik biçimde kısaltabilirsiniz.