← Vissza a bloghoz Bevált gyakorlatok

Image-méret optimalizálás: a kisebb image gyorsabb és biztonságosabb

Egy 1 GB-os image lassan utazik és nagyobb a támadási felülete. Megnézzük, hogyan zsugorítsd az image-eidet alapkép-választással, rétegtisztítással és multi-stage buildel.

Az image-méret könnyen olyan dolognak tűnik, amivel nem érdemes foglalkozni — elvégre a tárhely olcsó, és úgyis fut a konténer. A valóságban azonban a túlméretezett image-ek minden nap apró adót szednek tőled: lassabb deploy, lassabb skálázódás, magasabb tárhelyköltség és nagyobb támadási felület. Egy 1 GB-os image lassan utazik a hálózaton, és sokkal több olyan szoftvert tartalmaz, amiben sebezhetőség rejtőzhet. Ebben a cikkben végigvesszük a legfontosabb technikákat, amelyekkel reálisan tört részére zsugoríthatod az image-eidet — anélkül, hogy bármi lényegeset elveszítenél.

Miért számít a méret?

Mielőtt a hogyanba belevágunk, nézzük meg, miért éri meg az erőfeszítés:

  • Gyorsabb pull és push: minden deploy, CI futás és skálázás egy image letöltésével kezdődik. A kisebb image gyorsabban érkezik.
  • Gyorsabb indulás: a kisebb image hamarabb áll készen, ami autoscaling és gyors helyreállítás esetén kulcsfontosságú.
  • Kevesebb tárhely: a registry-ben és a node-okon is kevesebb helyet foglal, ami pénzben is mérhető.
  • Kisebb támadási felület: kevesebb csomag = kevesebb potenciális sebezhetőség. Egy fordító, egy shell vagy egy felesleges könyvtár mind kockázat.

💡 Tipp: A méretoptimalizálás és a biztonság gyakran kéz a kézben jár. Amikor kidobsz egy felesleges build eszközt, egyszerre csökkented a méretet és a támadási felületet is.

1. Válassz okosan alapképet

Az alapkép a legnagyobb egyetlen tényező az image méretében. Ugyanaz az alkalmazás drámaian eltérő méretű lehet a választott alapkép szerint:

AlapképTipikus méret
ubuntu:24.04~78 MB
python:3.12~1 GB
python:3.12-slim~130 MB
python:3.12-alpine~50 MB
node:22~1,1 GB
node:22-slim~240 MB
gcr.io/distroless/static~2 MB

Három fő irány közül választhatsz:

  • slim: a teljes image lecsupaszított változata, glibc-vel. Jó kompromisszum, kevés meglepetéssel.
  • alpine: rendkívül kicsi, de musl libc-t használ, ami egyes natív kiterjesztéseknél (pl. Python wheel-ek) kompatibilitási vagy fordítási gondot okozhat.
  • distroless: csak a futtatáshoz szükséges minimum, shell és csomagkezelő nélkül. A legkisebb és legbiztonságosabb, de a hibakeresés benne nehezebb, mert nincs shell.

Nincs egyetlen helyes válasz: az alpine kicsi, de néha extra fordítási munkát igényel; a slim biztonságos alapértelmezés; a distroless a végső optimalizáláshoz ideális, főleg fordított nyelveknél.

2. Távolítsd el a build függőségeket

Sok image azért hízik fel, mert a fordításhoz szükséges fejlesztői csomagok (build-essential, gcc, fejlécfájlok) benne maradnak. Ha mindenképpen egyetlen fázisban dolgozol, akkor a build függőségeket telepítsd, használd, majd távolítsd el — mindezt egyetlen rétegben:

RUN apt-get update && \
    apt-get install -y --no-install-recommends build-essential && \
    pip install --no-cache-dir -r requirements.txt && \
    apt-get purge -y --auto-remove build-essential && \
    rm -rf /var/lib/apt/lists/*

A --no-cache-dir a pip esetében megakadályozza a letöltött csomagok cache-elését, a purge --auto-remove pedig eltakarítja a build eszközöket a telepítés után.

3. Takaríts ugyanabban a rétegben

Ez az egyik leggyakoribb hiba. A Docker rétegei “csak hozzáadó” jellegűek: ha egy korábbi rétegbe bekerült egy fájl, akkor egy későbbi rétegben való törlése nem teszi kisebbé az image-et — a fájl ott marad a korábbi rétegben. Ezért mindig ugyanabban a RUN utasításban telepíts és takaríts:

# Rossz: a cache külön rétegben marad, az image nem kisebb
RUN apt-get update && apt-get install -y curl
RUN rm -rf /var/lib/apt/lists/*

# Jó: minden egy rétegben
RUN apt-get update && apt-get install -y --no-install-recommends curl && \
    rm -rf /var/lib/apt/lists/*

4. Használj .dockerignore fájlt

A build kontextus minden fájlja potenciálisan az image-be kerülhet egy COPY . . során. A .dockerignore megakadályozza a felesleges fájlok bemásolását:

node_modules
.git
.env
*.log
coverage
dist
tmp

Ez nem csak méretet spórol, hanem gyorsítja a build kontextus feltöltését a Docker daemonhoz, és csökkenti a véletlen titokszivárgás kockázatát is.

5. Aknázd ki a multi-stage buildet

A legnagyobb dobás szinte minden esetben a multi-stage build. A build eszközöket egy külön fázisban tartod, és csak a kész artefaktumot viszed át a minimális futtató image-be:

FROM golang:1.22 AS builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 go build -o app .

FROM gcr.io/distroless/static-debian12
COPY --from=builder /app/app /app
USER nonroot:nonroot
ENTRYPOINT ["/app"]

Ezzel egy 800 MB-os Go image akár 15 MB-ra is csökkenhet. A részleteket a Multi-stage build cikkben fejtem ki bővebben, sok példával.

6. Vizsgáld meg, mi van az image-edben

Optimalizálni csak azt tudod, amit látsz. Először nézd meg a méreteket:

docker images
docker history myapp:latest

A docker history rétegenként megmutatja, melyik utasítás mennyi helyet foglal — így rögtön kiderül, hol hízik az image. Még részletesebb betekintést ad a dive nevű eszköz, amely interaktívan, rétegenként mutatja a fájlokat és a pazarlást:

dive myapp:latest

A dive-val pontosan látod, melyik réteg mekkora, mennyi a redundáns vagy felesleges tartalom, és milyen “wasted space” keletkezett. Ez aranyat ér, ha egy konkrét image-et akarsz lefogyasztani.

⚠️ Figyelem: Ne ess túlzásba az alpine-nal csak a méret kedvéért. Ha egy natív függőség folyamatosan fordítási hibákat dob musl libc alatt, gyakran jobban jársz egy -slim image-dzsel — a néhány tíz megabájt megéri a nyugalmat.

Egy reális optimalizálási folyamat

Ha egy meglévő, túlméretezett image-et akarsz lefogyasztani, érdemes ezt a sorrendet követni:

  1. Futtasd a docker history vagy dive parancsot, és azonosítsd a legnagyobb rétegeket.
  2. Cseréld le az alapképet egy -slim vagy alpine változatra, és teszteld, hogy minden működik-e.
  3. Vezess be .dockerignore fájlt.
  4. Vond össze a RUN utasításokat, és takaríts a cache-ek után ugyanabban a rétegben.
  5. Vezesd be a multi-stage buildet, és tedd a build eszközöket külön fázisba.
  6. Mérj újra, és ismételd, amíg elégedett nem vagy.

Összefoglalás

A kisebb image gyorsabban utazik, hamarabb indul, kevesebb helyet foglal és biztonságosabb. A legfontosabb fogások: válassz okosan alapképet (slim, alpine vagy distroless), távolítsd el a build függőségeket, takaríts ugyanabban a rétegben, használj .dockerignore-t, és aknázd ki a multi-stage buildet. A docker history és a dive segít megtalálni, hol pazarolsz helyet.

Vedd elő a legnagyobb image-edet még ma, futtasd rajta a dive parancsot, és kezdj el faragni! Ha még az alapoknál tartasz, nézd meg, hogyan működik a Docker, vagy frissítsd fel a tudásod a Dockerfile írása lépésről lépésre cikkel.