Isparta’da B2B muhasebe SaaS hizmeti veren 38 mühendis çalıştıran bir şirket, “develop my machine works, ama production’da çalışmıyor” sorunlarından bıkıp 32 microservice’i Docker’a paketledi. 4 ayda development + CI/CD + production tamamen container-tabanlı oldu. Bu yazı sahadaki teknik notlar.
Container Nedir, VM’den Farkı
| Konu | VM | Container |
|---|---|---|
| OS | Tam OS (Windows/Linux) | Host OS kernel + lib paylaşır |
| Boyut | ~~GB | ~~MB |
| Açılış süresi | ~~dakika | ~~saniye |
| İzolasyon | Hipervisor (güçlü) | Namespace + cgroup (yeterli) |
| Density (1 host’ta kaç tane) | ~~10-50 | ~~100-1000 |
| Use case | Farklı OS, full app | Stateless service, microservice |
Multi-Stage Dockerfile (.NET 8 API)
# --- BUILD STAGE ---
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
# Cache restore layer (only invalidates if csproj changes)
COPY ["src/OrderService/OrderService.csproj", "OrderService/"]
RUN dotnet restore "OrderService/OrderService.csproj"
# Copy and build
COPY src/OrderService/. OrderService/
WORKDIR /src/OrderService
RUN dotnet build -c Release -o /app/build
# --- PUBLISH STAGE ---
FROM build AS publish
RUN dotnet publish -c Release -o /app/publish /p:UseAppHost=false
# --- RUNTIME STAGE (small image) ---
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS final
WORKDIR /app
# Non-root user
RUN groupadd -r appuser && useradd -r -g appuser appuser
USER appuser
COPY --from=publish --chown=appuser:appuser /app/publish .
EXPOSE 8080
ENV ASPNETCORE_URLS=http://+:8080
ENV ASPNETCORE_ENVIRONMENT=Production
HEALTHCHECK --interval=30s --timeout=5s --start-period=20s
CMD wget --quiet --tries=1 --spider http://localhost:8080/health || exit 1
ENTRYPOINT ["dotnet", "OrderService.dll"]
Sonuç image: ~~210 MB (build stage 1.8 GB değil — production’a gitmiyor).
Image Layer Optimizasyonu
| Pratik | Etki |
|---|---|
| Multi-stage build | SDK production’a gitmez |
| Cache layer (csproj önce, kod sonra) | Restore cached, build hızlı |
| Alpine base image (uygun yerde) | ~~50 MB → ~~10 MB |
| Non-root user | Güvenlik (CVE etkisi azalır) |
| .dockerignore | node_modules, .git, bin, obj kopyalanmaz |
| HEALTHCHECK | Orchestrator pod sağlığı bilir |
Docker Compose (Local Dev)
version: '3.9'
services:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: orderdb
POSTGRES_USER: dev
POSTGRES_PASSWORD: devpass
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U dev"]
interval: 5s
retries: 10
ports: ["5432:5432"]
redis:
image: redis:7-alpine
ports: ["6379:6379"]
rabbitmq:
image: rabbitmq:3-management-alpine
ports: ["5672:5672", "15672:15672"]
order-service:
build:
context: .
dockerfile: src/OrderService/Dockerfile
environment:
ConnectionStrings__Db: "Host=postgres;Database=orderdb;Username=dev;Password=devpass"
Redis__Host: "redis:6379"
RabbitMQ__Host: "rabbitmq"
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_started
rabbitmq:
condition: service_started
ports: ["8081:8080"]
inventory-service:
build:
context: .
dockerfile: src/InventoryService/Dockerfile
environment:
ConnectionStrings__Db: "Host=postgres;Database=invdb;Username=dev;Password=devpass"
ports: ["8082:8080"]
volumes:
pgdata:
Geliştirici tek komutla tüm stack’i ayağa kaldırır: docker compose up.
Private Registry (Harbor)
# Image build + tag
docker build -t harbor.firma.com.tr/saas/order-service:1.4.2
-f src/OrderService/Dockerfile .
# Push
docker login harbor.firma.com.tr
docker push harbor.firma.com.tr/saas/order-service:1.4.2
# Trivy security scan (CI/CD)
trivy image --severity HIGH,CRITICAL --exit-code 1
harbor.firma.com.tr/saas/order-service:1.4.2
Harbor Avantajları
- Private (internal repo)
- Replication (Azure Container Registry’ye sync)
- Built-in vulnerability scan (Trivy entegre)
- RBAC + project-based access
- Image signing (Notary v2)
Production: Container Orchestration Seçimi
| Opsiyon | Karmaşıklık | Kullanım |
|---|---|---|
| Docker standalone | Düşük | Tek host, basit servis |
| Docker Swarm | Orta | Küçük cluster, sabit topology |
| Kubernetes (K8s/AKS) | Yüksek | Production microservice, scale, advanced |
| Azure Container Apps | Düşük (managed) | Microservice, serverless container |
Şirket Azure Container Apps + AKS hibrit (Container Apps default, KEDA + heavy stateful AKS) seçti.
Volume Yönetimi
Stateless container: volume yok, image'dan baştan ayağa kalk
Stateful (DB, persistent): volume mount şart
Local dev:
docker volume create pgdata
docker run -v pgdata:/var/lib/postgresql/data postgres
Production (orchestrator):
- Kubernetes PersistentVolume (Azure Disk / Files)
- Container Apps: Azure Files mount
Networking
| Network type | Kullanım |
|---|---|
| Bridge (default) | Single host container’lar arası |
| Host | Container host network’ünü kullanır (performance) |
| Overlay (Swarm/K8s) | Multi-host container’lar arası |
| None | İzole, ağ yok (security) |
Sonuçlar
| Metrik | Önce | Sonra |
|---|---|---|
| “Çalışmıyor benim makinemde” sorunu | ~~haftalık | Yok |
| Yeni geliştirici onboarding | ~~1 gün (env kurulumu) | ~~30 dk (compose up) |
| Deployment süresi | ~~30 dk | ~~3 dk (image pull + deploy) |
| Resource kullanımı (RAM, dev makine) | ~~16 GB (3 VM) | ~~4 GB (3 container) |
| Image security CVE high/critical | — | 0 (Trivy gate) |
Sahada Düşülen Üç Tuzak
- Multi-stage build kullanmamak: SDK’lı image production’a 1.5-2 GB gider, attack surface büyür.
- Root user ile container çalıştırmak: CVE etkisi yıkıcı olabilir. Non-root user mecbur.
- HEALTHCHECK eklememek: Orchestrator container “ayakta” sanır ama app crashed. HEALTHCHECK + readiness probe şart.
CloudSpark olarak Docker container mimarisi, multi-stage build optimizasyonu, private registry (Harbor/ACR), security scan, Docker Compose dev ortamı ve production orchestration için danışmanlık veriyoruz.



