Docker Katman Cache ve İmaj Boyutu
30 saniyede özet
Docker imajı üst üste dizilmiş katmanlardan oluşur ve bir katman değişirse üstündekilerin hepsi yeniden hazırlanır. Dockerfile'daki satırların sırası, build'in 10 saniye mi 100 saniye mi süreceğini bu yüzden belirler.
Tek bir Java dosyası değiştirdin ve paketleme dakikalarca sürdü. Bütün kütüphaneler yeniden indirildi — oysa kütüphane listesine hiç dokunmadın.
-
Bayt: Bir satır değiştirdim, Docker yine bütün internet'i indiriyor!
-
Sen: Kütüphane listesini mi değiştirdin?
-
Bayt: Hayır, pom.xml'e dokunmadım bile. Sadece bir Java dosyası.
-
Bayt: Kitap yığınının ortasından bir kitap çekmek gibi mi bu?
Adım adım oku
- Kötü sırada önce bütün kaynak kod kopyalanır, bağımlılıklar ondan sonra indirilir.
- Tek bir dosya değişince o kopyalama katmanı ve altındaki her katman yeniden hazırlanır; indirme de baştan yapılır.
- İyi sırada önce yalnızca bağımlılık listesi kopyalanır ve bağımlılıklar indirilir, kaynak kod en sona kalır.
- Kod değişince yalnızca son katman yeniden hazırlanır; yavaş indirme cache'te bekler.
Tek bir kural
Docker her talimat için bir katmanDockerfile'daki her talimatın ürettiği, yeniden kullanılabilen ara imaj. Bir katman değişince ondan sonraki tüm katmanlar yeniden kurulur.Katmanlar birikimlidir: bir katmanda indirip sonrakinde sildiğin dosya alt katmanda durmaya devam eder ve imaj boyutundan düşmez.Sözlükte gör → üretir ve bir katmanı yeniden kullanır ancak girdileri değişmemişse.
Kritik kısmı şu: bir katman geçersizleşirse, aynı aşamada altındaki her katman da geçersizleşir.
FROM ... ✓ cacheWORKDIR /app ✓ cacheCOPY . . ✕ kaynak değiştiRUN ./mvnw package ✕ üstü ıskaladı → bu da ıskalarDördüncü satırın girdileri aslında değişmemiş olabilir. Fark etmez — zincir kırıldığı an aşağısı tamamen yeniden çalışır.
Kafam karıştı, daha basit anlat
Docker her adımı bir kat olarak saklar. Bir kat değişirse, üstüne kurulan bütün katlar da yeniden yapılır. Tıpkı alttaki bir tuğlayı değiştirmek gibi.
Kendin gör
Docker katman cache — sıralama neden derleme süresini belirler
Tohum 3- ·
FROM eclipse-temurin:21-jdk - ·
WORKDIR /app - ·
COPY . . - ·
RUN ./mvnw package -DskipTests - ·
ENTRYPOINT ["java","-jar","target/app.jar"]
Bu build
0 sn
soğuk cache: 103 sn
Cache’ten gelen
2/5
katman
İmaj boyutu
672 MB
çok aşamalı olsa 252 MB
Şu an ne oldu?
Build bağlamı: 12 MB
Docker, tek bir satır çalıştırmadan önce bağlamın tamamını daemon’a gönderir. Bu yüzden .dockerignore yalnızca imaj boyutunu değil, her build’in başlangıç süresini de etkiler.
Aklında kalsın: .dockerignore’ı imaj temizliği sanmak yaygın bir eksiktir; asıl etkisi bağlam boyutu ve cache geçerliliğidir.
Olay günlüğü (0)
Henüz olay yok. Oynat veya adımla.
Şu sırayla dene:
Önce COPY . .+Bir Java dosyası. ~99 saniye. Bağımlılık indirmesi yeniden çalıştı.Önce pom.xml’e geç. Aynı değişiklik ~29 saniye. Tek fark: iki satırın yeri.- Değişikliği
Hiçbir şeyyap,.dockerignoreanahtarını kapat. Build yine ~107 saniye. Hiçbir şey değişmediği hâlde. Çok aşamalıya geç. Süre benzer ama imaj 672 MB’tan 252 MB’a düşüyor.- Değişikliği
Temel imajyap. Her şey yeniden çalışıyor — ve bu doğru davranış.
Docker katman cache'i ne zaman geçersiz olur?
Sıralama: nadir üste, sık alta
Dockerfile önce COPY . . ile bütün projeyi kopyalıyor, sonra ./mvnw package çalıştırıyor. Tek bir Java dosyasını değiştirip yeniden build ettin. Bağımlılıklar yeniden indirilir mi? Cevabı göster
Evet. Kopyalanan dosyalardan biri değişince o katman ve altındaki bütün katmanlar geçersiz olur; indirme de o katmanların içinde.
Kural, kuralın kendisinden çıkar. Cache’i korumak istiyorsan az değişeni yukarı koy.
FROM eclipse-temurin:21-jdkWORKDIR /appCOPY . .RUN ./mvnw package -DskipTestsFROM eclipse-temurin:21-jdkWORKDIR /appCOPY pom.xml .RUN ./mvnw dependency:go-offline # pom.xml değişmedikçe cache'te kalırCOPY src ./srcRUN ./mvnw package -o -DskipTestspom.xml ayda birkaç kez değişir, src/ günde onlarca kez. İki satırın yerini değiştirmek build süresini üçte bire indirir.
Kafam karıştı, daha basit anlat
Az değişen şeyleri alt katlara, sık değişen kodunu en üste koy. Böylece kodu her değiştirdiğinde yalnızca en üst kat yeniden yapılır.
Her Dockerfile satırını, cache dostu bir sırada üstte mi altta mı olması gerektiğine göre ayır.
.dockerignore — sanılandan önemli. Simülatördeki 3. adım en sinsi senaryo: hiçbir şey değişmediği hâlde cache hiç tutmuyor.
Sebep şu: COPY . . tüm dizini kopyalar, target/ de dahil. Yerelde bir kez mvn package çalıştırdıysan target/ değişmiştir, dolayısıyla katman geçersizdir.
target/.git/.idea/*.mdİki ayrı kazanç var:
- Cache geçerliliği. Build çıktıları bağlamdan çıkınca katman gereksiz yere geçersizleşmez.
- Bağlam boyutu. Docker, tek satır çalıştırmadan önce bağlamın tamamını daemon’a gönderir. 240 MB yerine 12 MB göndermek her build’in başına saniyeler ekler ya da çıkarır.
`.dockerignore` dosyası neden önemlidir?
Çok aşamalı build
Derlemek için JDK, Maven ve tüm bağımlılıklar gerekir. Çalıştırmak için hiçbiri gerekmez.
FROM eclipse-temurin:21-jdk AS buildWORKDIR /appCOPY pom.xml .RUN ./mvnw dependency:go-offlineCOPY src ./srcRUN ./mvnw package -o -DskipTests
FROM eclipse-temurin:21-jreCOPY --from=build /app/target/app.jar app.jarENTRYPOINT ["java","-jar","/app.jar"]Son FROM’dan sonrası imaja girer; öncesi build makinesinde kalır. 672 MB → 252 MB.
İkinci, daha az bilinen kazanç: aşamalar ayrı cache zincirleridir. Kaynak kod değiştiğinde çalıştırma aşamasının FROM ...-jre katmanı cache’te kalır, çünkü o değişiklik onun aşamasını ilgilendirmez.
Boyut neden önemli: her deploy’da registry’den çekilen bayt, her node’da tutulan disk ve saldırı yüzeyi. JRE imajında derleyici yoktur — bir saldırganın kullanabileceği araç da yoktur.
Bir adım ötesi: layered jar. Spring Boot fat jar’ı tek bir dosyadır, yani tek bir katman. Uygulama kodunun bir satırı değişse 70 MB’lık katmanın tamamı yeniden iner.
RUN java -Djarmode=layertools -jar app.jar extractCOPY --from=build /app/dependencies/ ./COPY --from=build /app/application/ ./ # yalnızca bu katman değişirBağımlılıklar ve uygulama kodu ayrı katmanlara bölünür. Deploy’da yalnızca birkaç yüz kilobayt aktarılır.
Hepsini bir arada görmek ister misin? Aşağıda bir bankanın havale servisi için küçük bir proje var: sıralama, çok aşamalı build ve layered jar aynı dosyada.
Derinleş · Havale servisinin imajı: katman katman 3 dosya · ~43 satır · ilk okumada atlayabilirsin
Kafam karıştı, daha basit anlat
Derlemek için büyük bir alet çantası gerekir, çalıştırmak için değil. Çok aşamalı build, çantayı atölyede bırakıp yalnızca bitmiş ürünü paketlemektir.
Bir Spring Boot Dockerfile'ında her satırı değişim sıklığına göre yerleştir.
Tuzaklar ve CI’da cache
Yerelde çalışan cache, CI’da çoğu zaman çalışmaz — her job temiz bir makinede başlar ve yerel katman deposu boştur.
Çözüm, cache’i registry üzerinden paylaşmaktır:
docker buildx build \ --cache-from type=registry,ref=myapp:buildcache \ --cache-to type=registry,ref=myapp:buildcache,mode=max .Bu satır olmadan yukarıdaki tüm sıralama çabası CI’da hiçbir işe yaramaz — yerelde kazandığın süreyi orada geri verirsin.
Kendini sına
Bir katman değişince aynı aşamada ondan sonraki bütün katmanlar da yeniden hazırlanır.
Bu Dockerfile'da tek bir Java dosyası değiştiğinde ne yeniden çalışır?
Aklında kalacak üç şey
- 1 Bir katman değişirse, aynı aşamada ondan sonra gelen her katman da yeniden hazırlanır. Satır sırasının önemi buradan gelir.
- 2 Az değişen şey üste, sık değişen şey alta. Bağımlılık listesi kaynak koddan çok daha az değişir, bu yüzden önce o kopyalanır.
- 3 Çok aşamalı build iki işe yarar: derleme araçları son imajdan çıkar ve çalıştırma aşamasının cache'i derleme aşamasından bağımsızlaşır.
5 kart sonraki derste seni bekliyor
Bu dersin üstüne kurulanlar
Bunlar bu dersi temel alıyor; hazır olduğunda devam edebilirsin.
- DevOps & TestYayın Stratejileri — Hatalı Sürüm Kaç Kişiye Dokunur?Yeni sürüm hatalı çıktı. Bunu kaç kullanıcı fark etti: hepsi mi, yarısı mı, yoksa yalnızca birkaçı mı?Derse git
- DevOps & TestCI Pipeline — Geri Bildirim Neden Bu Kadar Geç Geliyor?Bir satır değiştirdin ve yeşil ışığı yirmi dakika bekliyorsun. Bu yirmi dakikanın ne kadarı gerçekten gerekli?Derse git
- DevOps & TestSırlar — Parola Git'e Girerse Ne Olur?Parolayı commit'ledin, fark edince dosyayı sildin ve yeniden commit'ledin. Artık güvende misin?Derse git