Docker Layer Cache Neden Hiç Tutmuyor
COPY . . satırı restore'un üstünde durunca

CI'da npm ci her push'ta 90 saniye. Lokal'de ikinci build saniyeler. Fark makine değil, context. Her commit COPY . . katmanını geçersiz kılıyor, üstündeki restore da evine gidiyordu. Cache 'bozuk' değildi. Ben bozuyordum.
Sıra sözleşmesi
Bir katman, üstündeki katmanların checksum'ına bağlı. Az değişen dosyayı önce kopyala. package-lock.json günde bir. src/ her commit. Lock'u üste al, npm ci onu izle, kaynak sonra gelsin. Tersi: her satır değişince ci.
# syntax=docker/dockerfile:1.7
FROM node:20.11.1-bookworm-slim AS build
WORKDIR /src
COPY package.json package-lock.json ./
RUN --mount=type=cache,target=/root/.npm \
npm ci
COPY tsconfig.json ./
COPY src/ src/
RUN npm run build
--mount=type=cache katman cache'inden ayrı. npm'in kendi cache'ini runner arasında yaşatır. Katman cache'i restore komutunu atlar; mount, komut çalışsa bile indirmeyi kısar. İkisini karıştırmayın. Mount'u 'o zaman COPY sırası önemsiz' diye okumak, her seferinde npm ci çalıştırır — sadece daha az ağ.
CI cache-from
Actions runner diskini unutur. Buildx cache-from: type=gha olmadan dün geceki katmanı görmez. Registry'ye --push ettiğin imaj, katman cache'inin tam ikamesi değil; özellikle ara stage'ler mode=max olmadan gitmez.
- uses: docker/setup-buildx-action@v3
- uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ghcr.io/${{ github.repository }}/api:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
mode=min sadece son imaj. Multi-stage'de build aşaması her seferinde yeniden. max ara katmanları da yazar. Depolama artar, 90 saniye düşer. İlk ısınmada kazanç yok; ikinci PR'da görünür. 'Cache tutmuyor' diye ilk job'a bakmayın.
Sessiz bozanlar
ARG satırı, altındaki her RUN için cache key'e girer. BUILD_DATE veya github.sha 'yı erken ARG yapmak, restore'u da öldürür. SHA'yı son satırda LABEL olarak koy. Tarih damgasını build'in başına koyma.
ARG'ı geç yaz
Build-arg'ı dosyanın tepesine koymak, altındaki her RUN 'u o arg değişince yeniler. GIT_SHA her commit değişir. Tepede durursa npm ci de yenilenir. SHA'yı son LABEL veya son ENV satırına indir. Tarih damgası da öyle. 'Build'e kimlik koyayım' içgüdüsü cache'i öldürür.
# syntax=docker/dockerfile:1.7
FROM node:20.11.1-bookworm-slim AS build
WORKDIR /src
COPY package.json package-lock.json ./
RUN --mount=type=cache,target=/root/.npm npm ci
COPY src/ src/
RUN npm run build
FROM node:20.11.1-bookworm-slim
WORKDIR /app
COPY --from=build /src/dist ./dist
ARG GIT_SHA=unknown
LABEL git.sha="${GIT_SHA}"
COPY --link BuildKit'te katmanı zincirden koparır, cache hit oranını yükseltir. Eski builder'da yok. syntax satırı olmadan --mount da yok. Dosyanın ilk satırı # syntax=docker/dockerfile:1.7 değilse, mount 'bilinmeyen flag' olur, CI kırmızı, cache hiç konuşulmaz.
apt cache mount ayrı: --mount=type=cache,target=/var/cache/apt + lists temizliği. Mount, paket dosyalarını runner arasında tutar; lists hâlâ katmana yazılırsa imaj şişer. Multi-stage yazısındaki tek RUN kuralı burada da durur. Cache mount, sırayı iptal etmez.
Registry cache ve GHA birlikte
cache-from: type=registry ile type=gha birlikte durabilir. Registry, runner değişince de yaşar. GHA, aynı repo işlerinde hızlıdır, retention kısadır. İkisini de yazmak, ilk ısınmayı registry'den, sonrakileri GHA'dan doldurur. Sadece registry, pull faturası. Sadece GHA, 7 gün sonra soğuk.
- uses: docker/build-push-action@v6
with:
context: .
file: Dockerfile
push: true
tags: ghcr.io/${{ github.repository }}/api:${{ github.sha }}
cache-from: |
type=gha
type=registry,ref=ghcr.io/${{ github.repository }}/api:buildcache
cache-to: |
type=gha,mode=max
type=registry,ref=ghcr.io/${{ github.repository }}/api:buildcache,mode=max
buildcache tag'i hareket eder — latest'in kuzeni. Bunu uygulama imajı sanma. Uygulama github.sha. Cache tag'i sadece BuildKit'in uzak katman deposu. Onu compose up ile çekmek, derleme ara katmanını koşturmaktır. Ayrı isim, ayrı amaç.
COPY --chmod ve --chown cache'i bozmaz, metadata'dır. ADD URL'den indirmek cache'i ağın haline bağlar. ADD kullanmıyorum. İndirme RUN + pinli URL veya build aşamasında paket yöneticisi.
name: image
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/build-push-action@v6
with:
context: .
file: Dockerfile
push: true
tags: ghcr.io/${{ github.repository }}/api:${{ github.sha }}
cache-from: |
type=gha
type=registry,ref=ghcr.io/${{ github.repository }}/api:buildcache
cache-to: |
type=gha,mode=max
type=registry,ref=ghcr.io/${{ github.repository }}/api:buildcache,mode=max
build-args: |
GIT_SHA=${{ github.sha }}
Ölçmeden 'cache bozuk' demiyorum
Buildx --progress=plain çıktısında CACHED satırını sayıyorum. Restore CACHED, COPY src değil. Tersi: restore her seferinde RUN, kaynak CACHED — sıra hâlâ ters veya lock her commit oynuyor. Lock'un her PR'da değişmesi ayrı bir hikâye; o zaman cache'in tutmaması doğru.
package-lock.json ile npm ci evli. npm install lock'u yeniden yazar, katman hash'i her job'da değişir. CI'da install yasak. Lokal'de de yasak. Bunu hook ile değil, kırmızı job ile anlatıyorum: lock diff'i boş değilse ve ci değilse kır.
Windows runner ile Linux runner aynı cache'i paylaşmaz. Matrix'e windows-2022 eklemek, GHA cache key'ine OS girmiyorsa çöp katman karışmasıdır. Key'e runner.os koy. Karışan cache, tutmuyor değil, yanlış tutuyor. Yanlış katman, yeşil build ve bozuk runtime üretir. Bunu bir kez yaşayınca key'e OS yazmayı unutmuyorum.
Cache tutmuyorsa önce sıraya, sonra ignore'a, sonra ARG yerine, sonra runner OS'a bakıyorum. Dördü de temizse lock dosyası her commit oynuyordur. O zaman problem cache değildir; bağımlılık hijyenidir. Hijyeni cache ile örtmeye çalışmak, 90 saniyeyi haklı çıkarır. Haklı 90 saniye, bozuk cache'den iyidir.
# lock first, source later — otherwise every commit busts npm ci
COPY package.json package-lock.json ./
RUN --mount=type=cache,target=/root/.npm npm ci
COPY src/ src/
RUN npm run build
.dockerignore yoksa context hash'i .git ile her commit değişir — kopyalamasan bile daemon hesaplar, bazı araçlar da bunu katmana bağlar. Unutulan ignore, cache yazısının kuzeni. Sırayı düzelttim, ignore yoktu, context 700 MB'dı. Cache o yükün yanında detaydı.
Yorumlar
Yorumlar (0)
Yorumlar üyelere açık. Üye ol · Giriş yap