مقدمة
هناك العديد من المقالات حول تشغيل الحاويات وكتابة
docker-compose.yml . لكن بالنسبة لي لفترة طويلة ، لم يكن السؤال واضحًا حول كيفية المتابعة بشكل صحيح إذا كان يجب ألا تبدأ بعض الحاوية حتى تكون حاوية أخرى جاهزة لمعالجة طلباتها أو إكمال قدر من العمل.
أصبح هذا السؤال ذا صلة بعد أن بدأنا بنشاط في استخدام
عامل الإرساء ، بدلاً من إطلاق عمال أرصفة فرديين.
ما هذا؟
في الواقع ، دع التطبيق الموجود في الحاوية B يعتمد على توفر الخدمة في الحاوية A. وعند بدء التشغيل ، لا يتلقى التطبيق الموجود في الحاوية B هذه الخدمة. ماذا يجب أن تفعل؟
هناك خياران:
- الأول هو الموت (ويفضل أن يكون ذلك مع رمز الخطأ)
- والثاني هو الانتظار ، ثم يموت على أي حال ، إذا لم يستجب التطبيق الموجود في الحاوية B للمهلة المحددة
بعد وفاة الحاوية B ، ستتم إعادة تشغيل
عامل الإرساء (حسب التكوين بالطبع) وسيحاول التطبيق الموجود في الحاوية B مرة أخرى الوصول إلى الخدمة في الحاوية A.
سيستمر هذا حتى تصبح الخدمة في الحاوية A جاهزة للرد على الطلبات ، أو حتى نلاحظ أن الحاوية يتم تحميلها بشكل زائد.
وفي الواقع ، هذه هي الطريقة العادية للهندسة المعمارية متعددة الحاويات.
ولكن ، على وجه الخصوص ، واجهنا موقفًا حيث تبدأ الحاوية A في إعداد وإعداد البيانات الخاصة بالحاوية B. التطبيق في الحاوية B غير قادر على التحقق مما إذا كانت البيانات جاهزة أم لا ، حيث يبدأ العمل معها على الفور. لذلك ، علينا أن نتلقى ونعالج الإشارة حول استعداد البيانات بمفردنا.
أعتقد أنه لا يزال بإمكانك إعطاء بعض حالات الاستخدام. ولكن الأهم من ذلك ، عليك أن تفهم بالضبط لماذا تفعل هذا. خلاف ذلك ، من الأفضل استخدام الأدوات القياسية
لرسو السفن .
قليلا من الأيديولوجية
إذا كنت تقرأ الوثائق بعناية ، فسيتم كتابة كل شيء هناك. وهي كل
الوحدة مستقلة ويجب أن تهتم بجميع الخدمات بها
التي سوف يعمل بها ، متوفرة له.
لذلك ، فإن السؤال هو عدم بدء أو عدم بدء الحاوية ، ولكن إلى
داخل الحاوية ، تحقق من جاهزية جميع الخدمات المطلوبة وفقط
ثم نقل السيطرة على تطبيق الحاوية.
كيف يتم تنفيذها
لحل هذه المشكلة ، ساعدني الوصف الذي قام به
عامل الرصيف كثيرًا ،
هذا الجزء منه
ومقال عن الاستخدام السليم
لنقطة الدخول و
CMD .
إذن ما نحتاج إلى الحصول عليه:
- يوجد ملحق أ نلفه في الحاوية أ
- يبدأ ويبدأ الاستجابة موافق على المنفذ 8000
- وأيضًا ، يوجد التطبيق B ، الذي نبدأ من الحاوية B ، ولكن يجب أن يبدأ العمل في موعد لا يتجاوز التطبيق أ وسيبدأ الاستجابة للطلبات على المنفذ 8000
تقدم الوثائق الرسمية طريقتين لحل هذه المشكلة.
الأول هو كتابة نقطة الدخول الخاصة بك في الحاوية ، والتي ستقوم بإجراء جميع الاختبارات ، ثم تبدأ في تطبيق العمل.
والثاني هو استخدام الملف الدفعي المكتوب بالفعل
wait-for-it.sh .
حاولنا في كلا الاتجاهين.
كتابة نقطة الدخول الخاصة بك
ما هو نقطة الدخول؟
هذا هو الملف القابل للتنفيذ الذي تحدده عند إنشاء الحاوية في
Dockerfile في حقل
ENTRYPOINT . هذا الملف ، كما ذكرنا سابقًا ، يقوم بإجراء عمليات فحص ، ثم يقوم بتشغيل التطبيق الرئيسي للحاوية.
إذن ما حصلنا عليه:
قم
بإنشاء مجلد
Entrypoint .
لديها اثنين من المجلدات الفرعية -
container_A و
container_B . سنقوم بإنشاء الحاويات لدينا فيها.
بالنسبة للحاوية A ، دعنا نلقي خادم http بسيط على بيثون. بعد البدء ، يبدأ في الاستجابة للحصول على الطلبات على المنفذ 8000.
لجعل تجربتنا أكثر وضوحًا ، قمنا بتعيين تأخير لمدة 15 ثانية قبل بدء تشغيل الخادم.
اتضح
ملف عامل التشغيل التالي
للحاوية A :
FROM python:3 EXPOSE 8000 CMD sleep 15 && python3 -m http.server --cgi
بالنسبة للحاوية B ، قم بإنشاء
ملف عامل النقل التالي
للحاوية B :
FROM ubuntu:18.04 RUN apt-get update RUN apt-get install -y curl COPY ./entrypoint.sh /usr/bin/entrypoint.sh ENTRYPOINT [ "entrypoint.sh" ] CMD ["echo", "!!!!!!!! Container_A is available now !!!!!!!!"]
ونضع enterpoint.sh القابل للتنفيذ في نفس المجلد. سيكون لدينا مثل هذا
ما يجري في الحاوية B:
- عندما يبدأ ، يبدأ الإدخال ، أي تطلق enterpoint.sh
- يبدأ entrypoint.sh ، باستخدام curl ، من استقصاء منفذ 8000 للحاوية A. ويقوم بذلك حتى يتلقى استجابة 200 (بمعنى أن curl في هذه الحالة سينتهي بنتيجة صفر وستنتهي الحلقة)
- عند استلام 200 ، تنتهي الحلقة وينتقل التحكم إلى الأمر المحدد في متغير $ cmd . ويشير إلى ما أشرنا إليه في ملف عامل ميناء في حقل CMD ، أي صدى "!!! Container_A متاح الآن !!!!!!!!" لماذا هذا هو الحال ، هو موضح في المقالة أعلاه
- نحن طباعة - !!! Container_A متاح الآن! واختتم.
سنبدأ كل شيء مع
إنشاء عامل ميناء .
عامل ميناء-compose.yml هنا لدينا هذا:
version: '3' networks: waiting_for_conteiner: services: conteiner_a: build: ./conteiner_A container_name: conteiner_a image: conteiner_a restart: unless-stopped networks: - waiting_for_conteiner ports: - 8000:8000 conteiner_b: build: ./conteiner_B container_name: conteiner_b image: waiting_for_conteiner.entrypoint.conteiner_b restart: "no" networks: - waiting_for_conteiner
هنا ، في
conteiner_a ، ليس من الضروري تحديد
المنافذ: 8000: 8000 . تم ذلك من أجل التمكن من التحقق من تشغيل خادم http الذي يعمل به من الخارج.
أيضا ، لا يتم إعادة تشغيل الحاوية B بعد إيقاف التشغيل.
نطلق:
docker-compose up —-build
نرى أنه لمدة 15 ثانية هناك رسالة حول عدم توفر الحاوية A ، ثم
conteiner_b | Conteiner_A is unavailable - sleeping conteiner_b | % Total % Received % Xferd Average Speed Time Time Time Current conteiner_b | Dload Upload Total Spent Left Speed 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> conteiner_b | <html> conteiner_b | <head> conteiner_b | <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> conteiner_b | <title>Directory listing for /</title> conteiner_b | </head> conteiner_b | <body> conteiner_b | <h1>Directory listing for /</h1> conteiner_b | <hr> conteiner_b | <ul> conteiner_b | <li><a href=".dockerenv">.dockerenv</a></li> conteiner_b | <li><a href="bin/">bin/</a></li> conteiner_b | <li><a href="boot/">boot/</a></li> conteiner_b | <li><a href="dev/">dev/</a></li> conteiner_b | <li><a href="etc/">etc/</a></li> conteiner_b | <li><a href="home/">home/</a></li> conteiner_b | <li><a href="lib/">lib/</a></li> conteiner_b | <li><a href="lib64/">lib64/</a></li> conteiner_b | <li><a href="media/">media/</a></li> conteiner_b | <li><a href="mnt/">mnt/</a></li> conteiner_b | <li><a href="opt/">opt/</a></li> conteiner_b | <li><a href="proc/">proc/</a></li> conteiner_b | <li><a href="root/">root/</a></li> conteiner_b | <li><a href="run/">run/</a></li> conteiner_b | <li><a href="sbin/">sbin/</a></li> conteiner_b | <li><a href="srv/">srv/</a></li> conteiner_b | <li><a href="sys/">sys/</a></li> conteiner_b | <li><a href="tmp/">tmp/</a></li> conteiner_b | <li><a href="usr/">usr/</a></li> conteiner_b | <li><a href="var/">var/</a></li> conteiner_b | </ul> conteiner_b | <hr> conteiner_b | </body> conteiner_b | </html> 100 987 100 987 0 0 98700 0 --:--:-- --:--:-- --:--:-- 107k conteiner_b | Conteiner_A is up - executing command conteiner_b | !!!!!!!! Container_A is available now !!!!!!!!
نحصل على إجابة لطلبك ، طباعة
!!! Container_A متاح الآن !!!!!!!! واختتم.
باستخدام wait-for-it.sh
يجدر القول على الفور أن هذا المسار لم ينفع لنا كما هو موضح في الوثائق.
وهي معروفة أنه إذا
تمت كتابة
ENTRYPOINT و
CMD في
Dockerfile ، فعند بدء تشغيل الحاوية ، سيتم تنفيذ الأمر من
ENTRYPOINT ، وسيتم تمرير محتويات
CMD إليها كمعلمات.
من المعروف أيضًا أنه يمكن إعادة تعريف
ENTRYPOINT و
CMD المحددة في
Dockerfile في
عامل ميناء.تنسيق بدء التشغيل
wait-for-it هو كما يلي:
wait-for-it.sh __ -- ___
بعد ذلك ، كما هو موضح في
المقالة ، يمكننا تحديد نقطة
دخول جديدة في
docker - compose.yml ،
وسيتم استبدال
CMD من
Dockerfile .
لذلك ، نحصل على:
ملف عامل الميناء للحاوية A لم يتغير:
FROM python:3 EXPOSE 8000 CMD sleep 15 && python3 -m http.server --cgi
ملف عامل الميناء للحاوية B FROM ubuntu:18.04 COPY ./wait-for-it.sh /usr/bin/wait-for-it.sh CMD ["echo", "!!!!!!!! Container_A is available now !!!!!!!!"]
Docker-compose.yml يشبه هذا:
version: '3' networks: waiting_for_conteiner: services: conteiner_a: build: ./conteiner_A container_name: conteiner_a image: conteiner_a restart: unless-stopped networks: - waiting_for_conteiner ports: - 8000:8000 conteiner_b: build: ./conteiner_B container_name: conteiner_b image: waiting_for_conteiner.wait_for_it.conteiner_b restart: "no" networks: - waiting_for_conteiner entrypoint: ["wait-for-it.sh", "-s" , "-t", "20", "conteiner_a:8000", "--"]
نقوم بتشغيل الأمر
wait-for-it ، ونخبره بالانتظار 20 ثانية حتى تبدأ الحاوية A ، ونحدد معلمة أخرى
"-" ، والتي يجب أن تفصل معلمات
wait-it عن البرنامج الذي سيتم تشغيله بعد اكتماله.
نحن نحاول!
ولسوء الحظ ، لم نحصل على شيء.
إذا تحققنا مع الوسيطات التي نستخدمها ننتظرها ، فسوف نرى أنه فقط
يتم تمرير ما حددناه في نقطة الدخول إليه ، لن يتم إرفاق
CMD من الحاوية.
خيار العمل
ثم ، هناك خيار واحد فقط. ما
حددناه في
CMD في
Dockerfile ، يجب أن ننقل إلى
الأمر في
docker-compose.yml .
بعد ذلك ،
اترك ملف Dockerfile الخاص بالحاوية B بدون تغيير ،
وسيظهر عامل الالتحام. version: '3' networks: waiting_for_conteiner: services: conteiner_a: build: ./conteiner_A container_name: conteiner_a image: conteiner_a restart: unless-stopped networks: - waiting_for_conteiner ports: - 8000:8000 conteiner_b: build: ./conteiner_B container_name: conteiner_b image: waiting_for_conteiner.wait_for_it.conteiner_b restart: "no" networks: - waiting_for_conteiner entrypoint: ["wait-for-it.sh", "-s" ,"-t", "20", "conteiner_a:8000", "--"] command: ["echo", "!!!!!!!! Container_A is available now !!!!!!!!"]
وفي هذا الإصدار ، يعمل.
في الختام ، يجب القول أنه في رأينا ، الطريق الصحيح هو الأول. إنه الأكثر عالمية ويسمح لك بإجراء فحص الجاهزية بأي طريقة ممكنة.
تعتبر ميزة الانتظار مقابل الاستخدام مجرد أداة مفيدة يمكنك استخدامها إما بشكل منفصل أو عن طريق التضمين في
entrypoint.sh .