فكر جيدًا قبل استخدام Docker-in-Docker لـ CI أو بيئة الاختبار.



Docker-in-Docker هو برنامج Docker افتراضي يعمل في الحاوية نفسها لإنشاء صور حاويات. كان الهدف الرئيسي من إنشاء Docker-in-Docker هو المساعدة في تطوير Docker نفسها. يستخدمه العديد من الناس لتشغيل Jenkins CI. يبدو هذا طبيعيًا في البداية ، ولكن بعد ذلك هناك مشاكل يمكن تجنبها عن طريق تثبيت Docker في حاوية Jenkins CI. توضح هذه المقالة كيفية القيام بذلك. إذا كنت مهتمًا بالحل النهائي بدون تفاصيل ، فما عليك سوى قراءة القسم الأخير من مقالة "حل المشكلة".



Docker-in-Docker: جيد


قبل أكثر من عامين ، قمت بإدراج العلم المميز في Docker وكتبت النسخة الأولى من dind . كان الهدف هو مساعدة الفريق الأساسي على تطوير Docker بشكل أسرع. قبل Docker-in-Docker ، كانت دورة التطوير النموذجية على النحو التالي:

  • اختراق القرصنة
  • المجسم
  • إيقاف برنامج تشغيل عامل الميناء ؛
  • إطلاق برنامج docker جديد ؛
  • اختبارات؛
  • تكرار حلقة.

إذا كنت ترغب في إنشاء تجميع جميل وقابل للتكرار (أي في حاوية) ، فقد أصبح أكثر تعقيدًا:

  • اختراق القرصنة
  • تأكد من تشغيل إصدار عامل من Docker ؛
  • بناء عامل الميناء الجديد مع عامل الميناء القديم ؛
  • وقف الشيطان عامل الميناء.
  • إطلاق برنامج docker جديد ؛
  • لاختبار؛
  • إيقاف برنامج Docker الجديد ؛
  • كرر.

مع ظهور Docker-in-Docker ، أصبحت العملية سهلة:

  • اختراق القرصنة
  • تجميع + إطلاق في خطوة واحدة ؛
  • تكرار حلقة.

أليس هذا أفضل بكثير؟



عامل الميناء في الميناء: سيئ


ومع ذلك ، على عكس الاعتقاد الشائع ، فإن Docker-in-Docker لا يتكون بنسبة 100 ٪ من النجوم ، والمهور ، وحيدات القرن. أعني ، هناك العديد من المشكلات التي يحتاج المطور إلى معرفتها.

أحدها يتعلق بـ LSM (وحدات أمان Linux) مثل AppArmor و SELinux: عند بدء الحاوية ، قد يحاول "Docker الداخلي" تطبيق ملفات تعريف الأمان التي تتعارض أو تخلط بين "Docker الخارجي". هذه هي أصعب مشكلة يجب حلها عند محاولة الجمع بين التطبيق الأصلي للعلامة المميزة. نجحت التغييرات الخاصة بي ، وستنتقل جميع الاختبارات أيضًا إلى جهاز Debian الخاص بي وآلات اختبار Ubuntu الافتراضية ، لكنها ستتعطل وتحترق على آلة مايكل كروسبي (على حد ما أتذكر ، كان لديه Fedora). لا أتذكر السبب الدقيق للمشكلة ، ولكن ربما حدث ذلك لأن Mike هو شخص حكيم يعمل مع SELINUX = فرض (استخدمت AppArmor) ، ولم تأخذ التغييرات التي أجريتها ملفات تعريف SELinux في الاعتبار.

عامل الميناء في الميناء: غاضب


تتعلق المشكلة الثانية بمحركات تخزين Docker. عند تشغيل Docker-in-Docker ، يتم تشغيل Docker الخارجي فوق نظام الملفات العادي (EXT4 أو BTRFS أو أي شيء لديك) ، ويتم تشغيل Docker الداخلي فوق نظام النسخ والكتابة (AUFS و BTRFS و Device Mapper ، إلخ.) ، اعتمادًا على ما تم تكوينه لاستخدام Docker خارجي). في هذه الحالة ، هناك العديد من التركيبات التي لن تعمل. على سبيل المثال ، لا يمكنك تشغيل AUFS فوق AUFS.

إذا قمت بتشغيل BTRFS أعلى BTRFS ، يجب أن يعمل هذا أولاً ، ولكن بمجرد ظهور المفاتيح الفرعية ، لا يمكن حذف المجلد الفرعي الأصلي. لا تحتوي وحدة Device Mapper على مساحة اسم ، لذلك إذا كانت العديد من مثيلات Docker تستخدمها على نفس الجهاز ، فيمكنهم جميعًا رؤية (والتأثير) الصور على بعضها البعض وعلى أجهزة النسخ الاحتياطي للحاويات. هذا سيء.

توجد حلول لحل العديد من هذه المشكلات. على سبيل المثال ، إذا كنت تريد استخدام AUFS في Docker الداخلي ، فما عليك سوى تحويل المجلد / var / lib / docker إلى واحد وكل شيء سيكون على ما يرام. أضاف Docker بعض مساحات الأسماء الأساسية إلى أسماء الأهداف في Device Mapper ، بحيث إذا تم إجراء مكالمات Docker متعددة على نفس الجهاز ، فلن "يخطوا" ضد بعضهم البعض.

ومع ذلك ، فإن هذا الإعداد أبعد ما يكون عن البساطة ، كما يمكنك أن ترى من هذه المقالات في مستودع dind على GitHub.

Docker in Docker: يزداد سوءًا


ماذا عن مخبأ البناء؟ قد يكون هذا صعبًا جدًا. غالبًا ما يسألني الناس "إذا قمت بتشغيل Docker-in-Docker ، فكيف يمكنني استخدام الصور الموجودة على المضيف ، بدلاً من إعادة كل شيء إلى Docker الداخلي"؟

حاول بعض الأشخاص المغامرين الربط / var / lib / docker من المضيف إلى حاوية Docker-in-Docker. في بعض الأحيان يتشاركون / var / lib / docker مع حاويات متعددة.


تريد إتلاف البيانات؟ لأن هذا هو بالضبط ما سيضر بياناتك!

تم تصميم برنامج docker بشكل واضح ليكون له وصول حصري إلى / var / lib / docker. لا شيء آخر يجب أن "يمس أو يلمس أو يلمس" أي ملفات Docker في هذا المجلد.

لماذا هو كذلك؟ لأنه نتيجة أحد أصعب الدروس المستفادة من تطوير dotCloud. عمل محرك الحاوية dotCloud مع العديد من العمليات للوصول / var / lib / dotcloud في نفس الوقت. الحيل الصعبة ، مثل استبدال الملف الذري (بدلاً من التحرير في مكانه) ، و "تجثم" الشفرة مع أقفال استشارية وإلزامية ، والتجارب الأخرى مع أنظمة آمنة مثل SQLite و BDB ، لم تنجح دائمًا. عندما قمنا بإعادة تصميم محرك الحاويات الخاص بنا ، والذي تحول في النهاية إلى Docker ، كان أحد قرارات التصميم الرئيسية هو جمع جميع عمليات الحاويات في ظل برنامج واحد للتخلص من كل هذا الهراء من الوصول المتزامن.

لا تفهموني خطأ: من الممكن جدًا القيام بشيء جيد وموثوق وسريع ، والذي سيشمل العديد من العمليات والتحكم الموازي الحديث. لكننا نعتقد أن كتابة التعليمات البرمجية والحفاظ عليها باستخدام Docker هو اللاعب الأبسط والأسهل.

هذا يعني أنه إذا قمت بمشاركة دليل / var / lib / docker بين مثيلات Docker متعددة ، فستواجه مشاكل. بالطبع ، قد يعمل هذا ، خاصة في المراحل الأولى من الاختبار. "استمع ، يا ما ، يمكنني تشغيل أوبونتو كرسو السفن!" ولكن حاول القيام بشيء أكثر تعقيدًا ، على سبيل المثال ، اسحب الصورة نفسها من حالتين مختلفتين ، وسوف ترى كيف يحترق العالم.

هذا يعني أنه إذا قام نظام CI الخاص بك بتنفيذ التجميعات وإعادة التجميعات ، فحينما تقوم بإعادة تشغيل حاوية Docker-in-Docker ، فإنك تخاطر بإسقاط قنبلة نووية في ذاكرة التخزين المؤقت الخاصة به. هذا ليس رائعًا على الإطلاق!

حل للمشكلة


دعونا نعود خطوة إلى الوراء. هل تحتاج حقًا إلى Docker-in-Docker أو تريد فقط أن تكون قادرًا على تشغيل Docker ، أي بناء وتشغيل حاويات وصور من نظام CI الخاص بك ، بينما يكون نظام CI نفسه في الحاوية؟

أراهن أن معظم الناس بحاجة إلى الخيار الأخير ، أي أنهم يريدون نظام CI مثل Jenkins لتشغيل الحاويات. وأسهل طريقة للقيام بذلك هي ببساطة إدخال مقبس Docker في حاوية CI الخاصة بك ، وربطها بعلامة -v.

ببساطة ، عند تشغيل حاوية CI (Jenkins أو أخرى) ، بدلاً من اختراق شيء ما باستخدام Docker-in-Docker ، ابدأ تشغيله من الخط:

docker run -v /var/run/docker.sock:/var/run/docker.sock ...

الآن ستتمكن هذه الحاوية من الوصول إلى مقبس Docker ، وبالتالي ، ستكون قادرة على إطلاق الحاويات. باستثناء أنه بدلاً من إطلاق حاويات "تابعة" ، سيتم تشغيل حاويات "ذات صلة".

جرب هذا باستخدام صورة Docker الرسمية (التي تحتوي على ثنائي Docker):

docker run -v /var/run/docker.sock:/var/run/docker.sock \
           -ti docker

يبدو ويعمل مثل Docker-in-Docker ، ولكنه ليس Docker-in-Docker: عندما تنشئ هذه الحاوية حاويات إضافية ، سيتم إنشاؤها في أعلى مستوى Docker. لن تواجه الآثار الجانبية للتداخل ، وستتم مشاركة ذاكرة التخزين المؤقت للبناء عبر مكالمات متعددة.

ملاحظة: نصحت الإصدارات السابقة من هذه المقالة بربط ثنائي Docker من المضيف بالحاوية. أصبح هذا غير موثوق به الآن ، حيث لم تعد آلية Docker تمتد إلى المكتبات الثابتة أو شبه الثابتة.

وبالتالي ، إذا كنت ترغب في استخدام Docker من Jenkins CI ، فلديك خياران:
تثبيت Docker CLI باستخدام نظام تغليف الصورة الأساسي (أي إذا كانت صورتك تعتمد على Debian ، استخدم حزم .deb) ، باستخدام Docker API.

القليل من الدعاية :)


أشكركم على البقاء معنا. هل تحب مقالاتنا؟ هل تريد رؤية مواد أكثر إثارة للاهتمام؟ ادعمنا عن طريق تقديم طلب أو التوصية لأصدقائك VPS القائم على السحابة للمطورين من $ 4.99 ، وهو نظير فريد من نوعه لخوادم مستوى الدخول التي اخترعناها لك: الحقيقة الكاملة عن VPS (KVM) E5-2697 v3 (6 نوى) 10GB DDR4 480GB SSD 1Gbps من $ 19 أو كيفية تقسيم الخادم؟ (تتوفر الخيارات مع RAID1 و RAID10 ، حتى 24 مركزًا و 40 جيجابايت DDR4).

Dell R730xd أرخص مرتين في مركز بيانات Equinix Tier IV في أمستردام؟ فقط لدينا 2 x Intel TetraDeca-Core Xeon 2x E5-2697v3 2.6GHz 14C 64GB DDR4 4x960GB SSD 1Gbps 100 TV من 199 دولارًا في هولندا!Dell R420 - 2x E5-2430 2.2 جيجا هرتز 6C 128 جيجا بايت DDR3 2x960GB SSD 1Gbps 100TB - من 99 دولار! اقرأ عن كيفية بناء مبنى البنية التحتية الفئة c باستخدام خوادم Dell R730xd E5-2650 v4 بتكلفة 9000 يورو مقابل سنت واحد؟

All Articles