← Kursa Dön
📄 Text · 25 min

Jib: Container Build without Docker

Giriş

Google Jib, Java uygulamaları için optimize edilmiş bir container image oluşturma aracıdır. Jib'in en dikkat çekici özelliği: Docker daemon gerektirmez. Dockerfile yazmanıza, Docker kurmanıza veya docker build çalıştırmanıza gerek yoktur. Jib, Maven veya Gradle plugin'i olarak çalışarak doğrudan container registry'ye push eder.

Bunu bir fotoğraf basma hizmetine benzetin: geleneksel yöntemde fotoğrafı çekersiniz (kaynak kodu), karanlık odada işlersiniz (Docker build), zarfa koyarsınız (push). Jib ise fotoğrafı çeker ve doğrudan albüme yapıştırır — aradaki tüm adımları ortadan kaldırır.

Bu derste Jib'in nasıl çalıştığını, Maven ve Gradle entegrasyonunu, layer optimizasyonunu, base image yapılandırmasını, authentication yönetimini ve Dockerfile ile karşılaştırmasını derinlemesine inceleyeceğiz.

Neden Jib?

Geleneksel Docker Build Süreci

1. Kaynak kodu yaz
2. mvn package → JAR oluştur
3. Dockerfile yaz
4. Docker daemon çalışıyor olmalı
5. docker build → image oluştur
6. docker push → registry'ye gönder
7. Cache yönetimini kendin yap

Jib ile

1. Kaynak kodu yaz
2. mvn compile jib:build → Bitti! Image registry'de.

Jib'in Avantajları

AvantajAçıklama
Docker daemon gerekmezCI/CD'de Docker kurulumu gereksiz. Docker-in-Docker (DinD) karmaşıklığı ortadan kalkar
Dockerfile gerekmezJava uygulamaları için best practice'ler otomatik uygulanır
Reproducible buildsAynı kaynak kodu → her zaman aynı image (deterministic)
Otomatik layer optimizasyonuBağımlılıklar, kaynaklar ve sınıflar otomatik ayrı katmanlara ayrılır
Hızlı incremental buildSadece değişen katmanlar yeniden oluşturulur
Best practice'ler otomatikNon-root user, reproducible timestamps, distroless base image

Maven Plugin Kurulumu

<build>
    <plugins>
        <plugin>
            <groupId>com.google.cloud.tools</groupId>
            <artifactId>jib-maven-plugin</artifactId>
            <version>3.4.4</version>
            <configuration>
                <!-- Base image — uygulamanın üzerine inşa edileceği image -->
                <from>
                    <image>eclipse-temurin:21-jre-alpine</image>
                </from>

                <!-- Hedef image — oluşturulacak image -->
                <to>
                    <image>registry.example.com/order-service</image>
                    <tags>
                        <tag>latest</tag>
                        <tag>${project.version}</tag>
                        <tag>${git.commit.id.abbrev}</tag>
                    </tags>
                </to>

                <!-- Container yapılandırması -->
                <container>
                    <!-- JVM flags -->
                    <jvmFlags>
                        <jvmFlag>-XX:+UseContainerSupport</jvmFlag>
                        <jvmFlag>-XX:MaxRAMPercentage=75.0</jvmFlag>
                        <jvmFlag>-XX:+UseG1GC</jvmFlag>
                        <jvmFlag>-XX:MaxGCPauseMillis=200</jvmFlag>
                        <jvmFlag>-XX:+HeapDumpOnOutOfMemoryError</jvmFlag>
                        <jvmFlag>-XX:HeapDumpPath=/tmp/heapdump.hprof</jvmFlag>
                        <jvmFlag>-XX:+ExitOnOutOfMemoryError</jvmFlag>
                    </jvmFlags>

                    <!-- Açılacak portlar -->
                    <ports>
                        <port>8080</port>
                    </ports>

                    <!-- Environment variables -->
                    <environment>
                        <SPRING_PROFILES_ACTIVE>docker</SPRING_PROFILES_ACTIVE>
                        <TZ>Europe/Istanbul</TZ>
                    </environment>

                    <!-- Non-root kullanıcı -->
                    <user>1000:1000</user>

                    <!-- Image oluşturulma zamanı -->
                    <creationTime>USE_CURRENT_TIMESTAMP</creationTime>

                    <!-- Metadata -->
                    <labels>
                        <org.opencontainers.image.title>order-service</org.opencontainers.image.title>
                        <org.opencontainers.image.version>${project.version}</org.opencontainers.image.version>
                        <maintainer>platform-team@example.com</maintainer>
                    </labels>
                </container>
            </configuration>
        </plugin>
    </plugins>
</build>

Gradle Plugin Kurulumu

plugins {
    id 'com.google.cloud.tools.jib' version '3.4.4'
}

jib {
    from {
        image = 'eclipse-temurin:21-jre-alpine'
    }
    to {
        image = 'registry.example.com/order-service'
        tags = ['latest', project.version]
    }
    container {
        jvmFlags = [
            '-XX:+UseContainerSupport',
            '-XX:MaxRAMPercentage=75.0',
            '-XX:+UseG1GC',
            '-XX:MaxGCPauseMillis=200',
            '-XX:+HeapDumpOnOutOfMemoryError',
            '-XX:HeapDumpPath=/tmp/heapdump.hprof'
        ]
        ports = ['8080']
        environment = [
            SPRING_PROFILES_ACTIVE: 'docker',
            TZ: 'Europe/Istanbul'
        ]
        user = '1000:1000'
        creationTime = 'USE_CURRENT_TIMESTAMP'
        labels = [
            'maintainer': 'platform-team@example.com',
            'org.opencontainers.image.title': 'order-service'
        ]
    }
}

Build Komutları

# ===== Maven =====

# 1. Doğrudan registry'ye push (Docker daemon GEREKMEZ!)
./mvnw compile jib:build
# Image doğrudan registry.example.com/order-service:latest olarak push edilir

# 2. Yerel Docker daemon'a image oluştur (Docker GEREKLİ)
./mvnw compile jib:dockerBuild
# docker images komutuyla görüntülenebilir

# 3. Tar dosyasına export et (Docker GEREKLİ DEĞİL)
./mvnw compile jib:buildTar
# target/jib-image.tar oluşur
# docker load --input target/jib-image.tar ile yüklenebilir

# ===== Gradle =====
./gradlew jib            # Registry'ye push
./gradlew jibDockerBuild # Yerel Docker'a
./gradlew jibBuildTar    # Tar dosyasına

# ===== Parametreli Build =====
./mvnw compile jib:build \
    -Djib.to.image=myregistry.com/myapp \
    -Djib.to.tags=v1.2.3,latest \
    -Djib.from.image=eclipse-temurin:21-jre-alpine

💡 İpucu: jib:build en güçlü komuttur — Docker daemon'a ihtiyaç duymaz, doğrudan registry'ye push eder. CI/CD pipeline'larında Docker yüklemenize gerek kalmaz, bu da pipeline kurulumunu basitleştirir ve Docker-in-Docker (DinD) karmaşıklığını ortadan kaldırır.

Jib Layer Yapısı

Jib, uygulamayı otomatik olarak optimize edilmiş katmanlara ayırır:

┌──────────────────────────────────────┐
│  Layer 5: Application classes        │ ← Sık değişir (~2 MB)
│  (com/example/*.class)               │
├──────────────────────────────────────┤
│  Layer 4: Application resources      │ ← Ara sıra değişir (~1 MB)
│  (application.yml, static/, ...)     │
├──────────────────────────────────────┤
│  Layer 3: Snapshot dependencies      │ ← Nadiren değişir (~5 MB)
│  (*-SNAPSHOT.jar)                    │
├──────────────────────────────────────┤
│  Layer 2: Project dependencies       │ ← Çok nadiren değişir (~70 MB)
│  (spring-boot, hibernate, ...)       │
├──────────────────────────────────────┤
│  Layer 1: Base image (JRE)           │ ← Neredeyse hiç değişmez
│  (eclipse-temurin:21-jre-alpine)     │
└──────────────────────────────────────┘

Bu katmanlama Spring Boot'un layered JAR'ına benzer ancak Jib bunu tamamen otomatik yapar — ek yapılandırma, Dockerfile veya extract adımı gerekmez.

Spring Boot Layered JAR vs Jib Layer Karşılaştırması

Spring Boot Layered JAR:
├── dependencies/           (3rd party libs)
├── spring-boot-loader/     (loader classes)
├── snapshot-dependencies/  (SNAPSHOT libs)
└── application/            (your classes + resources)

Jib:
├── dependencies/           (3rd party libs)
├── snapshot-dependencies/  (SNAPSHOT libs)
├── resources/              (application.yml, static/)  ← AYRI!
└── classes/                (compiled Java classes)      ← AYRI!

Jib, resources ve classes'ı ayrı layer'lara koyar. Bu, sadece application.yml değiştiğinde bile Java sınıflarının cache'ten gelmesini sağlar.

Base Image Yapılandırması

Eclipse Temurin (Önerilen — genel kullanım)

<from>
    <image>eclipse-temurin:21-jre-alpine</image>
</from>

Google Distroless (Maksimum güvenlik)

<from>
    <image>gcr.io/distroless/java21-debian12</image>
</from>

Distroless image'lar, yalnızca çalışma zamanı bağımlılıklarını içerir — shell, paket yöneticisi veya gereksiz araçlar yoktur. Bu, güvenlik açığı yüzeyini minimuma indirir.

Distroless:
✅ Shell yok → saldırgan shell açamaz
✅ Paket yöneticisi yok → ek yazılım kurulamaz
✅ Minimal CVE yüzeyi
❌ Debugging zor (shell yok!)
❌ exec ile container'a giremezsiniz

Belirli SHA Digest ile (Reproducible)

<from>
    <image>eclipse-temurin:21-jre-alpine@sha256:abc123def456...</image>
</from>

SHA digest kullanmak, base image'ın registry'de değişmesi durumunda bile aynı image'ı almanızı garanti eder.

Private Registry'den Base Image

<from>
    <image>private-registry.company.com/base-images/jre:21</image>
    <auth>
        <username>${env.REGISTRY_USER}</username>
        <password>${env.REGISTRY_PASS}</password>
    </auth>
</from>

Registry Authentication

Docker Hub

# Docker config ile (varsayılan)
docker login
# ~/.docker/config.json'a credential kaydedilir
# Jib otomatik olarak bu dosyayı okur

Google Container Registry (GCR)

gcloud auth configure-docker
# veya
gcloud auth print-access-token | docker login -u oauth2accesstoken --password-stdin gcr.io

Amazon ECR

aws ecr get-login-password --region eu-west-1 | \
    docker login --username AWS --password-stdin \
    123456789.dkr.ecr.eu-west-1.amazonaws.com

GitHub Container Registry (GHCR)

echo $GITHUB_TOKEN | docker login ghcr.io -u $GITHUB_USER --password-stdin

Maven settings.xml ile

<!-- ~/.m2/settings.xml -->
<servers>
    <server>
        <id>registry.example.com</id>
        <username>${env.DOCKER_USER}</username>
        <password>${env.DOCKER_PASS}</password>
    </server>
</servers>
<!-- pom.xml'de server ID referansı -->
<to>
    <image>registry.example.com/myapp</image>
    <credHelper>none</credHelper>
</to>

Extra Directories — Ek Dosyalar

Java dosyaları dışında ek dosyalar eklemek için extraDirectories kullanın:

<configuration>
    <extraDirectories>
        <paths>
            <!-- Statik dosyalar -->
            <path>
                <from>src/main/resources/static</from>
                <into>/app/static</into>
            </path>
            <!-- Konfigürasyon dosyaları -->
            <path>
                <from>config</from>
                <into>/app/config</into>
            </path>
            <!-- Script dosyaları -->
            <path>
                <from>scripts</from>
                <into>/app/scripts</into>
            </path>
        </paths>
        <permissions>
            <permission>
                <file>/app/scripts/entrypoint.sh</file>
                <mode>755</mode>
            </permission>
        </permissions>
    </extraDirectories>
</configuration>

CI/CD Pipeline ile Jib

GitHub Actions

name: Build with Jib

on:
  push:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest  # Docker KURULUMU GEREKMEZ!
    steps:
      - uses: actions/checkout@v4

      - name: Set up JDK 21
        uses: actions/setup-java@v4
        with:
          java-version: '21'
          distribution: 'temurin'
          cache: maven

      - name: Build and push with Jib
        run: |
          ./mvnw compile jib:build \
            -Djib.to.image=ghcr.io/${{ github.repository }}:${{ github.sha }} \
            -Djib.to.auth.username=${{ github.actor }} \
            -Djib.to.auth.password=${{ secrets.GITHUB_TOKEN }}
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Docker kurulumu yok! Pipeline sadece JDK kurar ve mvn jib:build çalıştırır. Bu, pipeline'ı hem basitleştirir hem de hızlandırır (Docker pull/setup süresi atlanır).

Jenkins

pipeline {
    agent any
    stages {
        stage('Build & Push') {
            steps {
                withCredentials([usernamePassword(
                    credentialsId: 'docker-registry',
                    usernameVariable: 'REGISTRY_USER',
                    passwordVariable: 'REGISTRY_PASS')]) {

                    sh '''
                        ./mvnw compile jib:build \
                            -Djib.to.auth.username=$REGISTRY_USER \
                            -Djib.to.auth.password=$REGISTRY_PASS
                    '''
                }
            }
        }
    }
}

Jib vs Dockerfile: Detaylı Karşılaştırma

KriterDockerfileJib
Docker daemon✅ Gerekli❌ Gerekmez
Dockerfile✅ Yazılmalı❌ Gerekmez
Layer optimizasyonuManuel (layered JAR)✅ Otomatik
ReproducibilityEkstra çaba✅ Varsayılan
EsneklikÇok yüksekJava uygulamaları için
Öğrenme eğrisiDocker bilgisi gerekirPlugin config yeterli
Build hızıCache'e bağlı✅ Incremental, hızlı
Non-Java dosyalar✅ DesteklerSınırlı (extraDirectories)
Multi-arch✅ buildx ile⚠️ Sınırlı destek
Native kütüphaneler✅ RUN ile yüklenebilir❌ Desteklemez
Debug (shell)✅ exec ile⚠️ Distroless'te yok

Ne Zaman Jib?

  • ✅ Saf Java/Spring Boot uygulamaları

  • ✅ CI/CD'de Docker kurulumu istemiyorsanız

  • ✅ Hızlı, reproducible build'ler istiyorsanız

  • ✅ Layer optimizasyonunu otomatik istiyorsanız

Ne Zaman Dockerfile?

  • ✅ Native kütüphaneler (JNI, FFI) gerekiyorsa

  • ✅ Özel sistem paketleri yüklemeniz gerekiyorsa (ffmpeg, imagemagick...)

  • ✅ Multi-stage build ile karmaşık pipeline gerekiyorsa

  • ✅ Non-Java uygulamalar (Python, Node.js...)

Yaygın Hatalar

1. Registry Authentication Eksik

# ❌ HATA: 401 Unauthorized
./mvnw jib:build

# ✅ Önce authenticate olun
docker login registry.example.com
# veya Maven settings.xml'de credential tanımlayın

2. creationTime Ayarlanmamış

<!-- ❌ Varsayılan: epoch (1970-01-01)
     docker images'da tarih garip görünür -->
<container>
    <!-- creationTime belirtilmemiş → reproducible ama kafa karıştırıcı -->
</container>

<!-- ✅ Güncel timestamp -->
<container>
    <creationTime>USE_CURRENT_TIMESTAMP</creationTime>
</container>

3. Spring Boot DevTools'u Image'a Eklemek

<!-- pom.xml'de devtools OPTIONAL olmalı -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <optional>true</optional>
    <!-- Jib, optional dependency'leri otomatik hariç tutar -->
</dependency>

4. JVM Flags Eksik

<!-- ❌ YANLIŞ — container ortamında JVM ayarsız çalışır -->
<container>
    <!-- jvmFlags belirtilmemiş → JVM container bellek limitini tanımayabilir -->
</container>

<!-- ✅ DOĞRU — container-aware JVM ayarları -->
<container>
    <jvmFlags>
        <jvmFlag>-XX:+UseContainerSupport</jvmFlag>
        <jvmFlag>-XX:MaxRAMPercentage=75.0</jvmFlag>
    </jvmFlags>
</container>

Özet

  • Jib, Docker daemon ve Dockerfile gerektirmeden container image oluşturur — mvn compile jib:build yeterli

  • Otomatik layer optimizasyonu: Dependencies, resources ve classes ayrı katmanlarda — Spring Boot layered JAR'dan bile iyi

  • CI/CD basitliği: Pipeline'da Docker kurulumu gerekmez, sadece JDK yeterli

  • Reproducible builds: Aynı kaynak kodu → aynı image — deterministic

  • Authentication: Docker config, Maven settings.xml veya command-line args ile

  • Ne zaman kullanmalı: Saf Java/Spring Boot uygulamaları için mükemmel; native kütüphaneler veya sistem paketleri gerekiyorsa Dockerfile tercih edin

  • Spring Boot layered JAR vs Jib: İkisi de layer optimizasyonu yapar ama Jib bunu otomatik ve daha ince granülaritede yapar — Maven/Gradle cache'i de daha etkili kullanır

  • GraalVM Native Image ile Jib: jib:build sadece JVM tabanlı image üretir; GraalVM native compilation için Dockerfile + multi-stage build tercih edin

  • Kubernetes ile kullanım: Jib + Skaffold entegrasyonu ile her code change'de otomatik build-push-deploy döngüsü kurulabilir — skaffold.yaml'da builder olarak jib seçilir

  • Debug modu: jib:dockerBuild ile lokal Docker daemon'a build yapıp docker exec -it <container> /bin/sh ile debug edebilirsiniz; distroless base image kullandıysanız debug için gcr.io/distroless/java21-debian12:debug tag'ini tercih edin

  • Health check: Jib image'larında Dockerfile'daki HEALTHCHECK direktifine denk gelen bir yapı yoktur — Kubernetes'te liveness/readiness probe'ları, Docker Compose'da healthcheck: direktifi kullanın; Spring Boot Actuator /actuator/health endpoint'i bu amaçla idealdir