Dumb-Init هو مشرف عملية بسيط ونظام init مصمم لتشغيله كـ PID 1 داخل بيئات الحاويات الحد الأدنى (مثل Docker). يتم نشره باعتباره ثنائيًا صغيرًا مرتبطًا بشكل ثابت في C.
شجعت الحاويات الخفيفة على فكرة تشغيل عملية أو خدمة واحدة دون أنظمة init عادية مثل SystemD أو Sysvinit. ومع ذلك ، فإن حذف نظام init غالبًا ما يؤدي إلى معالجة غير صحيحة للعمليات والإشارات ، ويمكن أن يؤدي إلى مشاكل مثل الحاويات التي لا يمكن إيقافها بأمان ، أو تتسرب الحاويات التي كان ينبغي تدميرها.
يمكّنك dumb-init من مجرد بادئة أمرك مع dumb-init . إنه بمثابة PID 1 ويولد قيادتك على الفور كعملية طفل ، مع الحرص على التعامل مع الإشارات وإعادة التوجيه بشكل صحيح عند استلامها.
عادة ، عند تشغيل حاوية Docker ، تصبح العملية التي تنفذها PID 1 ، مما يمنحها المراوغات والمسؤوليات التي تأتي مع كونها نظام init للحاوية.
هناك مسألتان شائعان يطرحهما:
في معظم الحالات ، لن يتم التعامل مع الإشارات بشكل صحيح.
يطبق Kernel Linux معالجة الإشارات الخاصة على العمليات التي تعمل على أنها PID 1.
عندما يتم إرسال العمليات إشارة على نظام Linux العادي ، ستقوم kernel أولاً بالتحقق من أي معالجات مخصصة قامت بتسجيل العملية لتلك الإشارة ، وإعادة العودة إلى السلوك الافتراضي (على سبيل المثال ، قتل العملية على SIGTERM ).
ومع ذلك ، إذا كانت العملية التي تتلقى الإشارة هي PID 1 ، فإنها تحصل على علاج خاص بواسطة النواة ؛ إذا لم يتم تسجيل معالج للإشارة ، فلن يتراجع kernel إلى السلوك الافتراضي ، ولا يحدث شيء. بمعنى آخر ، إذا لم تتعامل العملية الخاصة بك بشكل صريح ، فلن يكون SIGTERM أي تأثير على الإطلاق.
ومن الأمثلة الشائعة على CI Jobs التي تقوم docker run my-container script : إرسال SIGTERM إلى عملية docker run عادةً ما يقتل أمر docker run ، ولكن اترك الحاوية تعمل في الخلفية.
لا يتم جني عمليات الزومبي اليتيم بشكل صحيح.
تصبح العملية غيبوبة عندما تخرج ، وتظل غيبوبة حتى يستدعي الوالد بعض الاختلافات في نظام wait() استدعاءه. يبقى في جدول العملية كعملية "مندودة". عادة ، ستستدعي عملية الوالدين wait() على الفور وتجنب الزومبي الطويلة المعيشة.
في حالة خروج أحد الوالدين أمام طفله ، يكون الطفل "يتيمًا" ، ويتم إعادة عرضه تحت PID 1. وبالتالي فإن نظام init مسؤول عن wait() -ing على عمليات الزومبي اليتيم.
بطبيعة الحال ، لن wait() على العمليات العشوائية التي تصادفها مرتبطة بها ، لذلك غالبًا ما تنتهي الحاويات بعشرات من الزومبي المتجذرة في PID 1.
dumb-init يعمل dumb-init في PID 1 ، ويتصرف مثل نظام init بسيط. إنه يطلق عملية واحدة ، ثم تلقى جميع الوكلاء إشارات إلى جلسة متجذرة في عملية الطفل هذه.
نظرًا لأن عمليتك الفعلية لم تعد PID 1 ، عندما تتلقى إشارات من dumb-init ، سيتم تطبيق معالجات الإشارات الافتراضية ، وستتصرف العملية كما تتوقع. إذا ماتت العملية الخاصة بك ، فسوف تموت dumb-init أيضًا ، مع الحرص على تنظيف أي عمليات أخرى قد تبقى.
في وضعه الافتراضي ، تنشئ dumb-init جلسة متجذرة على الطفل ، ويرسل إشارات إلى مجموعة العمليات بأكملها. هذا مفيد إذا كان لديك طفل سيئ للغاية (مثل نص Shell) الذي لن يشير عادةً إلى أطفاله قبل الموت.
يمكن أن يكون هذا في الواقع مفيدًا خارج حاويات Docker في مشرفي العمليات العادية مثل Daemontools أو Supervisord للإشراف على البرامج النصية Shell. عادة ، لا يتم إعادة توجيه إشارة مثل SIGTERM التي يتلقاها قذيفة إلى عمليات التبريد الفرعي ؛ بدلاً من ذلك ، تموت عملية الصدفة فقط. مع غبية ، يمكنك فقط كتابة نصوص الصدفة مع غبي في Shebang:
#!/usr/bin/dumb-init /bin/sh
my-web-server & # launch a process in the background
my-other-server # launch another process in the foreground
عادةً ما ، من شأنه أن يقتل SIGTERM القذيفة ولكن ترك تلك العمليات تعمل (الخلفية والمقدمة!). مع غبية ، ستتلقى عمليات الدفع الفرعية نفس الإشارات التي تقوم بها قذيفة.
إذا كنت ترغب في إرسال الإشارات فقط إلى الطفل المباشر ، فيمكنك الركض باستخدام الوسيطة-- --single-child ، أو تعيين البيئة DUMB_INIT_SETSID=0 عند تشغيل dumb-init . في هذا الوضع ، يكون الغموض الشفاف تمامًا ؛ يمكنك حتى توصيل متعددة معًا (مثل dumb-init dumb-init echo 'oh, hi' ).
يسمح غبية الوفرة بإعادة كتابة الإشارات الواردة قبل فرضها. هذا مفيد في الحالات التي يكون لديك فيها مشرف Docker (مثل Mesos أو Kubernetes) الذي يرسل دائمًا إشارة قياسية (مثل Sigterm). تتطلب بعض التطبيقات إشارة توقف مختلفة من أجل القيام بتنظيف رشيق.
على سبيل المثال ، لإعادة كتابة الإشارة sigterm (رقم 15) إلى sigquit (رقم 3) ، فقط أضف --rewrite 15:3 على سطر الأوامر.
لإسقاط إشارة تمامًا ، يمكنك إعادة كتابتها إلى الرقم الخاص 0 الخاص.
عند التشغيل في وضع SetSID ، لا يكفي إعادة توجيه SIGTSTP / SIGTTIN / SIGTTOU في معظم الحالات ، لأنه إذا لم تكن العملية قد أضافت معالج إشارة مخصص لهذه الإشارات ، فلن يطبق kernel سلوك معالجة الإشارات الافتراضية (والذي سيعلق العملية) نظرًا لأنها عضو في مجموعة عملية أوال. لهذا السبب ، قمنا بتعيين إعادة كتابة الافتراضية على SIGSTOP من تلك الإشارات الثلاثة. يمكنك إلغاء الاشتراك من هذا السلوك عن طريق إعادة كتابة الإشارات إلى قيمها الأصلية ، إذا رغبت في ذلك.
تحذير واحد مع هذه الميزة: بالنسبة لإشارات التحكم في الوظائف ( SIGTSTP ، SIGTTIN ، SIGTTOU ) ، ستعلق الوصلات الغبية نفسها دائمًا بعد تلقي الإشارة ، حتى لو قمت بإعادة كتابتها إلى شيء آخر.
لديك بعض الخيارات لاستخدام dumb-init :
تحتوي العديد من توزيعات Linux الشهيرة (بما في ذلك Debian (منذ stretch ) ومشتقات Debian مثل Ubuntu (منذ bionic )) الآن على حزم غبية في مستودعاتها الرسمية.
على التوزيعات المستندة إلى Debian ، يمكنك تشغيل apt install dumb-init لتثبيت غبي ، تمامًا مثل تثبيت أي حزمة أخرى.
ملاحظة: لا ترتبط معظم الإصدارات التي توفرها التوزيعات من الغباء بشكل ثابت ، على عكس الإصدارات التي نقدمها (انظر الخيارات الأخرى أدناه). عادة ما يكون هذا جيدًا تمامًا ، ولكن يعني أن هذه الإصدارات من الغموض الغبي عمومًا لن تعمل عند نسخها إلى توزيعات Linux الأخرى ، على عكس الإصدارات المرتبطة بشكل ثابت الذي نقدمه.
إذا كان لديك خادم apt داخلي ، فإن تحميل .deb إلى الخادم الخاص بك هو الطريقة الموصى بها لاستخدام dumb-init . في DockerFiles ، يمكنك ببساطة apt install dumb-init وسيكون متاحًا.
تتوفر حزم Debian من علامة التبويب GitHub Remease ، أو يمكنك تشغيل make builddeb بنفسك.
.deb يدويًا (Debian/Ubuntu) إذا لم يكن لديك خادم apt داخلي ، فيمكنك استخدام dpkg -i لتثبيت حزمة .deb . يمكنك اختيار كيفية الحصول على .deb على الحاوية الخاصة بك (تركيب دليل أو wget -ing هو بعض الخيارات).
أحد الاحتمالات هو مع الأوامر التالية في Dockerfile:
RUN wget https://github.com/Yelp/dumb-init/releases/download/v1.2.5/dumb-init_1.2.5_amd64.deb
RUN dpkg -i dumb-init_*.debنظرًا لأنه يتم إصدار غبي الثنائي باعتباره ثنائيًا مرتبطًا بشكل ثابت ، فيمكنك عادةً أن تضغط على صورك. إليك مثال على القيام بذلك في Dockerfile:
RUN wget -O /usr/local/bin/dumb-init https://github.com/Yelp/dumb-init/releases/download/v1.2.5/dumb-init_1.2.5_x86_64
RUN chmod +x /usr/local/bin/dumb-init على الرغم من أن dumb-init المكتوبة بالكامل في C ، إلا أننا نقدم أيضًا حزمة Python التي تجمع وتثبيت الثنائي. يمكن تثبيته من PYPI باستخدام pip . ستحتاج أولاً إلى تثبيت برنامج التحويل البرمجي C (على Debian/Ubuntu ، يعد apt-get install gcc كافيًا) ، ثم مجرد pip install dumb-init .
اعتبارًا من 1.2.0 ، تتوفر الحزمة في PYPI كأرشيف للعجلات مسبقًا ولا تحتاج إلى تجميعها على توزيعات Linux الشائعة.
بمجرد التثبيت داخل حاوية Docker الخاصة بك ، ما عليك سوى بادئة الأوامر الخاصة بك باستخدام dumb-init (وتأكد من أنك تستخدم بناء جملة JSON الموصى به).
ضمن Dockerfile ، من الممارسات الجيدة استخدام غبي كنقطة دخول الحاوية الخاصة بك. "نقطة الإدخال" هي أمر جزئي يحصل مسبقًا على تعليمات CMD الخاصة بك ، مما يجعله مناسبًا جدًا للبكم:
# Runs "/usr/bin/dumb-init -- /my/script --with --args"
ENTRYPOINT [ "/usr/bin/dumb-init" , "--" ]
# or if you use --rewrite or other cli flags
# ENTRYPOINT ["dumb-init", "--rewrite", "2:3", "--"]
CMD [ "/my/script" , "--with" , "--args" ] إذا أعلنت نقطة إدخال في صورة أساسية ، فإن أي صور تنحدر منها لا تحتاج أيضًا إلى إعلان الوفرة الغبية. يمكنهم فقط تعيين CMD كالمعتاد.
للاستخدام التفاعلي لمرة واحدة ، يمكنك فقط تقديمه يدويًا:
$ docker run my_container dumb-init python -c 'while True: pass'
إن تشغيل هذا الأمر نفسه دون dumb-init قد يؤدي إلى عدم القدرة على إيقاف الحاوية دون SIGKILL ، ولكن مع dumb-init ، يمكنك إرسال المزيد من الإشارات إنسانية مثل SIGTERM .
من المهم أن تستخدم بناء جملة JSON لـ CMD و ENTRYPOINT . خلاف ذلك ، يستدعي Docker قذيفة لتشغيل الأمر الخاص بك ، مما أدى إلى قذيفة كـ PID 1 بدلاً من الوفرة الغبية.
غالبًا ما تريد الحاويات القيام ببعض الأعمال المسبقة التي لا يمكن القيام بها أثناء وقت البناء. على سبيل المثال ، قد ترغب في تحديد بعض ملفات التكوين بناءً على متغيرات البيئة.
أفضل طريقة لدمج ذلك مع غبية مثل هذا:
ENTRYPOINT [ "/usr/bin/dumb-init" , "--" ]
CMD [ "bash" , "-c" , "do-some-pre-start-thing && exec my-server" ]من خلال الاستمرار في استخدام غبية كنقطة دخول ، لديك دائمًا نظام init مناسب في مكانه.
يعد الجزء exec من أمر Bash مهمًا لأنه يحل محل عملية Bash مع الخادم الخاص بك ، بحيث لا توجد قذيفة إلا للحظات في البداية.
يتطلب بناء الثنائي الغبي من بروم البرامج المترجمة ورؤوس LIBC والافتراضات إلى GLIBC.
$ make
يتجاوز عددها مكون من 700 كيلو بايت المترجمة بشكل ثابت بسبب GLIBC ، لكن Musl أصبح الآن خيارًا. على Debian/Ubuntu apt-get install musl-tools
$ CC=musl-gcc make
عندما يتم تجميعها بشكل ثابت مع Musl ، يكون الحجم الثنائي حوالي 20 كيلو بايت.
نستخدم اتفاقيات Debian القياسية لتحديد تبعيات البناء (انظر في debian/control ). تتمثل إحدى الطرق السهلة للبدء apt-get install build-essential devscripts equivs ، ثم sudo mk-build-deps -i --remove لتثبيت جميع تبعيات البناء المفقودة تلقائيًا. يمكنك بعد ذلك استخدام make builddeb لبناء حزم دبيان غبية.
إذا كنت تفضل بناء حزمة Debian الآلية باستخدام Docker ، فما عليك سوى تشغيل make builddeb-docker . هذا أسهل ، ولكن يتطلب منك تشغيل Docker على جهازك.