3-bo'lim mashqlari — Node.js API uchun Dockerfile
Vazifa
Kichik TypeScript + Express API ni noldan konteynerlashtiramiz va bosqichma-bosqich yaxshilaymiz: avval "ishlaydigan", keyin "tez build", keyin "kichik va xavfsiz".
Tayyorgarlik: ilova
mkdir kitoblar-api && cd kitoblar-api
npm init -y
npm install express
npm install -D typescript @types/express @types/node tsx
npx tsc --init --outDir dist --rootDir src --module commonjs --target es2022 --esModuleInterop
package.json ga:
"scripts": {
"dev": "tsx watch src/index.ts",
"build": "tsc",
"start": "node dist/index.js"
}
src/index.ts:
import express from 'express';
const app = express();
const PORT = Number(process.env.PORT ?? 3000);
const kitoblar = [
{ id: 1, nom: "O'tkan kunlar", muallif: 'Abdulla Qodiriy' },
{ id: 2, nom: 'Sariq devni minib', muallif: "Xudoyberdi To'xtaboyev" },
];
app.get('/health', (_req, res) => res.json({ holat: 'ok' }));
app.get('/kitoblar', (_req, res) => res.json(kitoblar));
app.get('/versiya', (_req, res) => res.json({ versiya: process.env.APP_VERSION ?? 'dev' }));
const server = app.listen(PORT, '0.0.0.0', () => console.log(`API ${PORT}-portda`));
// Graceful shutdown (13-dars)
process.on('SIGTERM', () => {
console.log('SIGTERM — yopilmoqda');
server.close(() => process.exit(0));
});
1-mashq: Ishlaydigan Dockerfile ⭐
Eng oddiy Dockerfile yozing: image yasalsin va curl localhost:3000/kitoblar ishlasin. Hajmini yozib qo'ying.
Yechim
FROM node:22
WORKDIR /app
COPY . .
RUN npm install
RUN npm run build
CMD ["npm", "start"]
docker build -t kitoblar:v1 .
docker run -d --name k1 -p 3000:3000 kitoblar:v1
curl localhost:3000/kitoblar
docker images kitoblar:v1 # ~1.2 GB
Ishlaydi. Endi muammolarini topamiz.
2-mashq: Muammolarni toping ⭐⭐
1-mashqdagi Dockerfile da kamida 5 ta muammoni toping (14-17 darslar).
Javob
.dockerignoreyo'q — kompyuterdaginode_modulesva.gitkontekstga va image ga tushadiCOPY . .→RUN npm install— har kod o'zgarishida paketlar qaytadan o'rnatiladi (14-dars)npm install,npm ciemas — lock fayl kafolatlanmaydi- Dev paketlar (typescript, tsx) va manba kodi production image da (16-dars)
node:22— to'liq Debian, 1 GB+ (17-dars)CMD ["npm", "start"]— PID 1 —npm, u SIGTERM ni Node ga ishonchli uzatmaydi;docker stop10 soniya kutadi (13-dars)- Root sifatida ishlaydi (13-dars)
Tekshirish — 6-band:
time docker stop k1 # ~10 soniya — "SIGTERM — yopilmoqda" logda yo'q
docker logs k1
3-mashq: To'g'ri Dockerfile ⭐⭐⭐
Barcha muammolarni tuzating:
.dockerignore- Multi-stage: build bosqichi va production bosqichi
- Kesh uchun to'g'ri tartib
alpine,npm ci --omit=devUSER node,EXPOSE,HEALTHCHECK- Exec shaklidagi
CMD—nodeto'g'ridan-to'g'ri APP_VERSIONbuild argumenti orqali (/versiyaendpointi uni qaytarsin)
.dockerignore
node_modules
dist
.git
.env*
!.env.example
Dockerfile*
.dockerignore
*.log
Dockerfile
# syntax=docker/dockerfile:1
# ===== build =====
FROM node:22-alpine AS build
WORKDIR /app
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
# ===== production =====
FROM node:22-alpine
ARG APP_VERSION=dev
ENV NODE_ENV=production \
PORT=3000 \
APP_VERSION=${APP_VERSION}
WORKDIR /app
COPY --chown=node:node package.json package-lock.json ./
RUN --mount=type=cache,target=/root/.npm npm ci --omit=dev
COPY --chown=node:node --from=build /app/dist ./dist
USER node
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget -qO- http://localhost:3000/health || exit 1
CMD ["node", "dist/index.js"]
Tekshirish
docker rm -f k1
docker build --build-arg APP_VERSION=1.0.0 -t kitoblar:1.0.0 .
docker images kitoblar # v1 ~1.2 GB → 1.0.0 ~150-170 MB
docker run -d --name k2 -p 3000:3000 kitoblar:1.0.0
curl localhost:3000/versiya # {"versiya":"1.0.0"}
docker ps # bir oz kutib: (healthy)
docker exec k2 whoami # node
time docker stop k2 # ~1 soniyadan kam
docker logs k2 # "SIGTERM — yopilmoqda"
Kesh tekshiruvi — src/index.ts da bitta qatorni o'zgartirib qayta build:
=> CACHED [build 3/6] RUN npm ci
=> [build 5/6] COPY src ./src
=> [build 6/6] RUN npm run build
=> CACHED [stage-1 4/5] RUN npm ci --omit=dev
npm ci ikkala bosqichda ham keshdan.
4-mashq: Ishlab chiqish bosqichi ⭐⭐⭐
Xuddi shu Dockerfile ga dev bosqichini qo'shing: npm run dev (tsx watch) bilan ishlasin. --target dev bilan yasab, kod papkasini ulab ishga tushiring — fayl o'zgarganda server avtomatik qayta yuklansin.
Yechim
Dockerfile ga (build bosqichidan keyin):
# ===== dev =====
FROM node:22-alpine AS dev
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
CMD ["npm", "run", "dev"]
docker build --target dev -t kitoblar:dev .
docker run --rm -it -p 3000:3000 \
-v "$(pwd)/src":/app/src \
kitoblar:dev
src/index.ts ni o'zgartiring — terminalda tsx serverni qayta ishga tushiradi. Bu — bind mount (20-dars) bilan ishlab chiqish; 30-darsda Compose bilan qulayroq qilamiz.
Eslatma: dev bosqichi production bosqichidan oldin bo'lishi kerak, aks holda --target siz build oxirgi bosqich — dev ni yasaydi.
O'zingizni tekshiring
RUNvaCMDfarqi?- Nima uchun
package.jsonkod dan oldin ko'chiriladi? .dockerignoresiznode_modulesqanday muammolar keltiradi?- Multi-stage image hajmini qanday kamaytiradi?
- Nima uchun
CMD ["npm", "start"]emas,CMD ["node", "..."]? ARGvaENVfarqi?
Keyingi bo'lim
4-bo'lim: Ma'lumotlarni saqlash — volume, bind mount va konteynerdagi bazalar.