Docker Compose for Development: Local Infrastructure

Docker Compose for local development: PostgreSQL, Redis, Authentik, networking, health checks, volume management, and environment-specific configurations for TypeScript monorepos.

E
ECOSIRE Research and Development Team
|19 Mart 20268 dk okuma1.8k Kelime|

Docker Compose for Development: Yerel Altyapı

Keyifli bir başlangıç ​​deneyimi ("pnpm dev:infra kopyala ve çalıştır") ile acı verici bir deneyim ("önce PostgreSQL'i kurun, sonra Redis'i yapılandırın, sonra...") arasındaki fark, Docker Compose kurulumunuzun altyapı gereksinimlerinizi ne kadar iyi yakaladığıyla ilgilidir. İyi hazırlanmış bir docker-compose.dev.yml, herhangi bir makinedeki herhangi bir geliştiricinin dakikalar içinde tam olarak aynı altyapıyı çalıştırmasını sağlar.

Bu kılavuz, üretim kalitesinde bir yerel geliştirme yığınına yönelik kalıpları kapsar: hizmet yapılandırması, durum denetimleri, ağ oluşturma, birim yönetimi ve uygulamanızın başlatma sırası ile entegrasyon.

Önemli Çıkarımlar

  • Sistem kurulumlarıyla çakışmaları önlemek için yerel olarak PostgreSQL için varsayılan olmayan bir bağlantı noktası kullanın (5433)
  • Hizmet bağımlılıklarındaki durum denetimleri "bağlantı reddedildi" başlatma hatalarını önler
  • Adlandırılmış birimler, kapsayıcı yeniden başlatmaları arasında veritabanı verilerini korur; bağlama bağlamaları Windows'ta güvenilir şekilde çalışmaz
  • Ortam değişkenlerini .env.local dosyanızdan kapsayıcılara yüklemek için env_file kullanın
  • docker-compose.dev.yml ile docker-compose.prod.yml'yi ayırın — farklı amaçlara hizmet ederler
  • depends_on.condition: service_healthy modeli yalnızca konteynerin başlatılmasını değil, gerçek hazırlığı bekler
  • İsteğe bağlı hizmetleri (e-posta, izleme) dahil etmek için profiles kullanın
  • docker-compose (v1) değil docker compose (v2) komutunu çalıştırın — eklenti sözdizimi günceldir

Eksiksiz Geliştirme Yığını

# infrastructure/docker-compose.dev.yml
name: ecosire-dev

services:
  # ─── PostgreSQL ─────────────────────────────────────────────────
  postgres:
    image: postgres:17-alpine
    container_name: ecosire-postgres
    environment:
      POSTGRES_DB: ecosire_dev
      POSTGRES_USER: ecosire
      POSTGRES_PASSWORD: dev_password_change_in_prod
    ports:
      - "5433:5432"  # 5433 externally — avoids conflicts with system PostgreSQL
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./init-scripts:/docker-entrypoint-initdb.d  # Run SQL on first start
    command: >
      postgres
        -c shared_buffers=256MB
        -c effective_cache_size=1GB
        -c work_mem=16MB
        -c maintenance_work_mem=128MB
        -c checkpoint_completion_target=0.9
        -c wal_buffers=16MB
        -c max_connections=100
        -c log_min_duration_statement=100
        -c log_statement=ddl
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ecosire -d ecosire_dev"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 10s
    restart: unless-stopped

  # ─── Redis ──────────────────────────────────────────────────────
  redis:
    image: redis:7-alpine
    container_name: ecosire-redis
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data
    command: >
      redis-server
        --maxmemory 512mb
        --maxmemory-policy allkeys-lru
        --appendonly yes
        --appendfsync everysec
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: unless-stopped

  # ─── Authentik (Identity Provider) ──────────────────────────────
  authentik-server:
    image: ghcr.io/goauthentik/server:2024.12
    container_name: ecosire-authentik
    command: server
    environment:
      AUTHENTIK_REDIS__HOST: redis
      AUTHENTIK_POSTGRESQL__HOST: postgres
      AUTHENTIK_POSTGRESQL__USER: ecosire
      AUTHENTIK_POSTGRESQL__PASSWORD: dev_password_change_in_prod
      AUTHENTIK_POSTGRESQL__NAME: authentik
      AUTHENTIK_SECRET_KEY: dev-secret-key-change-in-production-32chars
      AUTHENTIK_ERROR_REPORTING__ENABLED: "false"
      AUTHENTIK_DISABLE_STARTUP_ANALYTICS: "true"
    volumes:
      - authentik_media:/media
      - authentik_certs:/certs
    ports:
      - "9000:9000"  # HTTP
      - "9443:9443"  # HTTPS
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
    healthcheck:
      test: ["CMD-SHELL", "ak healthcheck"]
      interval: 30s
      timeout: 10s
      retries: 5
      start_period: 60s  # Authentik takes time to initialize
    restart: unless-stopped

  authentik-worker:
    image: ghcr.io/goauthentik/server:2024.12
    container_name: ecosire-authentik-worker
    command: worker
    environment:
      AUTHENTIK_REDIS__HOST: redis
      AUTHENTIK_POSTGRESQL__HOST: postgres
      AUTHENTIK_POSTGRESQL__USER: ecosire
      AUTHENTIK_POSTGRESQL__PASSWORD: dev_password_change_in_prod
      AUTHENTIK_POSTGRESQL__NAME: authentik
      AUTHENTIK_SECRET_KEY: dev-secret-key-change-in-production-32chars
    volumes:
      - authentik_media:/media
      - authentik_certs:/certs
      - /var/run/docker.sock:/var/run/docker.sock  # For Authentik's proxy
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
    restart: unless-stopped

  # ─── Mailpit (Email Testing) ─────────────────────────────────────
  mailpit:
    image: axllent/mailpit:latest
    container_name: ecosire-mailpit
    ports:
      - "1025:1025"  # SMTP
      - "8025:8025"  # Web UI
    environment:
      MP_MAX_MESSAGES: 200
      MP_SMTP_AUTH_ACCEPT_ANY: true
      MP_SMTP_AUTH_ALLOW_INSECURE: true
    restart: unless-stopped
    profiles:
      - email  # Optional — use `docker compose --profile email up`

  # ─── pgAdmin (Database GUI) ─────────────────────────────────────
  pgadmin:
    image: dpage/pgadmin4:latest
    container_name: ecosire-pgadmin
    environment:
      PGADMIN_DEFAULT_EMAIL: [email protected]
      PGADMIN_DEFAULT_PASSWORD: admin
      PGADMIN_CONFIG_SERVER_MODE: "False"
      PGADMIN_CONFIG_MASTER_PASSWORD_REQUIRED: "False"
    ports:
      - "5050:80"
    volumes:
      - pgadmin_data:/var/lib/pgadmin
    depends_on:
      postgres:
        condition: service_healthy
    restart: unless-stopped
    profiles:
      - tools  # Optional

networks:
  default:
    name: ecosire-dev-network

volumes:
  postgres_data:
    name: ecosire-postgres-data
  redis_data:
    name: ecosire-redis-data
  authentik_media:
    name: ecosire-authentik-media
  authentik_certs:
    name: ecosire-authentik-certs
  pgadmin_data:
    name: ecosire-pgadmin-data

Package.json Komut Dosyaları

Docker Compose komutlarını monorepo komut dosyalarınıza bağlayın:

{
  "scripts": {
    "dev:infra": "docker compose -f infrastructure/docker-compose.dev.yml up -d",
    "dev:infra:down": "docker compose -f infrastructure/docker-compose.dev.yml down",
    "dev:infra:logs": "docker compose -f infrastructure/docker-compose.dev.yml logs -f",
    "dev:infra:reset": "docker compose -f infrastructure/docker-compose.dev.yml down -v && pnpm dev:infra",
    "dev:infra:email": "docker compose -f infrastructure/docker-compose.dev.yml --profile email up -d",
    "dev:infra:tools": "docker compose -f infrastructure/docker-compose.dev.yml --profile tools up -d"
  }
}

--profile bayrağı, isteğe bağlı hizmetlerin (Mailpit ile e-posta testi, pgAdmin ile veritabanı GUI'si) açıkça talep edilene kadar hareketsiz kalmasına izin verir.


Veritabanı Başlatma Komut Dosyaları

SQL dosyalarını infrastructure/init-scripts/ içine yerleştirin; bunlar ilk kapsayıcı başlangıcında çalışırlar:

-- infrastructure/init-scripts/01-create-databases.sql
-- Create all databases Authentik needs separately from the app DB
CREATE DATABASE authentik;
GRANT ALL PRIVILEGES ON DATABASE authentik TO ecosire;

-- Create test database for CI
CREATE DATABASE ecosire_test;
GRANT ALL PRIVILEGES ON DATABASE ecosire_test TO ecosire;
-- infrastructure/init-scripts/02-extensions.sql
-- Enable PostgreSQL extensions
\c ecosire_dev;
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS "pg_trgm";  -- Trigram search
CREATE EXTENSION IF NOT EXISTS "btree_gin"; -- Composite GIN indexes

\c ecosire_test;
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

Başlatma komut dosyaları alfabetik sırayla çalışır. \c database_name psql metakomutu etkin veritabanını değiştirir.


Ortam Değişkenleri Entegrasyonu

Uygulamanız monorepo kökündeki .env.local'dan okuyor. Docker hizmetlerinin, hizmet adlarını (localhost değil) kullanarak birbirine nasıl bağlanacağını bilmesi gerekir:

# .env.local (monorepo root)

# PostgreSQL — use 5433 externally (host) or 5432 internally (container network)
DATABASE_URL=postgresql://ecosire:dev_password_change_in_prod@localhost:5433/ecosire_dev

# Redis
REDIS_URL=redis://localhost:6379

# Authentik — use 9000 for external calls from your dev machine
AUTHENTIK_URL=http://localhost:9000
# Use service name for server-to-server calls within Docker network
AUTHENTIK_INTERNAL_URL=http://authentik-server:9000

# Email (Mailpit SMTP)
SMTP_HOST=localhost
SMTP_PORT=1025
SMTP_SECURE=false

# Application
NODE_ENV=development

Docker'ın içinde çalışan ve diğer hizmetlerle konuşması gereken uygulamalar için hizmet adlarını kullanın. Ana makinenizde çalışan uygulamalar için (geliştirme modunda NestJS, Next.js), ana makine eşlemeli bağlantı noktalarıyla birlikte localhost kullanın.


Sağlık Kontrolleri Derinlemesine İnceleme

Durum denetimleri art arda gelen başlatma hatalarını önler. depends_on.condition: service_healthy yalnızca konteynerin başlatılmasını değil, gerçek hazırlığı da bekler:

# Without health checks — can fail because PostgreSQL isn't ready
depends_on:
  - postgres

# With health checks — waits for PostgreSQL to accept connections
depends_on:
  postgres:
    condition: service_healthy

Kendi hizmetleriniz için özel durum kontrolleri:

// apps/api/src/health/health.controller.ts
@Get()
@Public()
@HealthCheck()
async check() {
  return this.health.check([
    () => this.db.isHealthy('database'),
    () => this.redis.isHealthy('redis'),
  ]);
}
# If your API is also dockerized
api:
  image: ecosire-api:latest
  healthcheck:
    test: ["CMD", "curl", "-f", "http://localhost:3001/api/health"]
    interval: 30s
    timeout: 10s
    retries: 3
    start_period: 30s

Birim Yönetimi

Adlandırılmış birimler, yeniden başlatmalar arasında verileri korur. Her birim türünün ne zaman kullanılacağını anlayın:

TürKalıcılıkPerformansKullanım Amaçlı
Adlandırılmış ciltEvetMükemmelVeritabanı verileri
Bağlama montajıEvetİyi (Linux), Kötü (macOS)Kaynak kodu çalışırken yeniden yükleme
tmpf'lerHayırMükemmelGeçici dosyalar, sırlar
# Use bind mounts for source code (enables hot reload)
volumes:
  - ./apps/api/src:/app/src  # Code changes reflected immediately

# Use named volumes for data
volumes:
  - postgres_data:/var/lib/postgresql/data

# Use tmpfs for ephemeral data
volumes:
  - type: tmpfs
    target: /tmp

Docker Masaüstüne sahip macOS'ta bağlama bağlamaları, Linux'a göre önemli ölçüde daha yavaş olan gRPC FUSE'yi kullanır. NestJS ve Next.js geliştirme sunucuları için, yerel dosya sistemi performansı elde etmek amacıyla bunları doğrudan ana makinenizde (Docker'da değil) çalıştırın.


Üretim Docker Oluşturma

Üretim oluşturma dosyası yapısal olarak farklıdır; yerel bağlantı noktaları, yeniden başlatma politikaları ve üretim kaynağı sınırları yoktur:

# infrastructure/docker-compose.prod.yml
name: ecosire-prod

services:
  postgres:
    image: postgres:17-alpine
    environment:
      POSTGRES_DB: ${POSTGRES_DB}
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - postgres_data:/var/lib/postgresql/data
    # No port mapping — only accessible within Docker network
    restart: always
    deploy:
      resources:
        limits:
          memory: 2G
        reservations:
          memory: 512M

  redis:
    image: redis:7-alpine
    command: redis-server --requirepass ${REDIS_PASSWORD} --maxmemory 1gb
    volumes:
      - redis_data:/data
    restart: always
    # No port mapping — internal only

volumes:
  postgres_data:
  redis_data:

Üretim, bağlantı noktalarını harici olarak açığa çıkarmaz; uygulamalar dahili Docker ağı aracılığıyla bağlanır. Nginx harici trafiği yönetir.


Yaygın Tuzaklar ve Çözümler

Tuzak 1: Bağlantı noktası sistem hizmetleriyle çakışıyor

PostgreSQL, Redis ve diğer hizmetler genellikle sistem hizmetleri olarak çalışır. Geliştirme aşamasında her zaman standart olmayan bağlantı noktalarını eşleştirin:

  • PostgreSQL: 5433:5432 (5432:5432 değil)
  • Redis: 6379:6379'ı koruyun (nadiren çakışır)
  • Varsayılan bağlantı noktasını neyin kullandığını kontrol etmek için lsof -i :5432 komutunu çalıştırın

Tuzak 2: Linux'ta toplu izin sorunları

Linux'taki Docker birimleri varsayılan olarak kök sahipliğini kullanır. Kapsayıcı kullanıcınız root değilse doğru sahipliği ayarlayın:

postgres:
  image: postgres:17-alpine
  user: "999:999"  # postgres user's UID:GID
  # Or use init container to fix permissions

Tuzak 3: Authentik'in başlatılması 60+ saniye sürer

Authentik, veritabanı geçişlerini ilk başlangıçta çalıştırır. Durum kontrolünde start_period: 60s zaman verir. Bağımlı hizmetler Authentik hazır olmadan başlarsa başarısız olurlar. service_healthy koşulunu kullanın ve ona yeterli miktarda start_period verin.

4. Tuzak: Mac'te Docker Masaüstü kaynak sınırları

Varsayılan Docker Masaüstü, 2 CPU ve 2 GB RAM ayırır; bu, PostgreSQL + Redis + Authentik'in aynı anda çalışması için yeterli değildir. Docker Masaüstü Ayarları > Kaynaklar'ı en az 4 CPU ve 6 GB RAM'e yükseltin.

Tuzak 5: docker-compose ve docker compose

Eski docker-compose (v1, Python'da yazılmıştır) kullanımdan kaldırılmıştır. docker compose (v2, eklenti) kullanın. Sürümünüzü kontrol edin: docker compose version. Docker Compose version v2.x.x görüyorsanız v2 kullanıyorsunuz demektir.


Sıkça Sorulan Sorular

Uygulama hizmetlerimi (NestJS, Next.js) geliştirme sırasında Docker'da çalıştırmalı mıyım?

Genellikle hayır; aktif geliştirme için, daha hızlı çalışır durumda yeniden yükleme ve daha kolay hata ayıklama için uygulama hizmetlerinizi ana makinenizde çalıştırın. Docker'ı yalnızca kararlı olan ve sık sık yeniden başlatılması gerekmeyen altyapı hizmetleri (veritabanları, önbellekler, kimlik sağlayıcılar) için kullanın. Bunun istisnası, uygulamanızın geliştirme işletim sisteminiz ile üretim ortamı arasında farklılık gösteren yerel bağımlılıklara sahip olmasıdır.

Docker Compose iş akışında veritabanı geçişlerini nasıl yönetirim?

Altyapıyı başlattıktan sonra taşıma işlemlerini ana makinenizden çalıştırın: pnpm dev:infra && pnpm db:migrate. Geliştirme sırasında geçişleri Docker kapsayıcısının içinde çalıştırmayın; Drizzle geçişlerini güvenli kılan tür denetimini ve IDE entegrasyonunu kaybedersiniz. İlk veritabanı oluşturmak için Docker'ın initdb.d komut dosyalarını kullanın.

Yerel Docker birimlerimi nasıl yedeklerim ve geri yüklerim?

Yedeklemek için docker run --rm -v postgres_data:/data -v $(pwd):/backup alpine tar czf /backup/postgres-backup.tar.gz /data kullanın. tar xzf kullanarak aynı yaklaşımla geri yükleyin. Geliştirme için, bağlantı noktasını açıkta bıraktığınız için pg_dump ile dökümü yapabilir ve psql ile geri yükleyebilirsiniz.

Docker Compose durumunu diğer ekip üyeleriyle nasıl paylaşırım?

Docker Compose dosyası git aracılığıyla paylaşılır ancak birimlerdeki veriler yereldir. Her geliştirici boş bir veritabanıyla başlar ve onu doldurmak için geçişleri/tohumları çalıştırır. Tutarlı test verileri oluşturmak için tohum komut dosyalarını (repoya bağlı) kullanın. Paylaşılan docker-compose.dev.yml herkesin aynı hizmet sürümlerini ve yapılandırmasını kullanmasını sağlar.

Geliştirme aşamasında neden gerçek e-posta yerine Mailpit kullanılmalı?

Mailpit, giden tüm e-postaları yakalayan ve bunları görüntülemek için bir web kullanıcı arayüzü sağlayan yerel bir SMTP sunucusudur. Geliştirme sırasında gerçek kullanıcılara yanlışlıkla gerçek e-posta gönderilmesini önler, SMTP kimlik bilgilerini gerektirmez ve e-posta şablonlarını gelen kutunuzu kontrol etmeden doğrulamanıza olanak tanır. Uygulamanızı SMTP_HOST=localhost SMTP_PORT=1025 kullanacak şekilde yapılandırın ve yakalanan e-postaları görmek için http://localhost:8025 adresini ziyaret edin.


Sonraki Adımlar

Yerel kalkınmaya yönelik iyi hazırlanmış bir Docker Compose kurulumu, yeni bir geliştiricinin katıldığı veya yeni bir makineyi çalıştırdığınız her seferde kârını ödeyen bir yatırımdır. ECOSIRE, tüm ekip genelinde yerel gelişim için Docker Compose'da PostgreSQL 17, Redis 7 ve Authentik'i çalıştırıyor.

Yerel geliştirme altyapınızı tasarlamak veya uygulamanızı üretim için kapsayıcı hale getirmek için yardıma mı ihtiyacınız var? Nasıl yardımcı olabileceğimizi görmek için DevOps hizmetlerimizi keşfedin.

E

Yazan

ECOSIRE Research and Development Team

ECOSIRE'da kurumsal düzeyde dijital ürünler geliştiriyor. Odoo entegrasyonları, e-ticaret otomasyonu ve yapay zeka destekli iş çözümleri hakkında içgörüler paylaşıyor.

WhatsApp'ta Sohbet Et