We are still cooking the magic in the way!
BuildKit وأداء البناء
BuildKit وأداء البناء
كان محرك بناء Docker القديم — المُستدعى بواسطة docker build قبل عصر BuildKit — معالجًا تسلسليًا للطبقات؛ لا يستطيع تشغيل المراحل المستقلة بالتوازي، ويُخزّن الأسرار في تاريخ الصورة، وكانت ذاكرة التخزين المؤقت لديه خشنة الحبيبات. BuildKit، الذي أصبح المحرك الافتراضي منذ Docker 23.0، يعيد كتابة هذه القواعد من أساسها. في Google وMeta وكل شركة كبيرة أخرى، فهم آليات BuildKit الداخلية ليس اختياريًا: دورة بناء مدتها 90 ثانية كان يمكن أن تكون 18 ثانية تمثّل ساعات عمل مهندسين حقيقية تُهدر يوميًا على مستوى الأسطول.
كيف يختلف BuildKit معماريًا
يُمثّل BuildKit ملف Dockerfile كـرسم بياني دوري موجّه (DAG) من عمليات LLB (Low-Level Build) بدلًا من قائمة تعليمات مسطّحة. يُرسَل هذا الرسم إلى الديمون buildkitd، الذي يمكنه جدولة العقد المستقلة بالتوازي، وتخطّي الرسوم الفرعية التي نتائجها مُخزَّنة مسبقًا، وبث الطبقات كـblobs قابلة للعنونة بالمحتوى. النتيجة: بناء متعدد المراحل متزامن فعلًا لا مجرد مظهر.
تفعيل BuildKit
منذ Docker Engine 23.0، أصبح BuildKit هو الافتراضي. في البيئات القديمة أو بيئات CI، فعّله صراحةً:
تثبيت الذاكرة المؤقتة (Cache Mounts): أكبر مكسب بخطوة واحدة
يُرفق تثبيت الذاكرة المؤقتة (--mount=type=cache) مجلدًا دائمًا بتعليمة RUN يبقى عبر دورات البناء دون أن يتحول إلى طبقة. هذه هي الطريقة المعيارية لتخزين مديري الحزم مؤقتًا. بدون تثبيت الذاكرة المؤقتة، يُعيد كل go mod download أو pip install تنزيل جيجابايتات من الاعتماديات عند كل إغفال للذاكرة المؤقتة.
id والمستخدم الحالي. اضبط sharing=locked (بناء واحد في كل مرة) أو sharing=private (نسخة مستقلة لكل بناء) عند تشغيل دورات بناء متزامنة في CI لتجنب تلف الذاكرة المؤقتة. الافتراضي هو sharing=shared — مناسب لمعظم الحالات.
أسرار البناء: لا مكان لها في الطبقات
الحل القديم ما قبل BuildKit للأسرار (ARG، ENV، النسخ متعدد المراحل) كان دائمًا يترك السر في طبقة وسيطة على الأقل يمكن الوصول إليها عبر docker history. أسرار BuildKit تُثبَّت كـtmpfs أثناء تعليمة RUN فقط ولا تُكتب في أي طبقة على الإطلاق. هذه هي الطريقة الآمنة الوحيدة لاستهلاك بيانات الاعتماد في وقت البناء.
docker history --no-trunc. في سجلات الحزم الداخلية على نطاق Google، هذه ثغرة أمنية نشطة. استخدم --mount=type=secret حصريًا.
تمرير وكيل SSH في وقت البناء
تتطلب اعتماديات Git الخاصة وصول SSH أثناء البناء. يوفر BuildKit --mount=type=ssh الذي يُعيد توجيه socket وكيل SSH من المضيف إلى عملية البناء — دون أن تلمس أي ملف مفاتيح الصورة قط:
docker buildx والبناء متعدد المنصات
buildx هو إضافة CLI التي تكشف واجهة BuildKit الكاملة. ميزته الأكثر أهمية في الإنتاج هي بناء الصور متعددة المنصات — بناء linux/amd64 وlinux/arm64 في أمر واحد ودفع manifest متعدد المعماريات. في AWS وGCP، يوفّر arm64 (Graviton / Tau T2A) تخفيضًا في التكلفة بنسبة 20–40% للأحمال الثقيلة على المعالج.
ubuntu-latest (amd64) وعداء ubuntu-latest-arm64، ابن كل منصة بشكل أصلي، ثم ادمج الـmanifests بـdocker buildx imagetools create. هذا ما تفعله سجلات الصور الكبيرة داخليًا.
الذاكرة المؤقتة المضمّنة وخلفيات الذاكرة البعيدة
يمكن لـBuildKit تصدير ذاكرته المؤقتة إلى سجل الصور حتى يعيد استخدامها عداؤو CI الجدد. مع --cache-to و--cache-from، يحقق العداء الجديد أوقات بناء قريبة من أوقات الذاكرة الدافئة:
mode=min (الافتراضي) يُخزّن طبقات المرحلة النهائية فقط — مفيد حين تريد أصغر blob للذاكرة المؤقتة. mode=max يُخزّن كل مرحلة وسيطة، ما يُنتج معدلات إصابة أفضل للبناء متعدد المراحل بتكلفة صورة cache أكبر. في monorepos ذات مراحل قاعدة مشتركة كثيرة، يكسب mode=max دائمًا تقريبًا.
أنماط الفشل في الإنتاج
- تسمّم الذاكرة المؤقتة عبر وسوم متغيرة: استخدام
--cache-fromمع وسم cache كـ:latestتكتب عليه وظيفة أخرى بالتزامن يؤدي إلى دورات بناء غير حتمية. ثبّت وسوم الذاكرة المؤقتة على فرع أو مرجع ثابت. - تثبيتات ذاكرة مؤقتة قديمة في CI: تثبيتات الذاكرة المؤقتة لـBuildKit محلية للعقدة. على عداؤي CI المؤقتين، يكون التثبيت فارغًا دائمًا. استخدم خلفيات الذاكرة المؤقتة للسجل، لا التثبيتات المحلية، في CI.
- نفاد ذاكرة buildkitd في monorepos الكبيرة: زد حدود ذاكرة
buildkitdواضبط--oci-worker-snapshotter=overlayfsعلى Linux للحصول على إزالة تكرار أفضل للطبقات. - تسريب الأسرار عبر build args: راجع التحذير أعلاه. راجع سجلات CI بحثًا عن الرموز المميزة التي مُررت عبر
ARG— تظهر بنص عادي في مخرجات البناء المطوّلة.