Docker: البناء متعدد المراحل
آخر تحديث: 2026-08-26
تتقلص الصورة من 1.2 جيجابايت إلى 12 ميجابايت — البناء متعدد المراحل هو التقنية الأكثر فعالية لتقليل حجم الصورة.
1. ما ستتعلمه
- مبادئ ومزايا البناء متعدد المراحل
- نسخ النواتج من مرحلة البناء
- اختيار الصورة الأساسية المثلى للتشغيل
- استراتيجيات تقليل حجم الصورة
- تحسين التخزين المؤقت وتقنيات التصحيح
2. قصة حقيقية لمطور Go
(1) نقطة الألم: صورة تطبيق بحجم 1.2 جيجابايت
صورة تطبيق Go الخاص بأليس حجمها 1.2 جيجابايت — لأنها استخدمت golang:1.22 كصورة أساسية، والتي تتضمن مترجم Go الكامل وكود المصدر وسلسلة الأدوات. لكن بيئة الإنتاج تحتاج فقط إلى ملف ثنائي مترجم بحجم 12 ميجابايت. كل نشر يستغرق 5 دقائق لتنزيل الصورة، ويتباطأ الخادم السحابي بنطاق ترددي 50 ميجابايت في كل مرة يتم فيها إصدار.
(2) نهج البناء متعدد المراحل
علمها بوب استخدام البناء متعدد المراحل: "في المرحلة الأولى، استخدم golang:1.22 للترجمة؛ في المرحلة الثانية، انسخ الناتج المترجم من Alpine."
# المرحلة 1: البناء
FROM golang:1.22 AS builder
WORKDIR /src
COPY . .
RUN go build -o server
# المرحلة 2: التشغيل (الملف الثنائي فقط، بدون مترجم Go)
FROM alpine:3.19
COPY --from=builder /src/server /usr/local/bin/
CMD ["server"]
(3) الفائدة: تم تقليل حجم الصورة من 1.2 جيجابايت إلى 12 ميجابايت
تم تقليل حجم الصورة بمقدار 100 مرة، وانخفض وقت التنزيل والنشر من 5 دقائق إلى 5 ثوانٍ، وتم تقليل الثغرات الأمنية من أكثر من 200 إلى 10 (المترجم غير مضمن في صورة الإنتاج).
3. مبادئ البناء متعدد المراحل
(1) مقارنة البناء أحادي المرحلة مقابل متعدد المراحل
graph TB
subgraph Single["بناء أحادي المرحلة"]
S1["golang:1.22<br/>أساسي 800 ميجابايت"] --> S2["go build<br/>ناتج الترجمة"] --> S3["الصورة النهائية<br/>1.2 جيجابايت<br/>(تشمل مترجم + كود مصدري)"]
end
subgraph Multi["بناء متعدد المراحل"]
M1["المرحلة 1: golang:1.22<br/>800 ميجابايت (مهملة)"] -->|"COPY --from"| M3["المرحلة 2: alpine:3.19<br/>أساسي 7 ميجابايت"]
M1 --> M2["go build<br/>ناتج الترجمة"]
M2 -->|"نسخ الملف الثنائي فقط"| M3
M3 --> M4["الصورة النهائية<br/>12 ميجابايت<br/>(الملف الثنائي فقط)"]
end
(1) الآلية الأساسية
| المرحلة | الغرض | المحتويات | الصورة النهائية |
|---|---|---|---|
| مرحلة البناء | الترجمة/البناء | مترجم، كود مصدري، أدوات بناء | ❌ مهملة |
| مرحلة التشغيل | التنفيذ | نسخ النواتج الضرورية فقط | ✅ محتفظ بها |
4. التطبيق العملي للبناء متعدد المراحل
▶ مثال: البناء متعدد المراحل في Go (الصعوبة: ⭐⭐)
# ============================================
# المرحلة 1: ترجمة الملف الثنائي Go
# ============================================
FROM golang:1.22 AS builder
WORKDIR /src
# نسخ ملفات التبعيات أولاً (تحسين التخزين المؤقت)
COPY go.mod go.sum ./
RUN go mod download
# نسخ الكود المصدري والترجمة
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o server
# ============================================
# المرحلة 2: إنشاء صورة تشغيل مصغرة
# ============================================
FROM alpine:3.19
# تثبيت شهادات CA (لطلبات HTTPS)
RUN apk --no-cache add ca-certificates
# نسخ الملف الثنائي المترجم فقط من مرحلة البناء
COPY --from=builder /src/server /usr/local/bin/server
# التشغيل كمستخدم غير root
RUN adduser -D -u 1000 appuser
USER appuser
EXPOSE 8080
CMD ["server"]
# بناء الصورة متعددة المراحل
docker build -t go-app:1.0 .
# التحقق من حجم الصورة النهائية
docker images go-app:1.0
REPOSITORY TAG IMAGE ID SIZE
go-app 1.0 a1b2c3d4e5f6 12.5MB
▶ مثال: البناء بـ Node.js → التشغيل بـ Nginx (الصعوبة: ⭐⭐⭐)
مشاريع الواجهة الأمامية مثل React و Vue: الملفات الثابتة تُولد أثناء مرحلة البناء، ويستخدم Nginx لخدمة طلبات HTTP أثناء وقت التشغيل.
# ============================================
# المرحلة 1: بناء تطبيق React
# ============================================
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# ============================================
# المرحلة 2: الخدمة بـ Nginx
# ============================================
FROM nginx:1.25-alpine
# نسخ الأصول المبنية من مرحلة البناء
COPY --from=builder /app/build /usr/share/nginx/html
# إعدادات Nginx مخصصة لتوجيه SPA
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
node_modules قد تكون 500+ ميجابايت، لكن ناتج البناء (HTML/CSS/JS) عادة يكون 5–20 ميجابايت فقط.
▶ مثال: استنساخ بيئة Python الافتراضية (الصعوبة: ⭐⭐)
يمكن أيضًا بناء مشاريع Python في مراحل متعددة: المرحلة الأولى تنشئ بيئة افتراضية، والمرحلة الثانية تنسخ البيئة الافتراضية وكود التطبيق فقط.
# المرحلة 1: إنشاء البيئة الافتراضية
FROM python:3.12 AS builder
WORKDIR /app
COPY requirements.txt .
RUN python -m venv /opt/venv && \
/opt/venv/bin/pip install --no-cache-dir -r requirements.txt
# المرحلة 2: التشغيل بالبيئة الافتراضية فقط
FROM python:3.12-slim
WORKDIR /app
COPY --from=builder /opt/venv /opt/venv
COPY . .
ENV PATH="/opt/venv/bin:$PATH"
CMD ["python", "app.py"]
5. اختيار صورة أساسية للتشغيل
(1) مقارنة صور ثلاث قواعد مصغرة
| الصورة الأساسية | الحجم | تتضمن Shell | تتضمن مدير حزم | تتضمن libc | حالات الاستخدام |
|---|---|---|---|---|---|
scratch |
0 ميجابايت | ❌ | ❌ | ❌ | ملفات Go/Rust الثنائية المترجمة بشكل ثابت |
distroless |
2–20 ميجابايت | ❌ | ❌ | ✅ (glibc) | من Google، آمنة للغاية |
alpine |
7 ميجابايت | ✅ (ash) | ✅ (apk) | ✅ (musl) | عند الحاجة إلى التصحيح |
(2) ملاحظات حول صورة Scratch
# Scratch: لا شيء على الإطلاق في الصورة
FROM scratch
COPY --from=builder /src/server /server
CMD ["/server"]
scratch لا تتضمن shell أو ca-certificates أو بيانات المنطقة الزمنية. يجب ترجمة برامج Go بشكل ثابت CGO_ENABLED=0 للتشغيل على scratch. إذا كان برنامجك يتطلب طلبات HTTPS أو سجلات بطابع زمني، فمن الأكثر أمانًا استخدام alpine.
6. تقنيات متقدمة للبناء متعدد المراحل
▶ مثال: تسمية المراحل و --target لاختيار البناء (الصعوبة: ⭐⭐)
# مراحل مسماة للوضوح
FROM golang:1.22 AS builder
WORKDIR /src
COPY . .
RUN go build -o server
# بديل: مرحلة تطوير مع أدوات إعادة التحميل التلقائي
FROM golang:1.22 AS dev
WORKDIR /src
RUN go install github.com/cosmtrek/air@latest
COPY . .
CMD ["air"]
# الإنتاج: تشغيل مصغر
FROM alpine:3.19 AS prod
COPY --from=builder /src/server /usr/local/bin/
CMD ["server"]
# بناء مرحلة dev فقط
docker build --target dev -t myapp:dev .
# بناء مرحلة prod فقط
docker build --target prod -t myapp:prod .
▶ مثال: --target لتصحيح النواتج الوسيطة (الصعوبة: ⭐⭐⭐)
# بناء وفحص مرحلة البناء
docker build --target builder -t myapp:builder .
# تشغيل صورة البناء لتصحيح مشاكل البناء
docker run -it myapp:builder bash
# التحقق من الملف الثنائي المترجم
docker run --rm myapp:builder ls -la /src/server
7. مرجع أحجام نواتج الترجمة حسب اللغة
| اللغة | الصورة الأساسية | ناتج البناء | الصورة النهائية | نسبة التخفيض |
|---|---|---|---|---|
| Go | golang:1.22 (780 ميجابايت) | ملف ثنائي ثابت 12 ميجابايت | alpine: 12 ميجابايت | 65x |
| Rust | rust:1.75 (1.5 جيجابايت) | ملف ثنائي ثابت 8 ميجابايت | alpine: 15 ميجابايت | 100x |
| Node.js | node:20 (1.1 جيجابايت) | build/ 5-20 ميجابايت | nginx-alpine: 25 ميجابايت | 44x |
| Java | eclipse-temurin:21 (450 ميجابايت) | JAR 30 ميجابايت | jre-alpine: 170 ميجابايت | 2.6x |
| Python | python:3.12 (1 جيجابايت) | venv/ 50 ميجابايت | slim: 200 ميجابايت | 5x |
8. مثال كامل: بناء متعدد المراحل لتطبيق ويب Go
# ============================================
# Dockerfile متعدد المراحل لتطبيق ويب Go
# المرحلة 1: بناء مع تخزين مؤقت للتبعيات
# المرحلة 2: تشغيل Alpine مصغر
# ============================================
# ---------- المرحلة 1: البناء ----------
FROM golang:1.22-alpine AS builder
# تثبيت git (لـ go mod download مع المستودعات الخاصة)
RUN apk add --no-cache git
WORKDIR /src
# التخزين المؤقت: نسخ ملفات التبعيات أولاً
COPY go.mod go.sum ./
RUN go mod download
# نسخ الكود المصدري
COPY . .
# بناء ملف ثنائي ثابت (CGO_ENABLED=0 لـ scratch/alpine)
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
go build -ldflags="-w -s" -o /app/server
# ---------- المرحلة 2: التشغيل ----------
FROM alpine:3.19
# تثبيت تبعيات وقت التشغيل
RUN apk --no-cache add ca-certificates tzdata
# إنشاء مستخدم غير root
RUN adduser -D -u 1000 appuser
WORKDIR /app
# نسخ الملف الثنائي فقط من مرحلة البناء
COPY --from=builder /app/server .
# تعيين الملكية
RUN chown -R appuser:appuser /app
USER appuser
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \
CMD wget -qO- http://localhost:8080/health || exit 1
CMD ["/app/server"]
# بناء
docker build -t go-web:1.0 .
# تشغيل
docker run -d -p 8080:8080 --name go-web go-web:1.0
# التحقق من الحجم والصحة
docker images go-web:1.0
docker inspect go-web --format='Size: {{.Size}}, Health: {{.State.Health.Status}}'
REPOSITORY TAG SIZE
go-web 1.0 15.2MB
Size: 15974400, Health: healthy
❓ أسئلة شائعة
go build التخزين المؤقت مباشرة. فقط الصورة النهائية لا تتضمن طبقات مرحلة البناء. سرعة البناء تقريبًا نفس سرعة البناء أحادي المرحلة، لكن حجم الصورة ينخفض بشكل كبير.--target builder للبناء حتى مرحلة البناء، ثم استخدم docker run -it myapp:builder bash للدخول إلى مرحلة التصحيح. يمكنك أيضًا تعريف مرحلة dev في Dockerfile تتضمن أدوات تصحيح (مثل dlv أو air).docker exec للدخول إليها. طرق التصحيح: ① بناء صورة تصحيح باستخدام --target builder؛ ② تغيير FROM scratch مؤقتًا إلى FROM alpine للتصحيح؛ ③ استخدام docker cp على المضيف لنسخ ملفات السجل للخارج.docker history لا يزال مرئيًا. استخدم --mount=type=secret (ميزة BuildKit) لتمرير الأسرار بأمان: RUN --mount=type=secret,id=ssh_key cp /run/secrets/ssh_key /root/.ssh/id_rsa.📖 ملخص
- البناء متعدد المراحل: الترجمة تحدث أثناء مرحلة البناء، بينما مرحلة التشغيل تنسخ النواتج فقط، مما يقلل حجم الصورة من جيجابايت إلى ميجابايت
FROM xxx AS builderتسمية المرحلة،COPY --from=builderنسخ عبر المراحل- خيارات الصورة الأساسية: scratch (0 ميجابايت) > distroless (2–20 ميجابايت) > alpine (7 ميجابايت)
--targetيمكن البناء في مرحلة محددة للتصحيح والتطوير- اللغات المترجمة مثل Go و Rust تستفيد أكثر (تخفيض الحجم 100x)، تليها مشاريع Node.js للواجهة الأمامية (44x)
CGO_ENABLED=0+-ldflags="-w -s"هي المعاملات القياسية للترجمة الثابتة في Go
📝 تمارين
- تمرين أساسي (الصعوبة: ⭐): قارن أحجام صور تطبيق Go المبنية باستخدام البناء أحادي المرحلة ومتعدد المراحل — استخدم أولاً
FROM golang:1.22لبناء أحادي المرحلة، ثم استخدم بناء متعدد المراحل، وسجل الفرق في الحجم بين الصورتين. - تمرين متقدم (الصعوبة: ⭐⭐): ابنِ تطبيق React في مراحل متعددة:
node:20-alpineبناء →nginx:1.25-alpineتشغيل. قارن أحجام الصورة النهائية لصورة Node.js الأساسية وصورة Nginx الأساسية. - تحدٍ (الصعوبة: ⭐⭐⭐): استخدم
docker historyلتحليل حجم كل طبقة في صورة مبنية متعددة المراحل، واشرح لماذا لا تدرج طبقات مرحلة البناء في الصورة النهائية.