بناء تطبيق Node
الهدف لخدمة Node هو صورة نهائية تحمل مخرجاتك المبنية وnode_modules الإنتاجية، ولا شيء غير ذلك. لا اعتماديات تطوير، ولا مصدرًا لا تشحنه، ولا ذاكرة مدير حزم. يوصلك بناء ذو ثلاث مراحل إلى هناك: حُلّ اعتماديات الإنتاج مرة، وابنِ بمجموعة الاعتماديات الكاملة بالتوازي، ثم جمّع مرحلة تشغيل نظيفة من الاثنين.
يبني هذا على ghcr.io/quenchworks/images/node:24. وهناك أيضًا ghcr.io/quenchworks/images/pnpm:10، وهو أساس node مفعّل فيه pnpm أصلًا عبر corepack، ما يوفّر خطوة corepack enable في متغيّر pnpm.
ملف Dockerfile المتعدد المراحل
# Build stage: pnpm is already enabled on the pnpm base image.FROM ghcr.io/quenchworks/images/pnpm:10 AS buildWORKDIR /app
# Resolve production dependencies on their own so the runtime stage can# copy just these. A cache mount keeps the pnpm store warm across builds.COPY package.json pnpm-lock.yaml ./RUN ["pnpm", "install", "--prod", "--frozen-lockfile"]RUN ["cp", "-r", "node_modules", "/prod_modules"]
# Now install everything (incl. dev deps) and build.RUN ["pnpm", "install", "--frozen-lockfile"]COPY . .RUN ["pnpm", "run", "build"]
# Runtime stage: a slim node base, nonroot, with only prod deps + dist.FROM ghcr.io/quenchworks/images/node:24 AS runtimeWORKDIR /appENV NODE_ENV=productionCOPY --from=build /prod_modules ./node_modulesCOPY --from=build /app/dist ./distCOPY --from=build /app/package.json ./package.jsonUSER 1001EXPOSE 3000CMD ["node", "dist/server.js"]FROM ghcr.io/quenchworks/images/node:24 AS buildWORKDIR /app
# Production dependencies only, into their own copy.COPY package.json package-lock.json ./RUN ["npm", "ci", "--omit=dev"]RUN ["cp", "-r", "node_modules", "/prod_modules"]
# Full install (incl. dev deps) and build.RUN ["npm", "ci"]COPY . .RUN ["npm", "run", "build"]
FROM ghcr.io/quenchworks/images/node:24 AS runtimeWORKDIR /appENV NODE_ENV=productionCOPY --from=build /prod_modules ./node_modulesCOPY --from=build /app/dist ./distCOPY --from=build /app/package.json ./package.jsonUSER 1001EXPOSE 3000CMD ["node", "dist/server.js"]# Yarn (Classic) is preinstalled on the yarn base image.FROM ghcr.io/quenchworks/images/yarn:1 AS buildWORKDIR /app
# Production dependencies only.COPY package.json yarn.lock ./RUN ["yarn", "install", "--frozen-lockfile", "--production"]RUN ["cp", "-r", "node_modules", "/prod_modules"]
# Full install (incl. dev deps) and build.RUN ["yarn", "install", "--frozen-lockfile"]COPY . .RUN ["yarn", "build"]
FROM ghcr.io/quenchworks/images/node:24 AS runtimeWORKDIR /appENV NODE_ENV=productionCOPY --from=build /prod_modules ./node_modulesCOPY --from=build /app/dist ./distCOPY --from=build /app/package.json ./package.jsonUSER 1001EXPOSE 3000CMD ["node", "dist/server.js"]ماذا تفعل كل مرحلة
- اعتماديات الإنتاج. ثبّت اعتماديات الإنتاج فقط، ثم انسخها إلى مسار ثابت (
/prod_modules). هذه هي المجموعة التي تحتاجها الصورة النهائية، محفوظة بمعزل عن اعتماديات التطوير التي يجلبها البناء لاحقًا. - البناء. ثبّت مجموعة الاعتماديات الكاملة، وانسخ المصدر، وشغّل البناء. تحمل هذه المرحلة المُجمِّع واعتماديات التطوير والمصدر، ولا يُشحن أيٌّ منها.
- التشغيل. ابدأ من جديد من
node:24، وانسخnode_modulesالإنتاجية وdist/المبني، وانزل إلىuid 1001، وابدأ. تبقى سلسلة الأدوات خلفك.
التالي
- تشغّل واجهة أمامية ساكنة بدلًا من خادم Node؟ ابنِ بـ
node، ثم اخدم الأصول المبنية منnginx، أو اشحن ملفًا ثنائيًا مكتفيًا ذاتيًا إلىstatic. - ثبّت بالبصمة الرقمية متى استقرّيت على أساس، ليجري كل بناء بالضبط على ما جرى فحصه وتوقيعه.