كيفية تشغيل Docker والحصول على المزيد من النوم مما فعلت

الأخطاء في قاعدة بيانات Gusto تعني أن الناس لا يتقاضون رواتبهم. مدفوعات الرهن العقاري ، والكلية الدراسية ، ودعم الطفل - كلهم ​​يعتمدون على شيكات دقيقة وفي الوقت المناسب. لهذا السبب نحتاج إلى عشرات الآلاف من الاختبارات عبر Ruby و Python و Go لتشغيلها على كل التزام.

يساعدنا خط أنابيب التكامل المستمر شديد الإرساء (CI) على القيام بذلك ، على نطاق واسع ، لجميع مستودعاتنا.

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

حيث بدأت هذه القائمة

اعتقدت أنني كنت أرى الأشياء ليلة واحدة.

كنت أقوم بتصحيح تكامل ActiveRecordDoc مع خط أنابيب التكامل المستمر القائم على Docker. ActiveRecordDoc هو جوهرة روبي محلية المنشأ تقوم بالتحقق من صحة الحقول المتعلقة ب PII الموجودة لدينا في معظم جداول قاعدة البيانات الخاصة بنا. لسوء الحظ ، كانت خطوة CI التي أدارت مهام Rake للأحجار الكريمة معلقة بشكل عشوائي ، مما جعل مجموعة الاختبار شديدة التقلب للفريق الهندسي بأكمله ، مما قلل من سرعة التكرار ، وألحق الضرر بالانتشار.

بعد أن أمضيت معظم المساء في البحث في سجلات ، قررت أن أجرب نفس البرنامج النصي محليًا. رأيت في النهاية محطة بلدي المحلية يتعطل بنفس طريقة خطوة CI.

حاولت أن أكرر هذه العملية - لكنها عملت بشكل جيد ، مرارًا وتكرارًا.

هل كنت أهلوسة؟

حاولت تسع مرات أخرى. تم تعليق البرنامج النصي أخيرًا قبل أن يتم تنفيذ وظيفة Ruby الخاصة. ظللت يدي عن لوحة المفاتيح ، وظل البرنامج معلقًا.

ضربت بحذر السهم "Up".

استمر التنفيذ.

هوه.

من خلال قراءة الكود ، بدا الأمر وكأنه تعبير Ruby المعلق المطلوب لإخراج اسم كل فئة من فئات طراز Rails المحملة على سطر واحد. تم إرسال 300،000 حرفًا بسهولة إلى STDOUT مرة واحدة. تساءلت إذا كانت الكمية الأولية من الأحرف في سطر واحد تسبب مشكلة. توقفت عن تسجيل الخط ، وتوقف وضع التقشر.

Welp.

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

تعرفت على هذا لـ ActiveRecordDoc عن طريق إضافة وظيفة SafeLog Ruby من شأنها أن تقسم المخرجات إذا تجاوز سطر واحد 10000 بايت. تم إصلاح الرقائق. عظيم!

الترفيه الدرامي: عندما نجد خطأ

مبادئ

يوفر Docker آلية لتحديد بيئات تنفيذ معزولة ، وإنشاء مثيل لها ، والحفاظ عليها. نفكر في حاويات Docker كوزن أثقل من العمليات ، ولكنها أخف وزنًا من الأجهزة الافتراضية الحقيقية. إنهم يتفوقون في مساعدتنا في تشغيل CI ، وإنشاء بيئات قابلة للتكرار ، واستخدام موارد المضيف للتطبيقات المنشورة بشكل أفضل. نظرًا لأنها غير معزولة تمامًا - على سبيل المثال ، مشاركة UIDS / GIDS مع النظام المضيف (ولكن فقط على أنظمة المضيف مثل Linux التي تدعم UIDs / GIDs) - يمكن أن تكون صعبة الاستخدام بشكل متواصل عند التنسيق.

المصطلح

الصورة: النقط الثنائية غير الثابتة المخزنة على القرص أو في دوكرهوب والتي يمكن إنشاء مثيل لها في "حاويات". صور عامل ميناء.

الحاوية: صورة تم إنشاء مثيل لها تقوم بتشغيل أوامر عشوائية. عامل ميناء الحاويات

نظام المضيف: النظام "الأساسي" الذي يقوم بتشغيل حاوية عامل ميناء. إذا كنت تقوم بتشغيل حاويات Docker على الكمبيوتر المحمول ، فإن الكمبيوتر المحمول هو النظام المضيف.

وحدة التخزين: نظام ملفات يمكن لحاوية عامل الوصول الوصول إليه. يمكن للحاوية مشاركة وحدات التخزين مع حاويات أخرى ونظام ملفات المضيف. حجم عامل ميناء ليرة سورية

عامل الرصيف: آلية تزامن لحاويات دوكر. عامل بناء - يؤلف ملف .yml المقدمة ويولد أوامر Docker الخام. من الناحية النظرية ، كل ما يمكنك القيام به مع عامل التأسيس ، يمكنك القيام به مع عامل الميناء.

الإصدارات

كان لدينا انقطاع في CI لأن الصورة الافتراضية لـ mysql على Dockerhub تمت ترقيتها من 5.7 إلى 8. اعتمد تثبيت Ruby على 5.7 ، والذي لم يعد موجودًا على المضيفين الجدد. بعض أفضل الممارسات التي استخلصناها:

  • عند الوراثة من صورة أساسية ، لا تترك الصورة الأساسية دون انقطاع (FROM: ruby). قم دائمًا بإصداره: (من: روبي: 2.3.4).
  • في ملف docker-compose.yml ، حدد دائمًا بشكل واضح إصدارًا من docker-compose باستخدام مفتاح الإصدار. هناك اختلافات كبيرة بين الإصدارات 2 و 3 ، إلخ.
  • تأكد من أن إصدار محرك الرصيف الذي تستخدمه هو نفسه ثابت عبر أسطولك. ترقية بشكل دوري. تتم ترقية Docker الثنائية أحيانًا بطرق غير متوافقة مع الإصدارات السابقة.

طبقات

الطبقات هي ببساطة ملفات مخزنة على نظام التشغيل المضيف تتكون من برنامج تشغيل Docker’s Union Filesystem لإنشاء الصورة النهائية. يمكن أن يؤدي الاستخدام الذكي للطبقة إلى تبسيط عملية النشر والتصحيح ، في حين أن طبقات لصق النسخ أو سوء الفهم يمكن أن تؤدي إلى الكثير من العمل غير الضروري.

  • ينشئ كل سطر محلي في Dockerfile "طبقة" للصورة التي تم إنشاؤها في النهاية.
  • كل طبقة تعتمد على كل طبقة سابقة. يتم تخزين كل طبقة في ذاكرة التخزين المؤقت ، والتي يمكن أن تسرع بشكل كبير بناء Docker الخاص بك. يمثل Docker Image مجموعة خطية من الطبقات.
  • كلما كان الاختناق على خط في Dockerfile ، كان يجب سرده في الملف السابق. يجب أن تكون خطوط المداخن العليا بالقرب من أسفل الملف. سيؤدي ذلك إلى تسريع عمليات الإنشاء عن طريق السماح لمنشئ صور Docker بالاعتماد على التخزين المؤقت.
  • إذا وجدت نفسك تلصق مجموعة الطبقات نفسها بين طبقات Dockerfiles المختلفة ، فقم بإلقاء نظرة على التركيبات متعددة المراحل. يمكن أن تساعد في القضاء على التكرار عبر قاعدة البيانات الخاصة بك.

عزل

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

  • استخدم علامة -project-name مع عامل التهيئة (لبادئة جميع الحاويات التي تم إنشاؤها) بشيء جديرة بالثقة بشكل فريد مثل BUILDKITE_JOB_ID ، أو رقم مستخرج من PRNG.
  • قم بتشغيل برنامج docker-compose بشكل صريح أو توقف برنامج docker بعد انتهاء التعليمات البرمجية.
  • إذا كان عليك تنظيم حاويات الإرساء يدويًا ، فقم بتسمية وحدات التخزين والشبكات بشكل فريد بحيث لا يكون هناك تلوث متقاطع.

استخدام الموارد

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

  • لا تحتوي حاوية الإرساء المفردة على أي قيود على الموارد ، ويتم فقط عنق الزجاجة من خلال ما سيعطيه نظام التشغيل kernel / host.
  • عامل الميناء ورسو السفن يدعمان أعلامًا خاصة لتقييد ذاكرة الحاوية ، وحدة المعالجة المركزية ، استهلاك المبادلة.
  • قم بتشغيل نظام عامل الرصيف - مجلدات بشكل دوري لمسح الصور ، وحدات التخزين ، الشبكات ، وغيرها التي لا ترتبط بالحاويات التي تعمل حاليًا. لتجنب ظروف السباق مع الحاويات الحية ، يمكنك استخدام مرشح التاريخ على النحو التالي: عامل تقليم حاوية الإرساء - عامل التصفية "حتى = 24 ساعة" فقط لتقليم الحاويات التي تكون يومًا أو أكثر.

نشر

استخدمنا في البداية Dockerhub لتخزين واسترجاع جميع الصور لدينا. لسوء الحظ ، كتب دوكرهوب فشلت بانتظام وكان سببا هاما من التقليب CI. كان من السهل أيضًا الكتابة فوق الصور بطريق الخطأ ، مع عدم وجود مسار تدقيق. هناك بدائل SaaS مثل Docker Trusted Registry و Artifactory وغيرها ، لكنها باهظة الثمن للغاية ولا نحتاج بعد إلى جميع ميزاتها.

  • نستضيف حاليًا مثيلًا لسجل Docker مفتوح المصدر ليكون بمثابة ذاكرة تخزين مؤقت للكتابة والقضاء على مخاوف موثوقية Dockerhub على القراءات.
  • نعتقد أن الاعتماد فقط على image_name: الأحدث لخدمة أحدث مثيل للصورة هو صورة مضادة ، حيث سيتم الكتابة فوق الصورة القديمة.
  • بدلاً من ذلك ، تأكد من وجود نسخة من صورتك دائمًا في Dockerhub. على سبيل المثال ، إذا قمت برفع صورة باسم ، على سبيل المثال ، gusto / zenpayroll: الأحدث ، فتأكد أيضًا من وجود صورة بها علامة MD5 / SHA خام في Dockerhub (gusto / zenpayroll: abc123).
  • يتم تخزين صور وطبقات المرسى فقط كملفات على نظام الملفات ، وتكون واضحة لخبزها في البرامج النصية للتمهيد. يعمل هذا على زيادة وقت بدء التشغيل (يلغي قراءة الشبكة مع سحب عامل الإرساء) ويمكن أن يقلل الطلبات على متاجر القطع الأثرية الخاصة بك.
الترفيه الدرامي: عندما نصلح الخلل

أحجام

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

  • إذا كنت تقوم بالتطوير في Docker ، فتأكد من التخزين في التعليمات البرمجية من نظام ملفات المضيف ، بدلاً من ADD’ing أو COPY’ing ، لتقليل دورات التكرار بشكل كبير.
  • لا تخبئ أبدًا أسرار ومواد تشفير خاصة في صورة. إذا لم تتمكن من استرداد الأسرار من إحدى الخدمات ، فقم بتخزينها من نظام الملفات المضيف.
  • تفضل وحدات التخزين المسماة على وحدات التخزين مجهولة المصدر / مجهولة الاسم.
  • من السهل إنشاء الملفات المملوكة للجذر غير القابلة للتشكيل على نظام ملفات المضيف. عند الإمكان ، قم بتنفيذ عمليات الحاوية كمستخدم ليس لديه أذونات الجذر على القرص.
  • إذا قمت بالتخزين في ملفات من الحاوية المضيفة ، فسيستوردون نفس البتات الدقيقة لمعرفات UID / GID الخاصة بهم - حتى إذا لم تكن هناك معرفات UID / GID مكافئة في سياق تنفيذ الحاوية ، أو يتم تعيينهم في مكان آخر. فقي بعناية مع توجيهات ADD الخاص بك!

الشذوذ المتنوعة

هناك الكثير من الحالات الزاوية مع Docker. ابق متيقظًا واحذر من الغرابة.

  • سوف يتعطل Docker بشكل غير محدد - إلى الأبد - إذا حاولت طباعة الكثير من المخرجات في مكالمة واحدة إلى STDOUT. هذا خطأ قديم في Docker ، وهو سهل التكرار بشكل مذهل ، وكان مصدر العديد من الاختبارات الرقيقة الغريبة بالنسبة لنا.
  • للتغلب على هذا ، تأكد من تقسيم الخطوط الطويلة للغاية في مكالمات STDOUT اللاحقة. لا نعرف الشكل الذي يبدو عليه الحد الأدنى بعد (وربما يعتمد على النظام) ، لكن بضعة آلاف من الأحرف على سطر واحد كانت ملعبًا جيدًا لنا للبقاء أدناه.
  • وحدات التخزين على OS X بطيئة بشكل لا يصدق. إذا كنت تقوم بتشغيل خادم Rails من حاوية Docker ، وتحاول تخزين مئات الأصول ، فسيكون ذلك بطيئًا للغاية بسبب كيفية عمل تسلسل مكالمات open () في Docker لنظام التشغيل Mac. لا يوجد هذا القيد عند تشغيل Docker في Linux لأن Docker يستخدم فعليًا نواة نظام التشغيل المضيف ، بينما Docker for Mac يدير Alpine Linux VM وراء الكواليس.

نصائح التصحيح

من الشائع للغاية الحاجة إلى تصحيح تشغيل حاويات السفن. نظرًا لأننا نجري اختبارات Rails بشكل شائع ، فقد نحتاج ، على سبيل المثال ، إلى تعديل ملفات RSpec لإدراج عبارات pr أو إعادة إنشاء الصور على مضيف CI مع تثبيت إصدارات حزم مختلفة. اكتشفنا بعض النصائح لجعل تصحيح الأخطاء أسهل.

  • تأكد من وجود بيئة تحرير عارية (vim / nano ، curl ، إلخ) كجزء من Dockerfile ، حتى بالنسبة للصور المشتقة من الصور الأساسية خفيفة الوزن مثل جبال الألب. إذا كنت بحاجة إلى تصحيح / تعديل / إعادة تشغيل حاوية مباشرة ، فسيكون ذلك أسهل كثيرًا.
  • استغلال الطبقات والتوجيه من بشدة. أحد أغلى أجزاء Dockerfile عادة ما يكون MyCustomPackages && MoreCustomPackages تثبيت apt-get. إذا وضعت هذه في طبقاتها الخاصة ، يمكنك تعديل بقية الملف دون الحاجة إلى إعادة بناء كل شيء.
  • في بعض الأحيان ، إذا لم تكن متأكدًا من السبب وراء تثبيت شيء ما ، فستكون أسرع تلميحات تصحيح الأخطاء هي قطع جميع الطبقات باستثناء تلك التي تعتقد أنها تقدم الخطأ ، والإسقاط في وعاء في حاوية قيد التشغيل لمراقبة البيئة.
  • بينما يدور تشغيل عامل الميناء في حاوية جديدة لرسو السفن ، يتيح لك docker exec -it إرفاقه بحاوية موجودة. استخدم عامل التنفيذ exec-it bash بشكل حر لتصحيح الأخطاء المكسورة.
  • يعرض لك docker ps جميع الحاويات التي تعمل حاليًا ، إلى جانب معرفات الحاويات الخاصة بها. استخدم بالاقتران مع عامل إرساء exec -it containerid123 bash لتصحيح الأخطاء.
  • احصل على الراحة من خيار --verbose. على الرغم من أنه يمكن أن يشعر بالإرهاق ، إلا أنه يمكن أن يساعد في تصحيح العديد من مشكلات تزامن nnarly ، من خلال عامل الإرساء و Docker الخام.
  • الحصول على جيد في قراءة خطوط طويلة سجل. تم اكتشاف معظم الأخطاء التي اكتشفتها من خلال قراءة كميات لا حصر لها من السجلات ومشاهدة ما هو مفقود ، أو لا ينبغي أن تكون موجودة في المقام الأول.

خاتمة

كان Docker أداة رائعة لدعم خط أنابيب CI / CD الذي تم استضافته ذاتيًا. لقد فتح توفيرًا كبيرًا في التكاليف (30٪ +) على الحلول السحابية ، ووفر الوقت الهندسي ، وساعد في توسيع نطاق أعمالنا المتعلقة بالإصدار الهندسي. إنه يحتوي على المراوغات ، ولكن إذا كنت تديرها بفعالية ، فمن الممكن تمامًا الحفاظ على استقرار البنية الأساسية لديك - مع الاستمرار في النوم في الوقت المحدد.

نُشر في الأصل على engineering.gusto.com في 28 نوفمبر 2018.