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 yapJib ile
1. Kaynak kodu yaz
2. mvn compile jib:build → Bitti! Image registry'de.Jib'in Avantajları
| Avantaj | Açıklama |
|---|---|
| Docker daemon gerekmez | CI/CD'de Docker kurulumu gereksiz. Docker-in-Docker (DinD) karmaşıklığı ortadan kalkar |
| Dockerfile gerekmez | Java uygulamaları için best practice'ler otomatik uygulanır |
| Reproducible builds | Aynı kaynak kodu → her zaman aynı image (deterministic) |
| Otomatik layer optimizasyonu | Bağımlılıklar, kaynaklar ve sınıflar otomatik ayrı katmanlara ayrılır |
| Hızlı incremental build | Sadece değişen katmanlar yeniden oluşturulur |
| Best practice'ler otomatik | Non-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:builden 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 giremezsinizBelirli 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ı okurGoogle Container Registry (GCR)
gcloud auth configure-docker
# veya
gcloud auth print-access-token | docker login -u oauth2accesstoken --password-stdin gcr.ioAmazon ECR
aws ecr get-login-password --region eu-west-1 | \
docker login --username AWS --password-stdin \
123456789.dkr.ecr.eu-west-1.amazonaws.comGitHub Container Registry (GHCR)
echo $GITHUB_TOKEN | docker login ghcr.io -u $GITHUB_USER --password-stdinMaven 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
| Kriter | Dockerfile | Jib |
|---|---|---|
| Docker daemon | ✅ Gerekli | ❌ Gerekmez |
| Dockerfile | ✅ Yazılmalı | ❌ Gerekmez |
| Layer optimizasyonu | Manuel (layered JAR) | ✅ Otomatik |
| Reproducibility | Ekstra çaba | ✅ Varsayılan |
| Esneklik | Çok yüksek | Java uygulamaları için |
| Öğrenme eğrisi | Docker bilgisi gerekir | Plugin config yeterli |
| Build hızı | Cache'e bağlı | ✅ Incremental, hızlı |
| Non-Java dosyalar | ✅ Destekler | Sı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ın2. 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:buildyeterliOtomatik 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:buildsadece JVM tabanlı image üretir; GraalVM native compilation için Dockerfile + multi-stage build tercih edinKubernetes 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çilirDebug modu:
jib:dockerBuildile lokal Docker daemon'a build yapıpdocker exec -it <container> /bin/shile debug edebilirsiniz; distroless base image kullandıysanız debug içingcr.io/distroless/java21-debian12:debugtag'ini tercih edinHealth check: Jib image'larında Dockerfile'daki
HEALTHCHECKdirektifine denk gelen bir yapı yoktur — Kubernetes'te liveness/readiness probe'ları, Docker Compose'dahealthcheck:direktifi kullanın; Spring Boot Actuator/actuator/healthendpoint'i bu amaçla idealdir
AI Asistan
Sorularını yanıtlamaya hazır