İçeriğe geç

Docker ile Spring Boot Uygulaması Deploy Etmek

T
Tolgahan
· · 13 dk okuma · 715 görüntülenme

Neden Docker ile Spring Boot?

Spring Boot uygulamanızı geliştirdiniz, testlerden geçti, her şey hazır. Ama deployment aşamasında "benim makinemde çalışıyordu" problemini herkes yaşamıştır. Farklı Java versiyonları, eksik system kütüphaneleri, farklı OS yapılandırmaları... Docker tüm bu sorunları ortadan kaldırır.

Docker ile Spring Boot uygulamanızı:

  • Tekrarlanabilir şekilde paketlersiniz — her build aynı sonucu verir

  • Taşınabilir hale getirirsiniz — laptop'tan AWS'ye aynı image çalışır

  • İzole edersiniz — bağımlılıklar diğer uygulamalarla çakışmaz

  • Ölçeklendirilebilir yaparsınız — Kubernetes ile kolayca scale edilir

Temel Dockerfile

İlk olarak basit bir Dockerfile ile başlayalım:

FROM eclipse-temurin:21-jre-alpine

WORKDIR /app

COPY target/*.jar app.jar

EXPOSE 8080

ENTRYPOINT ["java", "-jar", "app.jar"]

Bu Dockerfile çalışır ama birkaç problemi var:

  • Her kod değişikliğinde tüm JAR yeniden kopyalanır (layer cache kullanılamaz)

  • Build işlemi Docker dışında yapılması gerekir (mvn package önce çalıştırılmalı)

  • Image boyutu optimize edilmemiştir

Multi-Stage Build ile Optimizasyon

Multi-stage build, hem build hem de runtime aşamalarını tek bir Dockerfile'da tanımlamanızı sağlar. Build aşamasında kullanılan Maven, JDK ve kaynak kodlar final image'a dahil edilmez:

# Stage 1: Build
FROM eclipse-temurin:21-jdk-alpine AS build

WORKDIR /app

# Maven wrapper ve pom.xml'i kopyala (dependency cache için)
COPY mvnw pom.xml ./
COPY .mvn .mvn

# Dependency'leri indir (bu katman cache'lenir)
RUN ./mvnw dependency:go-offline -B

# Kaynak kodu kopyala ve build et
COPY src src
RUN ./mvnw package -DskipTests -B

# Stage 2: Runtime
FROM eclipse-temurin:21-jre-alpine

RUN addgroup -S appgroup && adduser -S appuser -G appgroup

WORKDIR /app

COPY --from=build /app/target/*.jar app.jar

RUN chown appuser:appgroup app.jar
USER appuser

EXPOSE 8080

HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
    CMD wget -qO- http://localhost:8080/actuator/health || exit 1

ENTRYPOINT ["java", \
    "-XX:+UseContainerSupport", \
    "-XX:MaxRAMPercentage=75.0", \
    "-Djava.security.egd=file:/dev/./urandom", \
    "-jar", "app.jar"]

Bu Dockerfile'da dikkat edilecek noktalar:

  • Dependency katmanı ayrı: pom.xml değişmediği sürece dependency'ler cache'den gelir

  • JRE kullanımı: Runtime'da JDK'ya gerek yok, JRE yeterli (image boyutu ~150MB daha küçük)

  • Non-root user: Güvenlik için uygulama root olarak çalışmaz

  • Container-aware JVM flags: UseContainerSupport ile JVM, container memory limit'ini doğru algılar

  • Health check: Docker, uygulamanın sağlıklı olup olmadığını kontrol eder

İpucu: eclipse-temurin OpenJDK'nın Adoptium tarafından dağıtılan production-grade build'idir. Alpine varyantı en küçük image boyutunu sağlar.

Spring Boot Layered JAR

Spring Boot 2.3+ ile gelen layered JAR özelliği, Docker cache'ini daha iyi kullanmanızı sağlar:

# Stage 1: Build
FROM eclipse-temurin:21-jdk-alpine AS build
WORKDIR /app
COPY mvnw pom.xml ./
COPY .mvn .mvn
RUN ./mvnw dependency:go-offline -B
COPY src src
RUN ./mvnw package -DskipTests -B

# Stage 2: Extract layers
FROM eclipse-temurin:21-jdk-alpine AS extract
WORKDIR /app
COPY --from=build /app/target/*.jar app.jar
RUN java -Djarmode=layertools -jar app.jar extract

# Stage 3: Runtime
FROM eclipse-temurin:21-jre-alpine
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app

COPY --from=extract /app/dependencies/ ./
COPY --from=extract /app/spring-boot-loader/ ./
COPY --from=extract /app/snapshot-dependencies/ ./
COPY --from=extract /app/application/ ./

USER appuser
EXPOSE 8080
ENTRYPOINT ["java", "org.springframework.boot.loader.launch.JarLauncher"]

Bu yaklaşımda uygulama kodunuz değiştiğinde sadece application katmanı yeniden build edilir. Spring dependency'leri ve third-party kütüphaneler cache'den gelir.

Docker Compose ile Geliştirme Ortamı

Gerçek dünya uygulamalarında veritabanı, cache, message queue gibi servisler de gerekir. Docker Compose ile tüm bileşenleri tek komutla ayağa kaldırabilirsiniz:

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "8080:8080"
    environment:
      - SPRING_PROFILES_ACTIVE=docker
      - SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/myapp
      - SPRING_DATASOURCE_USERNAME=root
      - SPRING_DATASOURCE_PASSWORD=secret
      - SPRING_DATA_REDIS_HOST=redis
      - SPRING_RABBITMQ_HOST=rabbitmq
    depends_on:
      mysql:
        condition: service_healthy
      redis:
        condition: service_started
    restart: unless-stopped

  mysql:
    image: mysql:8.0
    environment:
      MYSQL_DATABASE: myapp
      MYSQL_ROOT_PASSWORD: secret
    ports:
      - "3306:3306"
    volumes:
      - mysql_data:/var/lib/mysql
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data

  rabbitmq:
    image: rabbitmq:3-management-alpine
    ports:
      - "5672:5672"
      - "15672:15672"
    environment:
      RABBITMQ_DEFAULT_USER: admin
      RABBITMQ_DEFAULT_PASS: admin

volumes:
  mysql_data:
  redis_data:

depends_on ile condition: service_healthy kullanarak MySQL tamamen hazır olana kadar uygulamanın başlamaSını bekletebilirsiniz.

Environment Yönetimi

Spring Boot'un profile mekanizmasını Docker environment variable'ları ile birleştirebilirsiniz:

# application.yml
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/myapp
    username: root
    password: secret

---
# application-docker.yml
spring:
  config:
    activate:
      on-profile: docker
  datasource:
    url: ${SPRING_DATASOURCE_URL:jdbc:mysql://mysql:3306/myapp}
    username: ${SPRING_DATASOURCE_USERNAME:root}
    password: ${SPRING_DATASOURCE_PASSWORD:secret}

Hassas bilgiler (şifreler, API key'ler) için Docker secrets veya .env dosyası kullanın:

# .env dosyası (git'e eklemeyin!)
MYSQL_ROOT_PASSWORD=super_secret_password
SPRING_DATASOURCE_PASSWORD=super_secret_password
JWT_SECRET=my-jwt-secret-key-256-bit
# docker-compose.yml
services:
  app:
    env_file:
      - .env

Health Check ve Monitoring

Spring Boot Actuator ile sağlık kontrolü endpoint'leri ekleyin:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,prometheus
  endpoint:
    health:
      show-details: when-authorized

Dockerfile'daki HEALTHCHECK direktifi bu endpoint'i kullanır. Kubernetes ortamında liveness ve readiness probe'ları da aynı endpoint'lerle yapılandırılır.

Production Best Practices

  1. Image boyutunu küçük tutun: Alpine tabanlı image'lar kullanın. eclipse-temurin:21-jre-alpine yaklaşık 100MB'dir.

  2. Root olarak çalıştırmayın: Her zaman non-root user oluşturun.

  3. Build cache'i optimize edin: Sık değişen dosyaları (kaynak kodu) en sona koyun.

  4. Güvenlik taraması yapın: docker scout veya trivy ile image'daki güvenlik açıklarını tarayın:

# Trivy ile güvenlik taraması
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
    aquasec/trivy image myapp:latest
  1. Image tag'lerinde latest kullanmayın: Versiyon numarası ve commit hash kullanın:

docker build -t myapp:1.2.3 -t myapp:$(git rev-parse --short HEAD) .
  1. .dockerignore dosyası oluşturun:

.git
.gitignore
.idea
*.md
target
!target/*.jar
docker-compose*.yml
  1. JVM memory ayarlarını container'a göre yapın:

# Container'a 512MB memory limit verin
docker run -m 512m myapp:latest

# JVM otomatik olarak %75'ini (384MB) heap için kullanır
# -XX:MaxRAMPercentage=75.0 flag'i ile

Özet

  • Multi-stage build ile build ve runtime katmanlarını ayırın — image boyutu %70 küçülür

  • Layered JAR ile Docker cache'ini optimize edin — sadece değişen katmanlar rebuild edilir

  • Docker Compose ile tüm servisleri (DB, cache, queue) tek komutla yönetin

  • Non-root user, health check ve güvenlik taraması production'da zorunlu

  • Environment variable'lar ile konfigürasyonu dışarıdan yönetin — image'a secret gömmeyin

  • JVM container flags kullanarak memory'yi doğru yönetin

Paylaş:
Son güncelleme: Jul 18, 2026

Yorumlar

Giriş yapın ve yorum bırakın.

Henüz yorum yok

Düşüncelerinizi paylaşan ilk siz olun!

Bu yazıyı beğendiniz mi?

Bültene abone olun ve yeni yazılardan ilk siz haberdar olun. Spam yok, söz.

Bu konuyu derinlemesine öğrenmek ister misin?

Docker: Sıfırdan Uzmanlığa

60 ders Ücretsiz
Kursa Git

İlgili Yazılar