Multi-stage build — kichik va xavfsiz image

~15 daq · Docker · Dockerfile — o'z image ingiz

Muammodan boshlaylik

TypeScript (NestJS) ilova. Yasash uchun kerak: TypeScript kompilyatori, barcha devDependencies, manba kodi. Ishlatish uchun esa — faqat kompilyatsiya qilingan dist/ va production paketlar.

Bitta bosqichli Dockerfile:

FROM node:22
WORKDIR /app
COPY package*.json ./
RUN npm ci                     # dev paketlar bilan
COPY . .
RUN npm run build
CMD ["node", "dist/main.js"]

Image — 1.4 GB. Ichida TypeScript, eslint, jest, manba kodi, test fayllari — production da hech biri kerak emas. Har bir ortiqcha paket — ko'proq disk, sekinroq deploy va potentsial xavfsizlik zaifligi.


G'oya: yasash va ishlatish — alohida

# ===== 1-bosqich: yasash =====
FROM node:22-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# ===== 2-bosqich: ishlatish =====
FROM node:22-alpine
WORKDIR /app
ENV NODE_ENV=production
COPY package*.json ./
RUN npm ci --omit=dev
COPY --from=build /app/dist ./dist
USER node
CMD ["node", "dist/main.js"]
  • FROM ... AS build — bosqichga nom
  • Ikkinchi FROM — yangi, toza image boshlanadi
  • COPY --from=build — birinchi bosqichdan faqat kerakli fayllarni olish
  • Faqat oxirgi bosqich yakuniy image bo'ladi. Birinchi bosqich (TypeScript, dev paketlar, manba kod) — tashlab yuboriladi

Natija: 1.4 GB → ~180 MB.


Frontend: Node da yasash, Nginx da berish

React / Vite ilova — production da Node umuman kerak emas, faqat statik fayllar:

FROM node:22-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build                    # → /app/dist

FROM nginx:1.27-alpine
COPY --from=build /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80

Yakuniy image — Nginx va bir necha megabayt HTML/JS/CSS. ~50 MB, Node siz. 36-darsda batafsil.


Go: kompilyatsiya qilingan binar

Kompilyatsiya qilinadigan tillarda farq yanada katta:

FROM golang:1.23 AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o /server .

FROM gcr.io/distroless/static-debian12
COPY --from=build /server /server
USER nonroot
ENTRYPOINT ["/server"]

Go kompilyatori bilan image — ~800 MB. Yakuniy — ~10 MB: faqat bitta binar fayl, shell ham, paket menejeri ham yo'q (17-dars).


Bir nechta bosqich va maqsad (target)

FROM node:22-alpine AS base
WORKDIR /app
COPY package*.json ./

FROM base AS deps
RUN npm ci

FROM deps AS dev
COPY . .
CMD ["npm", "run", "dev"]             # ishlab chiqish — hot reload bilan

FROM deps AS build
COPY . .
RUN npm run build

FROM base AS production
ENV NODE_ENV=production
RUN npm ci --omit=dev
COPY --from=build /app/dist ./dist
USER node
CMD ["node", "dist/main.js"]

Kerakli bosqichni tanlab yasash:

docker build --target dev -t ilova:dev .
docker build --target production -t ilova:1.0 .
docker build -t ilova:1.0 .           # target siz — oxirgi bosqich

Bitta Dockerfile — ishlab chiqish va production uchun. Umumiy qismlar (base, deps) bir marta yoziladi.

BuildKit bosqichlarni parallel yasaydi va yakuniy maqsadga kerak bo'lmagan bosqichlarni umuman o'tkazib yuboradi.


Tashqi image dan nusxalash

--from — nafaqat o'z bosqichingizdan, balki istalgan image dan:

COPY --from=composer:2 /usr/bin/composer /usr/bin/composer

PHP image ga Composer ni o'rnatish o'rniga — tayyor image dan bitta faylni olish (34-dars).


Multi-stage ning foydalari

Bir bosqichli Multi-stage
Hajm Katta (yasash vositalari bilan) Kichik
Xavfsizlik Kompilyatorlar, dev paketlar — hujum yuzasi Faqat kerakli
Manba kodi image da Ha Yo'q (kompilyatsiya qilinadigan tillarda)
Deploy tezligi Sekin (yuklab olish) Tez
Dockerfile soni Dev va prod uchun alohida Bitta, --target bilan

Nima uchun shunday?

Nima uchun yasash vositalarini oxirida RUN rm -rf bilan o'chirib, bitta bosqichda qolish yetmaydi?

14-darsdagi qatlamlar qoidasi: oldingi qatlamda yaratilgan fayl keyingi qatlamda o'chirilsa — u baribir birinchi qatlamda qoladi va image hajmiga kiradi. Faqat bitta ulkan RUN ichida hammasini (o'rnatish, yasash, o'chirish) qilish mumkin — lekin bu keshni butunlay yo'q qiladi va o'qib bo'lmaydigan Dockerfile beradi. Multi-stage bu muammoni tuzilma darajasida hal qiladi: yangi FROM — butunlay toza boshlanish, va siz faqat aniq tanlangan fayllarni o'tkazasiz.


Xulosa

Eslab qoling:

  • Multi-stage: FROM ... AS nom → COPY --from=nom → faqat oxirgi bosqich qoladi
  • Yasash vositalari, dev paketlar, manba kod — yakuniy image ga kirmaydi
  • Node/TS: build bosqichi + npm ci --omit=dev li production bosqichi
  • Frontend: Node da build → Nginx da statik fayllar
  • Go/Rust: binar → distroless; ~10 MB
  • --target — bitta Dockerfile dan dev va prod
  • COPY --from=image — tashqi image dan fayl olish

Keyingi dars

17-dars: Image ni optimallash — alpine, slim, distroless