Tarkibga o'tish

08 β€” Dockerfile: o'z image'ingiz

⬅️ Oldingi: 07 β€” Konteyner bilan ishlash Β· 🏠 README Β· Keyingi: 09 β€” Image optimizatsiya va registry ➑️

Bu bobda: tayyor image yetmaganda o'z ilovangizni Dockerfile orqali image'ga paketlashni o'rganamiz β€” FROM/WORKDIR/COPY (va nega ADD emas)/RUN/ENV/ARG/EXPOSE/LABEL/USER direktivalari, har direktiva bir layer ekani va layerlar qanday keshlanishi, CMD va ENTRYPOINT farqi (shell vs exec form), eng muhim optimizatsiya β€” COPY package.json + RUN npm installni COPY . .dan oldin qo'yib build keshini saqlash, .dockerignore, va docker build -t myapp:1.0 . bilan namuna Express "vazifalar" ilovasini real image'ga aylantirib, ishga tushirib ko'rsatamiz.


Muammo: tayyor image yetmaydi

Oldingi bobda biz docker run nginx yoki docker run node kabi tayyor image'larni ishga tushirdik. Ular zo'r β€” lekin ularning ichida sizning ilovangiz yo'q. node image'i faqat Node.js bor toza muhit; sizning server.js faylingizni, package.json'ingizni va bog'liqliklaringizni hech kim u yerga qo'ymagan.

Ikkita yomon yo'l bor:

  1. Har safar qo'lda. Konteynerni ishga tushirib, ichiga docker cp bilan kodni ko'chirish, npm install qilish, keyin ishga tushirish. Konteyner o'chsa β€” hammasi yo'qoladi. Bu 05-bobdagi "qo'lda deploy" og'rig'ining aynan o'zi, faqat konteyner ichida.
  2. Volume bilan ulash. Kodingizni tashqaridan mount qilish β€” lekin u holda image ko'chirib bo'lmaydigan bo'lib qoladi: boshqa serverga olib borsangiz, kod va bog'liqliklar yana qo'lda kerak.

Bizga kerak narsa: kod + bog'liqlik + ishga tushirish buyrug'i hammasi bitta image ichida, qotirilgan holda. Bir marta quramiz β€” istalgan joyda bir xil ishlaydi. Buni qiladigan retsept fayli β€” Dockerfile.

πŸ“Œ Image β€” ilovangizning "muzlatilgan surati": OS qatlami + Node + sizning kodingiz + bog'liqliklar, bari ichida. Dockerfile β€” shu surat qanday quriladi, qadam-baqadam yozilgan retsept. docker build retseptni o'qib image yasaydi; docker run esa image'dan konteyner ishga tushiradi.

Bu bobda biz kitob bo'ylab ishlatadigan namuna ilovani β€” kichik "vazifalar" API'sini (Node.js + Express) β€” to'liq image'ga aylantiramiz.


Birinchi Dockerfile

Dockerfile β€” bu oddiy matn fayl, odatda ilova ildizida joylashadi va aynan Dockerfile deb nomlanadi (kengaytmasiz). Har qatori bitta direktiva: katta harf bilan yoziladigan buyruq (FROM, COPY, RUN, ...) va uning argumenti.

Mana namuna "vazifalar" ilovamiz uchun to'liq, ishlaydigan Dockerfile (har qatorni quyida tushuntiramiz):

# 1) Base image: kichik, rasmiy Node.js LTS (Alpine Linux)
FROM node:lts-alpine

# Image metadatasi (MAINTAINER emas, LABEL)
LABEL org.opencontainers.image.title="Vazifalar API" \
      org.opencontainers.image.description="Oddiy Express ilovasi"

# 2) Ish papkasi konteyner ichida
WORKDIR /app

# 3) AVVAL faqat bog'liqlik manifestini ko'chiramiz (kesh uchun)
COPY package*.json ./

# 4) Bog'liqliklarni aniq, qayta-takrorlanadigan tarzda o'rnatamiz
RUN npm ci --omit=dev

# 5) Endi qolgan kodni ko'chiramiz
COPY . .

# 6) Ilova qaysi portni tinglashini hujjatlashtiramiz
EXPOSE 3000

# 7) root emas, oddiy foydalanuvchi (xavfsizlik)
USER node

# 8) Konteyner ishga tushganda nima bajariladi (exec-form)
CMD ["node", "server.js"]

Bir necha qatorda butun ilova paketlandi. Endi har bir direktivani tartib bilan ochamiz.


Direktivalar: retsept tili

FROM β€” poydevor

FROM node:lts-alpine

Har Dockerfile FROM bilan boshlanadi: qaysi tayyor image ustiga quramiz. Bu base image (poydevor image). Biz noldan OS yozmaymiz β€” Node.js o'rnatilgan tayyor node:lts-alpine'dan boshlaymiz. lts β€” Node'ning uzoq qo'llab-quvvatlanadigan versiyasi, alpine β€” juda kichik Linux (image hajmi keskin kichik bo'ladi; 09-bobda batafsil).

πŸ’‘ Doim aniq tag ishlating (node:lts-alpine, node:22-alpine), node:latest emas. latest ertaga boshqa versiyaga aylanib, kutilmagan buzilishga olib kelishi mumkin.

WORKDIR β€” ish papkasi

WORKDIR /app

Konteyner ichidagi joriy papkani belgilaydi. Bundan keyingi COPY, RUN, CMD shu papkadan ishlaydi. Papka bo'lmasa β€” yaratiladi. Bu RUN mkdir /app && cd /app'dan toza va to'g'ri yo'l.

COPY β€” fayllarni ichkariga ko'chirish (va nega ADD emas)

COPY package*.json ./
COPY . .

COPY <manba> <manzil> β€” build kontekstidagi (sizning loyiha papkangizdagi) fayllarni image ichiga ko'chiradi. COPY . . β€” joriy papkadagi hammasini WORKDIR'ga.

ADD ham bor va o'xshash, lekin uning ikkita "sehrli" qo'shimcha xatti-harakati bor: u URL'dan yuklab olishi va .tar arxivni avtomatik ochishi mumkin. Aynan shu "sehr" tushunarsiz xatolarga sabab bo'ladi.

πŸ“Œ COPY'ni afzal ko'ring, ADD'dan qoching. COPY aynan ko'chiradi, boshqa hech narsa qilmaydi β€” bashorat qilinadigan. ADD'ni faqat haqiqatan lokal .tarni ochish kerak bo'lganda ishlating. Buni Docker rasmiy qo'llanmasi ham tavsiya qiladi.

RUN β€” image qurilayotganda buyruq bajarish

RUN npm ci --omit=dev

RUN β€” image qurilish vaqtida (build) buyruq bajaradi va natijani image'ga qotiradi. Bu yerda biz bog'liqliklarni o'rnatamiz. Biz npm install emas, npm ci ishlatdik: u package-lock.json'ga aniq mos keladigan versiyalarni o'rnatadi β€” qayta-takrorlanadigan (reproducible) va tezroq. --omit=dev esa faqat production bog'liqliklarini o'rnatadi (test/lint paketlari image'ga tushmaydi).

⚠️ RUN bilan CMD/ENTRYPOINTni adashtirmang. RUN β€” build paytida (image yasalayotganda), CMD/ENTRYPOINT β€” konteyner ishga tushganda. RUN npm ci image ichiga bog'liqliklarni o'rnatadi; CMD ["node","server.js"] esa keyin har run'da ilovani ishga tushiradi.

ENV va ARG β€” o'zgaruvchilar

ARG NODE_ENV=production
ENV NODE_ENV=$NODE_ENV
ENV PORT=3000
  • ENV β€” konteyner ichida ishlash vaqtida mavjud bo'ladigan muhit o'zgaruvchisi. Ilova process.env.PORT orqali o'qiy oladi.
  • ARG β€” faqat build vaqtida mavjud bo'ladigan o'zgaruvchi; docker build --build-arg NODE_ENV=production . bilan beriladi. Konteyner ichida ARG ko'rinmaydi.

Soddasi: ARG = qurilish parametri, ENV = ishlash vaqtidagi sozlama.

EXPOSE β€” qaysi portni tinglaydi

EXPOSE 3000

Bu β€” hujjatlashtiruvchi direktiva: "bu ilova 3000-portni tinglaydi" deb belgilaydi. U portni o'zi ochmaydi β€” portni tashqariga ulash uchun baribir docker run -p 8090:3000 kerak (07-bobdan). EXPOSE boshqa odamga (va Docker vositalariga) qaysi port muhimligini ko'rsatadi.

LABEL β€” metadata (MAINTAINER emas)

LABEL org.opencontainers.image.title="Vazifalar API" \
      org.opencontainers.image.source="https://github.com/foydalanuvchi/vazifalar"

LABEL β€” image'ga "kim, nima, qaysi versiya" kabi metadata yopishtiradi (docker inspect bilan ko'rinadi).

⚠️ MAINTAINER eskirgan (deprecated). Eski qo'llanmalarda MAINTAINER Ism <email> ko'rasiz β€” endi ishlatmang. O'rniga: LABEL org.opencontainers.image.authors="Ism <email>".

USER β€” root'dan voz keching

USER node

Standart holatda konteyner ichidagi jarayon root (super-foydalanuvchi) sifatida ishlaydi β€” agar ilovada zaiflik bo'lsa, hujumchi konteynerda root huquqiga ega bo'ladi. USER node β€” node image'ida oldindan tayyor turgan oddiy node foydalanuvchisiga o'tadi.

πŸ“Œ Eng yaxshi amaliyot: konteynerni non-root foydalanuvchi sifatida ishlating. USER'ni COPY/RUN'dan keyin qo'ying (avval fayllarni root sifatida joylab, keyin huquqni pasaytirasiz).

CMD va ENTRYPOINT β€” nima ishga tushadi

Bu eng ko'p chalkashtiriladigan ikki direktiva. Ikkalasi ham konteyner ishga tushganda nima bajarilishini belgilaydi, lekin xulqi farq qiladi.

CMD ["node", "server.js"]

CMD β€” standart buyruq, lekin almashtirilishi mumkin. docker run myapp echo salom desangiz, node server.js o'rniga echo salom ishlaydi.

ENTRYPOINT β€” konteynerning "asosiy dasturi"; docker run'da bergan argumentlar uni almashtirmaydi, balki unga qo'shiladi.

Ikkalasini birga ishlatish β€” eng kuchli idiom. ENTRYPOINT = qat'iy dastur, CMD = unga standart argument:

ENTRYPOINT ["node", "server.js"]
CMD ["--port", "3000"]
  • docker run myapp β†’ node server.js --port 3000
  • docker run myapp --port 8080 β†’ node server.js --port 8080 (CMD almashdi, ENTRYPOINT qoldi)

Bizning oddiy ilovamiz uchun yolg'iz CMD yetarli β€” moslashuvchanroq, chunki kerak bo'lsa docker run myapp sh bilan ichkariga kirib ko'rish oson.

Exec-form vs shell-form

Bu ikki yozuv juda muhim farqqa ega:

# exec-form (TAVSIYA ETILADI) β€” JSON massiv, qo'shtirnoq bilan
CMD ["node", "server.js"]

# shell-form β€” oddiy matn
CMD node server.js
  • Exec-form (["node", "server.js"]) β€” buyruqni to'g'ridan-to'g'ri bajaradi (PID 1 sifatida). Konteynerni to'xtatish signali (SIGTERM, docker stop) ilovaga to'g'ri yetadi β€” toza o'chish.
  • Shell-form (node server.js) β€” /bin/sh -c "node server.js" orqali ishlaydi; signal sh'da qoladi, ilovaga yetmasligi mumkin β€” konteyner sekin, "majburan" o'chadi.

πŸ“Œ Doim exec-form ishlating (CMD ["...", "..."] JSON massiv). Bu signal boshqaruvini to'g'rilaydi va docker stop ilovani toza yopadi.

Quyidagi diagramma CMD va ENTRYPOINT farqini umumlashtiradi:

CMD argumentni almashtiradi, ENTRYPOINT esa argumentni qo'shadi; exec-form va shell-form farqi


Layerlar: image β€” qatlamlar steki

Bu bobning yuragi shu yerda. Dockerfile'dagi har bir direktiva (FROM, COPY, RUN, ...) image'ga bitta yangi layer (qatlam) qo'shadi. Image β€” bu layerlarning ustma-ust qo'yilgan steki (uyumi). Har layer faqat o'zidan oldingi holatga nisbatan o'zgarishni saqlaydi.

Buni varaqlar uyumiga o'xshating: FROM β€” eng pastdagi varaq (Node bor OS), keyin har direktiva ustiga yangi shaffof varaq qo'yadi (WORKDIR papka yasadi, COPY package*.json manifest qo'shdi, RUN npm ci node_modules yasadi...). Image β€” shu varaqlar yig'indisi.

Dockerfile direktivalari pastdan yuqoriga image layerlar stekiga aylanadi: FROM base, WORKDIR, COPY, RUN, COPY, CMD

Eng muhim xususiyat: layerlar keshlanadi va qayta ishlatiladi. docker buildni ikkinchi marta ishga tushirganda, Docker har layer uchun "kirish ma'lumotlari o'zgarmadimi?" deb tekshiradi. O'zgarmagan bo'lsa β€” qaytadan qurmaydi, keshdan oladi (CACHED deb ko'rsatadi). Kesh birinchi o'zgargan layergacha ishlaydi: o'sha layer (va undan keyingi hammasi) qaytadan quriladi.

ℹ️ Ikki tushuncha aralashmasin: layer β€” image quruvchi bloki (qurilish vaqtida hosil bo'ladi va keshlanadi); 07-bobdagi konteyner qatlami esa image'ning yuqorisidagi yozish mumkin bo'lgan vaqtinchalik qatlam (run vaqtida). Image layerlari o'qish-uchun (read-only) va konteynerlar o'rtasida umumiy.


Build kesh va tartib β€” eng muhim optimizatsiya

Layer keshini bilsangiz, undan foyda olishingiz kerak. Sir tartibda: kam o'zgaradigan narsalarni yuqoriga, tez-tez o'zgaradigan narsalarni pastga qo'ying.

Ilovangizda nima tez-tez o'zgaradi? Kod (server.js). Nima kamdan-kam o'zgaradi? Bog'liqliklar (package.json β€” yangi paket qo'shganingizdagina o'zgaradi). Shuning uchun:

COPY package*.json ./     # avval faqat manifest
RUN npm ci --omit=dev     # bog'liqliklarni o'rnat
COPY . .                  # keyin qolgan kod

Endi kodingizni o'zgartirsangiz (server.js'ni tahrirlasangiz), package*.json o'zgarmagani uchun COPY package*.json va og'ir RUN npm ci layerlari keshdan olinadi β€” bog'liqliklar qayta o'rnatilmaydi. Faqat COPY . . qaytadan ishlaydi (bir lahzada).

❌ Noto'g'ri tartib (har kod o'zgarishida bog'liqliklarni qayta o'rnatadi):

# YOMON: kodni avval ko'chirsangiz, har o'zgarishda npm ci buziladi
COPY . .
RUN npm ci --omit=dev

Bunda COPY . . kodingiz har o'zgarganda layer keshini buzadi β€” undan keyingi RUN npm ci ham keshdan tushadi va har safar noldan ishlaydi (sekin: o'nlab soniya, ba'zan daqiqalar).

Quyidagi diagramma ikkala tartibni yonma-yon ko'rsatadi β€” kod o'zgarganda:

Ikki build tartibi yonma-yon: to'g'ri tartibda npm ci keshda qoladi, noto'g'ri tartibda hammasi qayta quriladi

πŸ’‘ Bu β€” Dockerfile'dagi eng muhim optimizatsiya saboq. Faqat ikki qatorni to'g'ri tartiblash bilan build vaqtini daqiqalardan soniyalarga tushirasiz. Xuddi shu naqsh barcha tillarda ishlaydi: Python'da COPY requirements.txt β†’ RUN pip install β†’ COPY . .; Go'da COPY go.mod go.sum β†’ RUN go mod download β†’ COPY . ..

Biz buni haqiqatan tekshirdik: to'g'ri tartibli image'ni qurib, keyin faqat server.js'ni o'zgartirib qayta qurganimizda Docker WORKDIR, COPY package*.json va RUN npm ci layerlarini CACHED deb belgiladi β€” faqat COPY . . qayta ishladi. Noto'g'ri tartibda esa har kod o'zgarishida npm ci qaytadan ishladi.


.dockerignore β€” kontekstga nima tushmasin

docker build -t myapp:1.0 . desangiz, oxiridagi nuqta (.) β€” bu build konteksti: Docker shu papkadagi hamma faylni daemonga jo'natadi. Agar papkangizda node_modules (yuzlab megabayt), .git tarixi yoki .env (sirlaringiz) bo'lsa β€” ular ham jo'natiladi. Bu sekin, og'ir va xavfli.

Yechim β€” .dockerignore fayli (xuddi .gitignore kabi):

node_modules
npm-debug.log
.git
.gitignore
.env
Dockerfile
.dockerignore
*.md
  • node_modules β€” image ichida RUN npm ci bilan toza o'rnatamiz; lokal (ehtimol boshqa OS uchun qurilgan) node_modules'ni ko'chirish noto'g'ri va COPY . . keshini ham buzadi.
  • .git β€” butun versiya tarixi image'ga kerak emas, faqat hajmni shishiradi.
  • .env β€” sirlar (parol, API kalit) image ichiga hech qachon tushmasin. Ularni run vaqtida -e yoki Compose/secrets bilan beriladi.

⚠️ .env'ni image'ga COPY qilmang. Image ko'pincha registry'ga (boshqalar ko'radigan joyga) jo'natiladi β€” ichidagi parol ochiq qoladi. .dockerignore'ga .env'ni qo'shish β€” birinchi himoya.


Image'ni qurish va ishga tushirish

Endi hammasini birga ko'ramiz. Loyiha papkasida Dockerfile, .dockerignore, package.json, package-lock.json va server.js bor.

1) Image'ni quramiz:

docker build -t vazifalar:1.0 .
  • -t vazifalar:1.0 β€” image'ga tag (nom:versiya) beradi.
  • oxiridagi . β€” build konteksti (joriy papka). Docker shu papkadan Dockerfileni topib o'qiydi.

Kutilgan chiqish (qisqartirilgan):

 => [1/5] FROM docker.io/library/node:lts-alpine
 => [2/5] WORKDIR /app
 => [3/5] COPY package*.json ./
 => [4/5] RUN npm ci --omit=dev
 => [5/5] COPY . .
 => exporting to image
 => naming to docker.io/library/vazifalar:1.0

2) Image ro'yxatda turibdimi β€” tekshiramiz:

docker images
REPOSITORY   TAG   IMAGE ID       CREATED         SIZE
vazifalar    1.0   2b3a30198dc9   5 seconds ago   148MB

3) Konteyner sifatida ishga tushiramiz:

docker run -d --name vazifalar-c -p 8090:3000 vazifalar:1.0
  • -d β€” fonda (detached), --name β€” qulay nom, -p 8090:3000 β€” host'ning 8090-portini konteynerning 3000-portiga ulaydi (07-bobdan).

4) Ishlayotganini tekshiramiz:

curl http://localhost:8090/vazifalar
[{"id":1,"matn":"Dockerfile yozish","bajarildi":false}]

Ishladi β€” bizning kod o'z image'imiz ichida konteyner bo'lib aylanmoqda. Yangi vazifa qo'shib ko'ramiz:

curl -X POST http://localhost:8090/vazifalar \
  -H "Content-Type: application/json" \
  -d '{"matn":"Image push qilish"}'
{"id":2,"matn":"Image push qilish","bajarildi":false}

5) Konteyner haqiqatan non-root ekanini tekshiramiz (USER node ishladimi):

docker exec vazifalar-c whoami
node

Root emas, node β€” to'g'ri.

6) Tozalash:

docker rm -f vazifalar-c       # konteynerni o'chiramiz
docker rmi vazifalar:1.0       # image'ni o'chiramiz

βœ… Tekshirildi. Bu bobdagi Dockerfile, namuna Express ilova va build kesh saboq lokal Docker'da (Engine 29.x) haqiqatan ishga tushirildi: docker build muvaffaqiyatli yakunlandi, konteyner node foydalanuvchi sifatida ishladi, curl GET va POST so'rovlariga to'g'ri javob berdi, build keshi to'g'ri tartibda npm cini CACHED qildi, noto'g'ri tartibda esa qayta qurdi. So'ng image va konteyner tozalandi.


Tez ma'lumotnoma

Direktiva Vazifasi Misol
FROM Base image FROM node:lts-alpine
WORKDIR Ish papkasi WORKDIR /app
COPY Fayl ko'chirish (afzal) COPY . .
ADD COPY + URL/tar (kamdan-kam) ADD x.tar.gz /app
RUN Build paytida buyruq RUN npm ci --omit=dev
ENV Run paytidagi o'zgaruvchi ENV PORT=3000
ARG Build paytidagi o'zgaruvchi ARG NODE_ENV
EXPOSE Tinglanadigan port (hujjat) EXPOSE 3000
LABEL Metadata (MAINTAINER emas) LABEL ...title="API"
USER Non-root foydalanuvchi USER node
CMD Standart buyruq (almashadi) CMD ["node","server.js"]
ENTRYPOINT Asosiy dastur (qo'shiladi) ENTRYPOINT ["node","server.js"]

08-bob mashqlari

Ko'pchilik mashq lokal Docker o'rnatilgan kompyuterda bajariladi. Har mashqdan keyin yaratgan konteyner/image'laringizni docker rm -f va docker rmi bilan tozalab boring.

Oson

  1. Bo'sh papkada bitta index.js (console.log("Salom Docker")) yarating va shuni ishga tushiradigan eng kichik Dockerfile yozing (FROM node:lts-alpine, WORKDIR, COPY, CMD). docker build -t salom:1.0 . qiling.

  2. docker images bilan yaratgan image'ingizni toping. Uning SIZE ustunini yozib oling.

  3. salom:1.0 image'ini docker run salom:1.0 bilan ishga tushiring va Salom Docker chiqishini ko'ring.

  4. Dockerfile'da MAINTAINER qatorini ko'rdingiz deylik. Uni qaysi direktivaga almashtirasiz? Bitta qatorda yozing.

O'rta

  1. Yuqoridagi "vazifalar" Dockerfile'ida CMD ["node", "server.js"]ni shell-formga (CMD node server.js) o'zgartiring. Build qilib, konteynerni docker stop bilan to'xtating β€” qaysi form docker stop'da tezroq to'xtaydi? Nega?
Yechim

Exec-form (CMD ["node", "server.js"]) tezroq va toza to'xtaydi. Shell-form'da buyruq /bin/sh -c "node server.js" orqali ishlaydi: docker stop jo'natgan SIGTERM signali sh jarayoniga boradi, lekin Node'ga uzatilmasligi mumkin. Shuning uchun konteyner SIGTERM'ga javob bermay, ~10 soniyadan keyin SIGKILL bilan majburan o'chadi. Exec-form'da Node to'g'ridan-to'g'ri PID 1 bo'ladi, signalni o'zi oladi va darrov toza yopiladi. Doim exec-form (JSON massiv) ishlating.

  1. EXPOSE 3000 bo'lgan image'ni docker run bilan -psiz ishga tushiring. Brauzerdan/curl'dan unga ulanib ko'ring. Nima bo'ladi va nega?
Yechim

Ulanib bo'lmaydi. EXPOSE faqat hujjatlashtiruvchi direktiva β€” u portni tashqariga ochmaydi. Portni host'ga ulash uchun baribir docker run -p 8090:3000 ... kerak. EXPOSE shunchaki "bu ilova 3000-portni tinglaydi" deb belgilaydi, boshqa hech narsa qilmaydi.

  1. .dockerignore faylini namuna ilovaga qo'shing (node_modules, .git, .env). Build qiling. Keyin .dockerignore'ni o'chirib qayta build qiling β€” build kontekstining hajmi (transferring context) qanchaga oshdi?

  2. ENV PORT=4000ni Dockerfile'ga qo'shing va ilovani process.env.PORT'ni o'qiydigan qiling. Build qilib, -p 8090:4000 bilan ishga tushiring. Endi konteyner qaysi portni tinglaydi?

Yechim

Dockerfile'ga ENV PORT=4000 qo'shilgach, ilova (process.env.PORT || 3000 mantig'i bilan) 4000-portni tinglaydi. Shuning uchun EXPOSE'ni ham EXPOSE 4000ga o'zgartirib, docker run -p 8090:4000 vazifalar:1.0 bilan ishga tushirasiz β€” host'ning 8090-porti konteynerning 4000-portiga ulanadi. ENV β€” run vaqtidagi sozlama bo'lgani uchun ilova uni process.env orqali ko'radi.

Qiyin

  1. Namuna "vazifalar" ilovasiga to'liq Dockerfile yozing: FROM node:lts-alpine, WORKDIR /app, COPY package*.json ./, RUN npm ci --omit=dev, COPY . ., EXPOSE 3000, USER node, CMD ["node","server.js"]. Build qiling, -p 8090:3000 bilan ishga tushiring, curl http://localhost:8090/vazifalar bilan tasdiqlang.
Yechim

Dockerfile:

FROM node:lts-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
EXPOSE 3000
USER node
CMD ["node", "server.js"]

Build va run:

docker build -t vazifalar:1.0 .
docker run -d --name vazifalar-c -p 8090:3000 vazifalar:1.0
curl http://localhost:8090/vazifalar
# [{"id":1,"matn":"Dockerfile yozish","bajarildi":false}]
docker rm -f vazifalar-c && docker rmi vazifalar:1.0

COPY package*.json ./ + RUN npm cini COPY . .dan oldin qo'ygani uchun keyinchalik kod o'zgarsa ham bog'liqlik keshi saqlanadi.

  1. Build kesh isboti. 9-mashqdagi image'ni quring. Endi faqat server.js'ni o'zgartiring (bitta izoh qo'shing) va docker buildni qayta ishga tushiring. Chiqishda qaysi qadamlar CACHED deb belgilanadi? Endi package.json'ga yangi paket qo'shib qayta quring β€” endi nima o'zgaradi?
Yechim

Faqat server.js o'zgarganda WORKDIR, COPY package*.json ./ va og'ir RUN npm ci qadamlari CACHED bo'ladi β€” faqat COPY . . (va undan keyingilar) qaytadan ishlaydi:

 => CACHED [2/5] WORKDIR /app
 => CACHED [3/5] COPY package*.json ./
 => CACHED [4/5] RUN npm ci --omit=dev
 => [5/5] COPY . .

package.json o'zgarganda esa COPY package*.json ./ keshi buziladi β€” undan keyingi RUN npm ci ham qaytadan ishlaydi (bog'liqliklar yangidan o'rnatiladi). Saboq: kam o'zgaradigan bog'liqliklarni yuqoriga, tez o'zgaradigan kodni pastga qo'ying.

  1. Noto'g'ri tartib bilan taqqoslang. COPY . .ni RUN npm cidan oldin qo'yilgan ikkinchi Dockerfile yozing. Birini, keyin ikkinchisini quring; keyin ikkalasida ham faqat kodni o'zgartirib qayta quring. Qaysi biri har safar npm cini qayta ishlatadi? Vaqtni o'lchang.
Yechim

Noto'g'ri tartibli Dockerfile:

FROM node:lts-alpine
WORKDIR /app
COPY . .
RUN npm ci --omit=dev
CMD ["node", "server.js"]

Bunda kod har o'zgarganda COPY . . keshi buziladi (kod kontekstning bir qismi), shuning uchun undan keyingi RUN npm ci har safar qaytadan ishlaydi β€” sekin. To'g'ri tartibda esa npm ci CACHED qoladi. time docker build ... bilan o'lchasangiz, to'g'ri tartib soniyalar, noto'g'ri tartib o'nlab soniyada tugaydi.

  1. CMD va ENTRYPOINTni birga ishlatadigan Dockerfile yozing: ENTRYPOINT ["node", "server.js"] va CMD ["--port", "3000"]. docker run myapp va docker run myapp --port 8080 ikki holatda konteyner ichida qanday to'liq buyruq ishga tushadi?
Yechim
  • docker run myapp β†’ node server.js --port 3000 (CMD standart argument sifatida ishlatildi).
  • docker run myapp --port 8080 β†’ node server.js --port 8080 (run'dagi argument CMDni almashtirdi, ENTRYPOINT o'zgarmadi).

ENTRYPOINT β€” qat'iy "asosiy dastur", CMD β€” almashtiriladigan standart argument. Bu naqsh ilovani har doim node server.js orqali ishga tushishini kafolatlaydi, lekin argumentni moslashuvchan qoldiradi.

  1. docker build chiqishidagi Sending build context/transferring context qatorini toping. Loyihangizga 200MB lik fayl qo'shsangiz (.dockerignore'ga kiritmasdan), bu raqam qanday o'zgaradi? Nega .dockerignore muhim?
Yechim

Build konteksti β€” docker build .dagi . papkadagi hamma fayl (.dockerignore'da istisno qilinmaganlari). 200MB lik fayl qo'shilsa, u ham daemonga jo'natiladi β€” kontekst hajmi ~200MB ga oshadi, build sekinlashadi va u fayl kerak bo'lmasa ham. .dockerignore bilan keraksiz narsalarni (node_modules, .git, katta fayllar) chiqarib tashlash kontekstni kichik, build'ni tez va image'ni xavfsiz (.env tushmaydi) qiladi.


⬅️ Oldingi: 07 β€” Konteyner bilan ishlash Β· 🏠 README Β· Keyingi: 09 β€” Image optimizatsiya va registry ➑️