7-bo‘lim

Ko'p bosqichli qurish

Qurish vositalarini yakuniy obrazdan chiqarib tashlash, COPY --from, scratch va --target.

🕑 15 daqiqa o‘qish 📄 661 so‘z 👁 0 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. Muammo
  2. Yechim: bosqichlarga bo'lish
  3. Farqni o'lchaymiz
  4. Yana ham kichik: scratch
  5. --target bilan bitta bosqichni qurish
  6. Bosqichlar orasida nusxa olish
  7. Kesh bilan birga ishlatish
  8. Xulosa

Ilovani qurish uchun kompilyator, kutubxona sarlavhalari va paket menejeri kerak. Ishlatish uchun esa - faqat tayyor natija.

Ko'p bosqichli qurish shu ikkisini ajratadi.

Muammo #

Kichik C dasturini olamiz - u mingtagacha sonlarni qo'shadi:

Natija
#include <stdio.h>

int main(void) {
    long jami = 0;
    for (long i = 1; i <= 1000000; i++) jami += i;
    printf("Dokon hisoboti: %ld\n", jami);
    return 0;
}

Eng oddiy Dockerfile:

DOCKERFILE
FROM alpine:3.21
RUN apk add --no-cache build-base
WORKDIR /ilova
COPY hisob.c .
RUN gcc -static -O2 -o hisob hisob.c
CMD ["./hisob"]

Ishlaydi:

Terminal
docker run --rm bir-bosqich:1
Natija
Dokon hisoboti: 500000500000

Lekin obrazda kerakmas narsa bor:

Terminal
docker run --rm bir-bosqich:1 which gcc
Natija
/usr/bin/gcc

Ishlab chiqarish serveringizda kompilyator turibdi. U hech qachon kerak bo'lmaydi, lekin obrazni kattalashtiradi va hujum yuzasini kengaytiradi.

Yechim: bosqichlarga bo'lish #

DOCKERFILE
FROM alpine:3.21 AS quruvchi
RUN apk add --no-cache build-base
WORKDIR /ilova
COPY hisob.c .
RUN gcc -static -O2 -o hisob hisob.c

FROM alpine:3.21
WORKDIR /ilova
COPY --from=quruvchi /ilova/hisob .
CMD ["./hisob"]

Ikkita FROM - ikkita bosqich. Birinchisiga AS quruvchi deb nom berdik.

Ikkinchi bosqich toza alpine dan boshlanadi va undan faqat bitta narsa oladi: COPY --from=quruvchi orqali tayyor faylni.

Ko'p bosqichli qurish 1-bosqich: quruvchi FROM alpine AS quruvchi RUN apk add build-base COPY hisob.c RUN gcc -o hisob 353 MB - tashlab yuboriladi 2-bosqich: yakuniy FROM alpine COPY --from=quruvchi CMD ["./hisob"] kompilyator yo'q 12.4 MB - yetkaziladi faqat natija 1-bosqich yakuniy obrazga umuman kirmaydi - u faqat vositachi. Qurish keshida qoladi, lekin registry ga yuborilmaydi va serverga tushmaydi.
Quruvchi bosqich ishini bajarib, ortda qoladi

Natija bir xil:

Terminal
docker run --rm kop-bosqich:1
Natija
Dokon hisoboti: 500000500000

Kompilyator esa yo'q:

Terminal
docker run --rm kop-bosqich:1 which gcc || echo "gcc yo'q - yaxshi"
Natija
gcc yo'q - yaxshi

Farqni o'lchaymiz #

Terminal
for t in bir-bosqich:1 kop-bosqich:1; do
    docker images "$t" --format '{{.Repository}}    {{.Size}}'
done
Natija
bir-bosqich    353MB
kop-bosqich    12.4MB

353 MB va 12.4 MB - 28 barobar farq. Va bu farq har bir serverga, har bir yetkazishda takrorlanadi.

Yana ham kichik: scratch #

scratch - bu obraz emas, balki "asos yo'q" degan maxsus so'z. Statik bog'langan dastur uchun undan boshqa hech narsa kerak emas:

DOCKERFILE
FROM alpine:3.21 AS quruvchi
RUN apk add --no-cache build-base
WORKDIR /ilova
COPY hisob.c .
RUN gcc -static -O2 -o hisob hisob.c

FROM scratch
COPY --from=quruvchi /ilova/hisob /hisob
CMD ["/hisob"]
Terminal
docker run --rm scratch-bosqich:1
docker images scratch-bosqich:1 --format '{{.Repository}}    {{.Size}}'
Natija
Dokon hisoboti: 500000500000
scratch-bosqich    200kB

200 kilobayt. Butun obraz - bitta bajariladigan fayl.

Uchalasini yonma-yon qo'yamiz:

Terminal
for t in bir-bosqich:1 kop-bosqich:1 scratch-bosqich:1; do
    printf '%-18s %s ta qatlam\n' "$t" \
        "$(docker inspect "$t" -f '{{len .RootFS.Layers}}')"
done
Natija
bir-bosqich:1      5 ta qatlam
kop-bosqich:1      3 ta qatlam
scratch-bosqich:1  1 ta qatlam
VariantHajmiQatlamIchida nima bor
Bir bosqichli353 MB5Kompilyator, sarlavhalar, manba kod
Ko'p bosqichli12.4 MB3Alpine + dastur
scratch200 kB1Faqat dastur
scratch da hech narsa yo'q - haqiqatan hech narsa
Terminal
docker run --rm scratch-bosqich:1 ls

Bu ishlamaydi: ls ham, sh ham obrazda yo'q. Demak:

  • docker exec ... sh bilan ichiga kira olmaysiz
  • Xato xabarlari tushunarsiz bo'ladi
  • Ilovangiz libc ga bog'liq bo'lsa, umuman ishga tushmaydi

scratch faqat statik bog'langan dasturlar uchun: Go, Rust, -static bilan qurilgan C.

Oraliq yechim - distroless obrazlari: ular qobiqsiz, lekin libc va sertifikatlarni saqlaydi.

--target bilan bitta bosqichni qurish #

Ba'zan faqat quruvchi bosqich kerak bo'ladi - masalan CI da testlarni o'sha muhitda ishga tushirish uchun:

Terminal
docker run --rm faqat-quruvchi:1 sh -c 'which gcc && ls hisob'
Natija
/usr/bin/gcc
hisob

Bu obraz shunday qurilgan:

Natija
docker build -f Dockerfile.kop --target quruvchi -t faqat-quruvchi:1 .

--target qurishni belgilangan bosqichda to'xtatadi.

Bitta Dockerfile - uch maqsad

Ko'p loyihalarda bitta faylda uchta bosqich bo'ladi:

Natija
FROM node:22-alpine AS quruvchi     # bog'liqliklar + qurish
FROM quruvchi AS sinov              # testlar shu yerda ishlaydi
FROM nginx:alpine AS yakuniy        # faqat natija

CI da:

Natija
docker build --target sinov -t app:sinov .   # testlar
docker build -t app:1.0 .                    # ishlab chiqarish

Sinov bosqichi quruvchidan meros oladi, demak bog'liqliklar qaytadan o'rnatilmaydi.

Bosqichlar orasida nusxa olish #

COPY --from uchta shaklda ishlaydi:

YozuvNimadan oladi
COPY --from=quruvchiNomlangan bosqichdan
COPY --from=0Tartib raqami bo'yicha (0 - birinchi)
COPY --from=nginx:alpineTo'g'ridan-to'g'ri boshqa obrazdan

Uchinchisi foydali: masalan sertifikatlarni tayyor obrazdan olish.

Natija
COPY --from=alpine:3.21 /etc/ssl/certs /etc/ssl/certs
Nomlangan bosqichni afzal ko'ring

--from=0 ishlaydi, lekin bosqich qo'shsangiz raqamlar suriladi va Dockerfile jimgina buziladi.

AS quruvchi deb nom bergan ma'qul - u o'qishga ham oson.

Kesh bilan birga ishlatish #

Ko'p bosqichli qurish 5-bobdagi qoidani bekor qilmaydi. Ikkalasi birga ishlatiladi:

DOCKERFILE
FROM node:22-alpine AS quruvchi
WORKDIR /ilova

# 1) kam o'zgaradigan
COPY package.json package-lock.json ./
RUN npm ci

# 2) tez-tez o'zgaradigan
COPY . .
RUN npm run build

FROM nginx:alpine
COPY --from=quruvchi /ilova/dist /usr/share/nginx/html

Kod o'zgarganda npm ci keshdan olinadi, yakuniy obraz esa har doim toza nginx dan boshlanadi.

Xulosa #

  • Qurish uchun kerak bo'lgan vositalar ishlatish uchun kerak emas - ularni yakuniy obrazdan chiqarib tashlang.
  • Har bir FROM yangi bosqich boshlaydi; AS nom bilan unga nom beriladi.
  • COPY --from=<bosqich> oldingi bosqichdan faqat kerakli faylni oladi.
  • Vositachi bosqich yakuniy obrazga umuman kirmaydi va registry ga yuborilmaydi.
  • O'lchangan farq: 353 MB → 12.4 MB, qatlamlar 5 → 3.
  • scratch - "asos yo'q" degani; statik dastur uchun obraz 200 kB ga tushdi.
  • scratch da qobiq ham, ls ham yo'q - nosozlik topish qiyinlashadi; oraliq yechim distroless.
  • --target qurishni belgilangan bosqichda to'xtatadi - CI da test bosqichini alohida ishga tushirish uchun qulay.
  • COPY --from boshqa obrazdan ham nusxa olishi mumkin.
  • Nomlangan bosqichni ishlating: --from=0 bosqich qo'shilganda jimgina buziladi.
Amaliy topshiriq
  1. Bir bosqichli Dockerfile yozing va obraz hajmini o'lchang.
  2. Uni ikki bosqichga bo'lib, hajmni yana o'lchang.
  3. Yakuniy obrazda kompilyator yo'qligini tekshiring.
  4. scratch variantini yasab, uchala hajmni jadvalga yozing.
  5. scratch obrazida sh ishga tushirib ko'ring va xatoni o'qing.
  6. --target bilan faqat quruvchi bosqichni quring.
  7. Quruvchi bosqichda test ishga tushiradigan bosqich qo'shing.
  8. COPY --from=<obraz> bilan boshqa obrazdan fayl oling.
  9. Bog'liqlik keshini ko'p bosqichli qurish bilan birlashtiring.
  10. O'z loyihangizni ko'p bosqichli qilib qayta yozing va hajmdagi farqni yozib qo'ying.

Xatolik topdingizmi?

Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.