REFERANS
Docker ile SQL Server & PostgreSQL Kurulumu
Local development için veritabanlarını Docker container olarak çalıştırmak en hızlı ve tekrarlanabilir yöntemdir. Bu bölüm: container kurulumu, EF Core bağlantı yapılandırması, migration uygulaması ve production-ready docker-compose senaryolarını kapsar.
1. Docker Kurulumu
# SQL Server 2022 (Developer Edition — ücretsiz, development için)
docker run -d \
--name sqlserver \
-e "ACCEPT_EULA=Y" \
-e "MSSQL_SA_PASSWORD=YourStr0ng!Pass" \
-e "MSSQL_PID=Developer" \
-p 1433:1433 \
-v sqlserver_data:/var/opt/mssql \
mcr.microsoft.com/mssql/server:2022-latest
# Kontrol:
docker logs sqlserver
# "SQL Server is now ready for client connections" mesajını bekle
SA şifresi gereksinimleri: En az 8 karakter, büyük harf + küçük harf + rakam + özel karakter. Basit şifre verirsen container hemen kapanır.
# PostgreSQL 16
docker run -d \
--name postgres \
-e POSTGRES_USER=appuser \
-e POSTGRES_PASSWORD=YourStr0ng!Pass \
-e POSTGRES_DB=appdb \
-p 5432:5432 \
-v postgres_data:/var/lib/postgresql/data \
postgres:16-alpine
# Kontrol:
docker exec -it postgres psql -U appuser -d appdb -c "SELECT version();"
2. Docker Compose — Tam Geliştirme Ortamı
# docker-compose.yml
services:
sqlserver:
image: mcr.microsoft.com/mssql/server:2022-latest
container_name: sqlserver
environment:
ACCEPT_EULA: "Y"
MSSQL_SA_PASSWORD: "YourStr0ng!Pass"
MSSQL_PID: "Developer"
ports:
- "1433:1433"
volumes:
- sqlserver_data:/var/opt/mssql
healthcheck:
test: /opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P "$$MSSQL_SA_PASSWORD" -No -Q "SELECT 1"
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
volumes:
sqlserver_data:
# docker-compose.yml
services:
postgres:
image: postgres:16-alpine
container_name: postgres
environment:
POSTGRES_USER: appuser
POSTGRES_PASSWORD: YourStr0ng!Pass
POSTGRES_DB: appdb
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U appuser -d appdb"]
interval: 5s
timeout: 3s
retries: 5
volumes:
postgres_data:
# Başlat:
docker compose up -d
# Durumu kontrol et:
docker compose ps
docker compose logs -f sqlserver
# Durdur (veri korunur):
docker compose stop
# Tamamen sil (veri DAHİL):
docker compose down -v
3. EF Core Connection String Yapılandırması
// appsettings.Development.json
{
"ConnectionStrings": {
"Default": "Server=localhost,1433;Database=AppDb;User Id=sa;Password=YourStr0ng!Pass;TrustServerCertificate=True;"
}
}
// Program.cs
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(
builder.Configuration.GetConnectionString("Default"),
sqlOptions =>
{
sqlOptions.EnableRetryOnFailure(
maxRetryCount: 3,
maxRetryDelay: TimeSpan.FromSeconds(10),
errorNumbersToAdd: null);
sqlOptions.CommandTimeout(30);
}));
// appsettings.Development.json
{
"ConnectionStrings": {
"Default": "Host=localhost;Port=5432;Database=appdb;Username=appuser;Password=YourStr0ng!Pass;"
}
}
// Program.cs
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseNpgsql(
builder.Configuration.GetConnectionString("Default"),
npgsqlOptions =>
{
npgsqlOptions.EnableRetryOnFailure(
maxRetryCount: 3,
maxRetryDelay: TimeSpan.FromSeconds(10),
errorCodesToAdd: null);
npgsqlOptions.CommandTimeout(30);
}));
🚨 Güvenlik:
appsettings.Development.jsondosyasını.gitignore'a ekle. Production'da connection string'leri environment variable veya Azure Key Vault / AWS Secrets Manager ile yönet. Asla source control'e şifre koyma.
4. Container Hazır Olana Kadar Bekleme (Startup Resilience)
Container başlatıldıktan sonra DB hemen bağlantı kabul etmeyebilir. Retry pattern ile bekle:
// Program.cs — Migration uygularken retry:
using var scope = app.Services.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
var retryCount = 0;
const int maxRetries = 10;
while (retryCount < maxRetries)
{
try
{
await db.Database.MigrateAsync();
break;
}
catch (Exception ex) when (retryCount < maxRetries - 1)
{
retryCount++;
Console.WriteLine($"DB bağlantısı bekleniyor... ({retryCount}/{maxRetries})");
await Task.Delay(TimeSpan.FromSeconds(3));
}
}
Alternatif: Docker Compose depends_on + healthcheck kullanarak API container'ının DB hazır olduktan sonra başlamasını sağla:
# docker-compose.yml — API service
api:
build: .
depends_on:
sqlserver:
condition: service_healthy
environment:
ConnectionStrings__Default: "Server=sqlserver,1433;Database=AppDb;User Id=sa;Password=YourStr0ng!Pass;TrustServerCertificate=True;"
# docker-compose.yml — API service
api:
build: .
depends_on:
postgres:
condition: service_healthy
environment:
ConnectionStrings__Default: "Host=postgres;Port=5432;Database=appdb;Username=appuser;Password=YourStr0ng!Pass;"
Docker network içinde container ismi host olarak kullanılır (
postgres,sqlserver),localhostdeğil.
5. İlk Database ve Migration Oluşturma
# Migration oluştur:
dotnet ef migrations add InitialCreate -o Data/Migrations
# Migration'ı Docker'daki DB'ye uygula:
dotnet ef database update
# Alternatif: SQL script olarak export et:
dotnet ef migrations script -o migrations.sql
Migration öncesi DB'yi manuel oluşturma:
# Container içinde sqlcmd ile DB oluştur:
docker exec -it sqlserver /opt/mssql-tools18/bin/sqlcmd \
-S localhost -U sa -P "YourStr0ng!Pass" -No \
-Q "CREATE DATABASE AppDb;"
# PostgreSQL'de ek DB oluştur (POSTGRES_DB dışında):
docker exec -it postgres psql -U appuser -c "CREATE DATABASE seconddb;"
6. Volume ve Data Persistence
| Strateji | Komut | Veri korunur mu? |
|---|---|---|
| Named volume | -v sqlserver_data:/var/opt/mssql |
Container silinse bile |
| Bind mount | -v ./data:/var/opt/mssql |
Host'ta görünür |
| No volume | (volume tanımlanmaz) | Container silinince kaybolur |
| Temiz başlangıç | docker compose down -v |
Volume dahil siler |
# Volume listele:
docker volume ls
# Volume içeriğini incele:
docker volume inspect sqlserver_data
# Sadece belirli volume'ü sil:
docker volume rm sqlserver_data
7. Birden Fazla Veritabanı (Multi-DB Senaryo)
Microservice'lerde her servisin kendi DB'si olabilir:
# docker-compose.yml — çoklu DB
services:
sqlserver:
image: mcr.microsoft.com/mssql/server:2022-latest
environment:
ACCEPT_EULA: "Y"
MSSQL_SA_PASSWORD: "YourStr0ng!Pass"
MSSQL_PID: "Developer"
ports:
- "1433:1433"
volumes:
- sqlserver_data:/var/opt/mssql
- ./init-scripts:/docker-entrypoint-initdb.d
volumes:
sqlserver_data:
-- init-scripts/01-create-databases.sql
-- sqlcmd ile çalıştır (container başlangıcında otomatik çalışmaz, manuel gerekir):
CREATE DATABASE orders_db;
GO
CREATE DATABASE inventory_db;
GO
CREATE DATABASE identity_db;
GO
-- Her DB için ayrı login:
CREATE LOGIN orders_svc WITH PASSWORD = 'OrdersPass123!';
GO
USE orders_db;
CREATE USER orders_svc FOR LOGIN orders_svc;
ALTER ROLE db_owner ADD MEMBER orders_svc;
GO
// Her DbContext farklı connection string:
builder.Services.AddDbContext<OrdersDbContext>(o =>
o.UseSqlServer(config.GetConnectionString("OrdersDb")));
builder.Services.AddDbContext<InventoryDbContext>(o =>
o.UseSqlServer(config.GetConnectionString("InventoryDb")));
# docker-compose.yml — çoklu DB
services:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_USER: admin
POSTGRES_PASSWORD: YourStr0ng!Pass
POSTGRES_DB: shared
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
- ./init-scripts:/docker-entrypoint-initdb.d # ← Başlangıç scriptleri
volumes:
postgres_data:
-- init-scripts/01-create-databases.sql
-- Container ilk başlatıldığında otomatik çalışır:
CREATE DATABASE orders_db;
CREATE DATABASE inventory_db;
CREATE DATABASE identity_db;
-- Her DB için ayrı kullanıcı (en az yetki prensibi):
CREATE USER orders_svc WITH PASSWORD 'OrdersPass123!';
GRANT ALL PRIVILEGES ON DATABASE orders_db TO orders_svc;
CREATE USER inventory_svc WITH PASSWORD 'InvPass123!';
GRANT ALL PRIVILEGES ON DATABASE inventory_db TO inventory_svc;
// Her DbContext farklı connection string:
builder.Services.AddDbContext<OrdersDbContext>(o =>
o.UseNpgsql(config.GetConnectionString("OrdersDb")));
builder.Services.AddDbContext<InventoryDbContext>(o =>
o.UseNpgsql(config.GetConnectionString("InventoryDb")));
8. Faydalı Docker Komutları — Günlük Kullanım
| Komut | Açıklama |
|---|---|
docker compose up -d |
Tüm servisleri başlat (detached) |
docker compose stop |
Durdur (veri korunur) |
docker compose down |
Container'ları sil (volume kalır) |
docker compose down -v |
Her şeyi sil (temiz başlangıç) |
docker compose logs -f <service> |
Canlı log takibi |
docker compose restart <service> |
Tek servisi yeniden başlat |
docker stats |
CPU/RAM kullanımı |
Veritabanı CLI erişimi:
# SQL Server CLI (sqlcmd):
docker exec -it sqlserver /opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P "YourStr0ng!Pass" -No
# Tek sorgu çalıştır:
docker exec -it sqlserver /opt/mssql-tools18/bin/sqlcmd \
-S localhost -U sa -P "YourStr0ng!Pass" -No \
-Q "SELECT name FROM sys.databases;"
# PostgreSQL CLI (psql):
docker exec -it postgres psql -U appuser -d appdb
# Tek sorgu çalıştır:
docker exec -it postgres psql -U appuser -d appdb -c "\dt"
9. Sık Karşılaşılan Sorunlar
| Problem | Neden | Çözüm |
|---|---|---|
| SQL Server container hemen kapanıyor | Zayıf SA password | En az 8 karakter, büyük+küçük+rakam+özel |
| "Connection refused" hatası | Container henüz hazır değil | Healthcheck + retry pattern kullan |
| Port çakışması (1433/5432 meşgul) | Host'ta başka DB çalışıyor | Port'u değiştir: -p 1434:1433 |
| Permission denied (Linux volume) | UID mismatch | -e MSSQL_AGENT_ENABLED=false veya chown |
| PostgreSQL "role does not exist" | Yanlış kullanıcı adı | POSTGRES_USER env var'ı kontrol et |
| Migration timeout | DB henüz bağlantı kabul etmiyor | depends_on: condition: service_healthy |
| Veri kayboldu | Volume tanımlanmamış | Named volume veya bind mount ekle |
| Container içinden dışarı erişilemiyor | Network isolation | host.docker.internal kullan (host erişimi) |
10. Production'a Geçiş Notları
Docker Compose production veritabanı için önerilmez (tek node, HA yok). Production'da:
| Ortam | SQL Server | PostgreSQL |
|---|---|---|
| Azure | Azure SQL Database (managed) | Azure Database for PostgreSQL Flexible |
| AWS | RDS for SQL Server | RDS for PostgreSQL / Aurora |
| Self-hosted | Docker Compose sadece dev/test | Docker Compose sadece dev/test |
| Kubernetes | StatefulSet + PVC (dikkatli!) | CloudNativePG Operator / Crunchy |
// Production — managed DB:
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(
Environment.GetEnvironmentVariable("DATABASE_URL")
?? throw new InvalidOperationException("DATABASE_URL not configured"),
o => o.EnableRetryOnFailure()));
// Production — managed DB:
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseNpgsql(
Environment.GetEnvironmentVariable("DATABASE_URL")
?? throw new InvalidOperationException("DATABASE_URL not configured"),
o => o.EnableRetryOnFailure()));
Tavsiye: Development'ta Docker Compose + local container kullan. Staging/Production'da managed database servisi kullan. Connection string'leri her ortam için ayrı tut (User Secrets → Environment Variables → Key Vault).