
تتيح لك وظيفة الإنشاء متعدد المراحل في ملفات Dockerfile إنشاء صور حاوية صغيرة بمستوى أعلى من التخزين المؤقت وحماية أقل. في هذه المقالة ، سأريك بعض القوالب المتقدمة - شيء أكثر من نسخ الملفات بين الإنشاء والتشغيل. أنها تسمح لك لتحقيق أقصى قدر من الكفاءة وظيفة. ومع ذلك ، إذا كنت مبتدئًا في مجال التجميع متعدد المراحل ، فمن المحتمل أولاً أنه لن يكون من الخطأ قراءة دليل المستخدم.
توافق الإصدار
تمت إضافة دعم بناء متعدد المراحل إلى Docker في الإصدار v17.05. تعمل جميع القوالب مع أي إصدار لاحق ، لكن بعضها أكثر فاعلية ، بفضل الروابط التي تستخدم جانب خادم BuildKit . دعنا نقول BuildKit يتخطى بشكل فعال المراحل غير المستخدمة ، وإذا أمكن ، يخلق مراحل في نفس الوقت (لقد أبرزت هذه الأمثلة بشكل منفصل). تتم حاليًا إضافة BuildKit إلى Moby كواجهة خلفية تجريبية للبناء ويجب أن تكون متوفرة في Docker CE v18.06. يمكن استخدامه أيضًا بشكل مستقل أو كجزء من مشروع img .
الميراث المرحلة
يضيف البناء متعدد المراحل العديد من مفاهيم بناء الجملة الجديدة. بادئ ذي بدء ، يمكنك إعطاء المرحلة التي تبدأ بالأمر FROM
اسم AS stagename
واستخدام --from=stagename
في COPY
لنسخ الملفات من هذه المرحلة. في الواقع ، يوجد أمر مشترك بين الأمر FROM
والتسمية --from
، وليس لأي شيء لهما نفس الاسم. كلاهما يأخذ نفس الوسيطة ، يتعرف عليها ، ويبدأ مرحلة جديدة من هذه النقطة ، أو يستخدمها كمصدر لنسخ الملف. أي لاستخدام المرحلة السابقة في جودة الصورة الأصلية للمرحلة الحالية ، لا يمكنك أخذ فقط - من --from=stagename
، ولكن أيضًا اسم المرحلة FROM stagename
. يكون مفيدًا في حالة استخدام نفس الأجزاء الشائعة في العديد من الأوامر في Dockerfile: فهو يقلل من الكود الشائع ويبسط صيانته ، مع إبقاء الخطوات الفرعية منفصلة. وبالتالي ، لا يؤثر إعادة إنشاء مرحلة واحدة على ذاكرة التخزين المؤقتة للتجميع للآخرين. وفقًا لذلك ، يمكن تجميع كل مرحلة على حدة باستخدام تسمية - --target
عند استدعاء docker build
.
FROM ubuntu AS base RUN apt-get update && apt-get install git FROM base AS src1 RUN git clone … FROM base as src2 RUN git clone …
في هذا المثال ، يتم إنشاء المرحلتين الثانية والثالثة في BuildKit في نفس الوقت.
الاستخدام المباشر للصور
بدلاً من استخدام أسماء مرحلة التجميع في أوامر FROM
التي كانت تدعم مراجع الصور سابقًا فقط ، يمكنك استخدام الصور مباشرة باستخدام التسمية - من. اتضح لنسخ الملفات مباشرة من هذه الصور. على سبيل المثال ، linuxkit/ca-certificatesimage
في التعليمة البرمجية التالية مباشرة جذور TLS CA إلى الخطوة الحالية.
FROM alpine COPY --from=linuxkit/ca-certificates / /
صورة الاسم المستعار الشائعة
لا تتضمن مرحلة الإنشاء بالضرورة أي أوامر ؛ قد تتكون FROM
خط FROM
واحد. إذا كنت تستخدم الصورة في عدة أماكن ، فسيؤدي ذلك إلى تبسيط عملية القراءة وجعلها بحيث إذا كنت بحاجة إلى تحديث الصورة الإجمالية ، فإنك تحتاج فقط إلى تغيير سطر واحد.
FROM alpine:3.6 AS alpine FROM alpine RUN … FROM alpine RUN …
في هذا المثال ، كل مكان يستخدم صورة جبال الألب ملتزم فعليًا بـ alpine:3.6
، وليس alpine:latest
. عندما يحين الوقت للترقية إلى alpine:3.7
، ستحتاج إلى تغيير سطر واحد ، ولا شك: الآن تستخدم جميع عناصر التجميع إصدارًا محدثًا.
هذا هو الأهم عند استخدام وسيطة الإنشاء في الاسم المستعار. يشبه المثال التالي المثال السابق ، ولكنه يسمح للمستخدم بإعادة تعريف جميع مثيلات التجميع التي تشارك فيها صورة جبال الألب عن طريق تعيين --build-arg ALPINE_VERSION=value
خيار --build-arg ALPINE_VERSION=value
. تذكر: يجب تحديد أي وسيطات مستخدمة في أوامر FROM
قبل مرحلة الإنشاء الأولى .
ARG ALPINE_VERSION=3.6 FROM alpine:${ALPINE_VERSION} AS alpine FROM alpine RUN …
استخدام وسيطات الإنشاء في "- من"
يجب ألا تحتوي القيمة المحددة في - من تسمية COPY
على وسيطات التجميع. على سبيل المثال ، المثال التالي غير صالح.
// THIS EXAMPLE IS INTENTIONALLY INVALID FROM alpine AS build-stage0 RUN … FROM alpine ARG src=stage0 COPY --from=build-${src} . .
هذا يرجع إلى حقيقة أنه يجب تحديد التبعيات بين المراحل قبل بدء التجميع. ثم التقييم المستمر لجميع الفرق غير مطلوب. على سبيل المثال ، يمكن أن يؤثر متغير البيئة المحدد في صورة alpine
على تقييم القيمة - من. السبب في أنه يمكننا تقييم وسيطات الأمر FROM
هو أن هذه الوسائط يتم تعريفها عالميًا قبل بدء أي مرحلة. لحسن الحظ ، كما اكتشفنا سابقًا ، يكفي تحديد مرحلة الاسم المستعار باستخدام أمر FROM
واحد والإشارة إليه.
ARG src=stage0 FROM alpine AS build-stage0 RUN … FROM build-${src} AS copy-src FROM alpine COPY --from=copy-src . .
الآن ، إذا تجاوزت وسيطة التجميع src
، src
الخطوة الأولية لعنصر COPY
النهائي. يرجى ملاحظة: إذا لم تعد تستخدم بعض الخطوات ، فلن يتمكن من تخطيها إلا الروابط القائمة على BuildKit.
الشروط باستخدام بناء الحجج
طلب منا إضافة دعم لشروط نمط IF/ELSE
إلى Dockerfile. لا نعرف حتى الآن ما إذا كنا سنضيف شيئًا مشابهًا ، ولكن في المستقبل سنحاول استخدام دعم العميل في BuildKit. وفي الوقت نفسه ، من أجل تحقيق سلوك مماثل ، يمكنك استخدام المفاهيم الحالية متعددة المراحل (مع بعض التخطيط).
// THIS EXAMPLE IS INTENTIONALLY INVALID FROM alpine RUN … ARG BUILD_VERSION=1 IF $BUILD_VERSION==1 RUN touch version1 ELSE IF $BUILD_VERSION==2 RUN touch version2 DONE RUN …
يوضح المثال السابق الكود الزائف لظروف التسجيل باستخدام IF/ELSE
. لتحقيق سلوك مشابه مع عمليات الإنشاء الحالية متعددة المراحل ، قد تحتاج إلى تحديد شروط الفرع المختلفة كخطوات منفصلة واستخدام وسيطة لتحديد مسار التبعية الصحيح.
ARG BUILD_VERSION=1 FROM alpine AS base RUN … FROM base AS branch-version-1 RUN touch version1 FROM base AS branch-version-2 RUN touch version2 FROM branch-version-${BUILD_VERSION} AS after-condition FROM after-condition RUN …
تستند الخطوة الأخيرة في Dockerfile إلى الخطوة after-condition
، وهي الاسم المستعار للصورة (يتم التعرف عليه استنادًا إلى BUILD_VERSION
بناء BUILD_VERSION
). اعتمادًا على قيمة BUILD_VERSION
، يتم تحديد هذه المرحلة أو تلك من القسم الأوسط.
يرجى ملاحظة: فقط الروابط القائمة على BuildKit يمكنها تخطي الفروع غير المستخدمة. في الإصدارات السابقة من الروابط ، سيتم بناء جميع المراحل ، ولكن قبل إنشاء الصورة النهائية ، سيتم تجاهل نتائجها.
مساعد التطوير / الاختبار لمرحلة الإنتاج الدنيا
في الختام ، دعنا ننظر إلى مثال على دمج القوالب السابقة لشرح كيفية إنشاء ملف Dockerfile يقوم بإنشاء صورة إنتاجية دنيا ويمكن بعد ذلك استخدام محتوياته لاختبار صورة تطوير وإنشاءها. لنبدأ بمثال Dockerfile الأساسي:
FROM golang:alpine AS stage0 … FROM golang:alpine AS stage1 … FROM scratch COPY --from=stage0 /binary0 /bin COPY --from=stage1 /binary1 /bin
عندما يتم إنشاء صورة إنتاج الحد الأدنى ، وهذا خيار شائع إلى حد ما. ولكن ماذا لو كنت بحاجة أيضًا إلى الحصول على صورة مطورة بديلة أو إجراء اختبارات مع هذه الثنائيات في المرحلة النهائية؟ أول ما يتبادر إلى الذهن هو ببساطة نسخ ثنائيات مماثلة في مراحل الاختبار والتطوير. المشكلة هي هذه: ليس هناك ما يضمن أنك سوف تختبر جميع ثنائيات الإنتاج في نفس المجموعة. في المرحلة الأخيرة ، قد يتغير شيء ما ، لكنك ستنسى إجراء تغييرات مماثلة في مراحل أخرى أو ارتكاب خطأ في طريقة نسخ الملفات الثنائية. في النهاية ، نحن لا نختبر ملفًا ثنائياً منفصلاً ، بل الصورة النهائية.
البديل هو تحديد مرحلة التطوير والاختبار بعد مرحلة الإنتاج ونسخ محتويات مرحلة الإنتاج بأكملها. ثم استخدم أمر FROM
واحد لخطوة الإنتاج لجعل خطوة الإنتاج الافتراضية هي الخطوة الأخيرة مرة أخرى.
FROM golang:alpine AS stage0 … FROM scratch AS release COPY --from=stage0 /binary0 /bin COPY --from=stage1 /binary1 /bin FROM golang:alpine AS dev-env COPY --from=release / / ENTRYPOINT ["ash"] FROM golang:alpine AS test COPY --from=release / / RUN go test … FROM release
بشكل افتراضي ، سيستمر Dockerfile في تكوين صورة افتراضية دنيا ، بينما ، على سبيل المثال ، يقوم التجميع الذي يحتوي على الخيار - --target=dev-env
بإنشاء صورة تحتوي على غلاف يحتوي على جميع ثنائيات الإصدار النهائي.
آمل أن يكون هذا مفيدًا واقترح كيفية إنشاء Dockerfiles متعددة المراحل أكثر كفاءة. إذا كنت تشارك في DockerCon2018 وترغب في معرفة المزيد حول عمليات الإنشاء متعددة المراحل أو Dockerfiles أو BuildKit أو أي مواضيع ذات صلة ، فقم بالتسجيل في رابط مسار Hallway أو متابعة الاجتماعات الداخلية لمنصة Docker على مسارات Contribute و Collaborate أو Black Belt .