بناء مراحل متعددة لـ Docker تقليل حجم الصورة دون تدمير عملية الإطلاق

تحديث في

مقدمة عملية لبناء صناديق Docker متعددة المراحل — حجم الصناديق قبل وبعد الفعل، أمثلة على ملفات Docker لـ Node.js وPython، والمخاطر التي تُواجهها كل فريق على الأقل مرة واحدة.

بناء مراحل متعددة لـ Docker: تقليل حجم الصورة دون تدمير عملية الإطلاق  1
إعلان · حذف؟

صورة Node.js الخاصة بك هي 1.1 جيجابايت. لقد أضفت .dockerignore، وقمت بحذف الاعتماديات المطورة، وحاولت node:slim — لم يتحرك كثيرًا. الحل الفعلي هو بنية متعددة المراحل. إذا لم تقم بالتحويل بعد، فإنك تنقل مُترجم TypeScript إلى الإنتاج.

تم تطوير بنية متعددة المراحل في Docker منذ الإصدار 17.05 (2017). فهي مُستخدمة بشكل غير كافٍ. إليك توضيحًا حقيقيًا: ما تتغيره، مدى التغيير، والثلاثة أخطاء التي تقع فيها الفرق عند الانتقال الأولي.

مشكلة المرحلة الواحدة

تبدأ ملفات Docker غالبًا على النحو التالي:

FROM node:20

WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

EXPOSE 3000
CMD ["node", "dist/server.js"]

بناء ذلك وتحقق مع docker images: ~1.1 جيجابايت. تنقل صورة Node 20 الكاملة مع npm، وسلسلة أدوات TypeScript، وكل الاعتمادات المطورة، وملف المصدر الكامل. لا يُستخدم أي من ذلك في الإنتاج — فقط يحتاج التطبيق إلى المخرج المُجمّع وعدد قليل من المكتبات التشغيلية. dist/ النتيجة.

بنية متعددة المراحل: الحل

يبدأ كل FROM تعليمات بمرحلة جديدة مع نظام ملفات نظيف. اجعل أسماء المراحل باستخدام AS، ثم استخدم COPY --from=stagename لإحضار ملفات محددة إلى المرحلة التالية. لا تُدخل المراحل الوسيطة في الصورة النهائية — فهي أثر بناء، تُحذف بعد إكمال COPY .

هناك نفس التطبيق كبنية متعددة المراحل مناسبة:

# ---- Build stage ----
FROM node:20 AS builder

WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build


# ---- Runtime stage ----
FROM node:20-alpine AS runtime

WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY --from=builder /app/dist ./dist

EXPOSE 3000
CMD ["node", "dist/server.js"]

السطر المهم: COPY --from=builder /app/dist ./dist. يُسحب بدلاً من كتابة منطق القسمة بنفسك، استخدم النتيجة المُجمّعة من المرحلة المُبنية إلى صورة التشغيل المبنية على Alpine. لا يُلامس مُترجم TypeScript، أو الملفات المصدرية، أو الاعتمادات المطورة في الطبقة النهائية.

النتيجة: ~160 ميجابايت بدلاً من 1.1 جيجابايت. هذا يمثل تقليلًا تقريبيًا بنسبة 85% لتطبيق Node معتاد — وهو نفس المُنتج المُجمّع، ولكن بدون التكوين المحيط به.

إضافة مرحلة اختبار

يمكنك إضافة مرحلة اختبار بين بناء وتشغيل تُجرى فيها مجموعات الاختبار. إذا فشلت الاختبارات، يُوقف البناء قبل إنشاء صورة التشغيل. إذا نجح الاختبار، يُتجاهل مرحلة الاختبار بالكامل عند بناء الصورة للإنتاج.

FROM node:20 AS builder

WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build


FROM builder AS tester

RUN npm test


FROM node:20-alpine AS runtime

WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY --from=builder /app/dist ./dist

EXPOSE 3000
CMD ["node", "dist/server.js"]

في بيئة CI توجه إلى مرحلة الاختبار بشكل محدد: docker build --target tester .. عند صور الإنتاج، تُبنى بدون هدف، ويقوم Docker بتشغيل جميع المراحل تباعًا، وينتهي عند المرحلة الأخيرة FROM. تُجرى مرحلة الاختبار ولكن نظام الملفات يُحذف — تُستخدم الاختبارات كمُدخل، وليس كمحتوى.

اللغة بايثون: نفس الفكرة، تنفيذ مختلف

تتبع بنية متعددة المراحل في بايثون نفس النمط. الفرق الرئيسي: تُثبت مكتبات بايثون تحت /root/.local عند استخدام --user، لذا يتم نسخ هذا المجلد إلى صورة التشغيل المُختصرة.

FROM python:3.12 AS builder

WORKDIR /app
COPY requirements.txt .
RUN pip install --user -r requirements.txt
COPY . .


FROM python:3.12-slim AS runtime

WORKDIR /app
COPY --from=builder /root/.local /root/.local
COPY --from=builder /app .

ENV PATH=/root/.local/bin:$PATH

CMD ["python", "main.py"]

python:3.12 صورة القاعدة: ~1 جيجابايت. python:3.12-slim مع الاعتماديات المثبتة فقط: ~180-250 ميجابايت حسب ما هو موجود في requirements.txt. تأتي الملفات المُجمّعة مجانًا لأنها توجد بجانب المصدر. .pyc الملفات

ثلاثة أخطاء تقع فيها الجميع على الأقل مرة واحدة

1. نسخ الملفات الخاطئة

الخطأ الأكثر شيوعًا: أنك COPY --from=builder /app ./ بدلاً من COPY --from=builder /app/dist ./dist. فقط نسخت كل شيء — الملفات المصدرية، مجموعات الاختبار، node_modules، وما إلى ذلك — إلى صورة التشغيل "الصغيرة". أصبح الآن أكبر من النسخة ذات المرحلة الواحدة.

كن واضحًا بشأن ما تنسخه. نسخ فقط المجلد أو الملفات التي يحتاجها النقطة الدخول في الإنتاج. بالنسبة لمعظم تطبيقات Node: المخرج المُجمّع (dist/) وبدائل محددة. بالنسبة للبايثون: المكتبات المثبتة ورمز التطبيق، وليس requirements.txt، ولا الاختبارات، ولا الملاحظات.

2. تسرب الأسرار في الطبقات

إذا قمت بتمرير أسرار في معلمات البناء (مثلاً، ARG NPM_TOKEN متبوعًا باستخدامها في أمر RUN )، فإن هذه الأسرار تكون مرئية في كل طبقة تليها — حتى في بنية متعددة المراحل. docker history myimage سيظهر ذلك.

النهج الصحيح هو Docker BuildKit's --mount=type=secret:

RUN --mount=type=secret,id=npm_token     NPM_TOKEN=$(cat /run/secrets/npm_token) npm ci

السر يُوضع فقط عند تشغيل الطبقة — لا يُثبت أبدًا في تاريخ الصورة. قم بالبناء باستخدام: docker build --secret id=npm_token,src=.npmrc .

الحل البسيط الذي يستخدمه الناس — حذف السر في نفس RUN الطبقة — لا يساعد حقًا في بنية متعددة المراحل، لكن نهج BuildKit أكثر نظافة بغض النظر.

3. نسيان .dockerignore

تقلل بنية متعددة المراحل حجم الصورة النهائية، لكن COPY . . في مرحلة البناء تُرسل سياقك الكامل إلى خادم Docker. بدون .dockerignore، تشمل ذلك .git/, node_modules/، مجموعات الاختبار، ملفات محلية .env ، وأي أسرار تُخزن في ملفات نصية. يرى البناء كل ذلك.

أقل .dockerignore لأي مشروع Node:

.git
node_modules
dist
.env
*.log
coverage
.nyc_output

إضافة .dockerignore في نفس اليوم الذي تضيف فيه Dockerfile. يظهر حجم سياق البناء في السطر الأول من docker build الإخراج (Sending build context to Docker daemon X MB) — إذا كان هذا العدد كبيرًا بشكل مريب، فتحقق مما يُدرج.

أداة مفيدة لعملية Dockerfile

إذا كنت ترغب في نقطة بداية قبل كتابة ملفك الخاص، فإن مُولِّد Dockerfile على IO Tools ستعمل على تكوين ملف Docker متعدد المراحل لسلاسل شائعة. بعد أن تكتب شيئًا، قم بتشغيله من خلال مدقق ومنسق Dockerfile لإيقاف الأخطاء الشائعة قبل أن تصل إلى بيئة CI — مثل نقص WORKDIR، استخدام latest مُلائم، أو تشغيل كـ root بشكل غير ضروري.

الاستنتاج

تُعد بنية متعددة المراحل تغييرًا مكونًا من خطين: أضف مرحلة بناء مُسمّاة، ونسخ المخرج المُجمّع إلى صورة مُختصرة جديدة. تقليل الحجم غالبًا ما يكون مُستحقًا — 80-90% هو متوسط لتطبيقات Node وبايثون. الأخطاء الرئيسية هي التوسع الزائد في COPY --from، تسرب الأسرار كمعلمات بناء، وتجاهل .dockerignore. قم بتصحيح هذه الأخطاء وستحصل على صورة إنتاجية مُصممة بشكل مناسب للإنتاج.

هل تريد حذف الإعلانات؟ تخلص من الإعلانات اليوم

تثبيت ملحقاتنا

أضف أدوات IO إلى متصفحك المفضل للوصول الفوري والبحث بشكل أسرع

أضف لـ إضافة كروم أضف لـ امتداد الحافة أضف لـ إضافة فايرفوكس أضف لـ ملحق الأوبرا

وصلت لوحة النتائج!

لوحة النتائج هي طريقة ممتعة لتتبع ألعابك، يتم تخزين جميع البيانات في متصفحك. المزيد من الميزات قريبا!

إعلان · حذف؟
إعلان · حذف؟
إعلان · حذف؟

ركن الأخبار مع أبرز التقنيات

شارك

ساعدنا على الاستمرار في تقديم أدوات مجانية قيمة

اشتري لي قهوة
إعلان · حذف؟