عند إعادة تشغيل الخادم سنحتاج لإعادة تشغيل حاويات دوكر Docker من أجل ضمان استمرار تشغيل الخدمات داخل هذه الحاويات وتجنب توقفها. في هذا المقال سنوضح ثلاثة طرق مختلفة لبدء تشغيل حاويات دوكر تلقائيًا بعد إعادة تشغيل النظام، حيث ستعتمد الطريقة الأولى والثانية على وجود خدمة دوكر على مستوى الجهاز أي أنها تتطلب وجود عملية dockerd تعمل في الخلفية وتدير الحاويات وإلا سنواجه أخطاء في تطبيقها.
لنشرح الطرق الثلاثة بالتفصيل في الفقرات التالية:
الطريقة الأولى: استخدام خيار --restart always
هذه الطريقة هي الإعداد الافتراضي لتشغيل الحاوية، فعندما نشغل حاوية دوكر باستخدام الأمر docker run
يمكننا تحديد الخيار restart always--
لنقرر تشغيل الحاوية تلقائيًا عند إعادة تشغيل الخادم، أو عند توقف الحاوية لأي سبب كان.
يوضح المثال التالي تشغيل حاوية أباتشي مع تفعيل إعادة التشغيل التلقائي:
docker run -p 8080:80 --name apache -v "${PWD}":/usr/local/apache2/htdocs -d --restart always httpd
هنالك عدة احتمالات ممكنة للخيار restart--
نتحكم من خلالها في كيفية إعادة تشغيل الحاوية تلقائيًا تشمل التالي:
-
restart no--
وهو الإعداد الافتراضي، ويعني عدم تشغيل الحاوية تلقائيًا عند توقف الحاوية لأي سبب كان -
restart always--
إعادة تشغيل الحاوية تلقائيًا بمجرد توقفها، وسيعاد تشغيل جميع الحاويات التي بدأ تشغيلها سابقًا باستخدام هذا الخيار
تحذير: إذا كانت الحاوية تحتوي على خطأ برمجي تسبب في توقفها، فيتسبب هذا الخيار في محاولة إعادة تشغيلها بصورة مستمرة ومتكررة، إلا لو أوقفناها يدويًا باستخدام الأمرdocker stop
فعندها سيتوقف تشغيلها في نفس الجلسة، لكن ستعاد محاولة تشغيلها من جديد عند إعادة تشغيل الخادم -
restart unless-stopped--
يعمل هذا الخيار بشكل مشابه لخيارrestart always--
الفرق هنا هو عدم إعادة تشغيل الحاوية التي أوقفناها يدويًا باستخدامdocker stop
-
restart on-failure--
إعادة تشغيل الحاوية تلقائيًا فقط في حالة توقفها بسبب خطأ ما
يمكننا معرفة سلوك إعادة التشغيل حاوية باستخدام الأمر docker inspect
:
docker inspect <containername> ... "RestartPolicy": { "Name": "always", "MaximumRetryCount": 0 }, ...
لتغيير سلوك إعادة تشغيل الحاوية أثناء تشغيلها بالفعل، يمكننا استخدام الأمر docker update
، على سبيل المثال، إذا كنا نحتاج لتغيير أسلوب إعادة تشغيل حاوية معينة ونريد إعادة تشغيلها فقط في حالة حدوث خطأ في الحاوية سنكتب الأمر التالي:
docker update --restart on-failure <containername>
الطريقة الثانية: استخدام Docker Compose
يمكننا أيضًا تحديد طريقة إعادة التشغيل التلقائي للحاوية من خلال ملف docker-compose.yml باستخدام الكلمة المفتاحية restart كما يلي:
services: db: image: mariadb:latest restart: always
هناك أربعة إعدادات مسموح بها هنا وهي:
- no وهو الخيار الافتراضي ويعني إعادة تشغيل الحاوية يدويًا فقط
- always لإعادة تشغيل الحاوية دائمًا
- unless-stopped لإعادة تشغيل الحاوية في كافة الأحوال، إلا في حال أوقفناها يدويًا
- on-failure لإعادة تشغيل الحاوية فقط في حال توقفت نتيجة لحدوث خطأ
تتشابه دلالة الخيارات السابقة مع الخيارات المشروحة في الطريقة الأولى التي تستخدم الخيار restart--
مع الأمر docker run
، لكن هنا سيكون المعامل restart: always
ساري المفعول حتى نوقف الحاويات الموجودة في الملف docker-compose.yml
ونحذفها يدويًا باستخدام الأمر docker-compose down
.
ملاحظة: ينبغي التأكد من تحديد الكلمة المفتاحية restart
في المستوى الصحيح داخل إعدادات الحاوية المعنية وهي db
في المثال.
services: db: image: mariadb:latest volumes: - vol-db:/var/lib/mysql environment: MYSQL_USER: wpuser MYSQL_PASSWORD: password restart: always
الطريقة الثالثة: إعادة تشغيل الحاوية باستخدام systemd
الطريقة الأساسية لبدء تشغيل حاويات Docker هي عن باستخدام الأمر docker run
والذي يسمح لنا بإنشاء حاوية من صورة دوكر Docker image وتشغيلها، لكن هناك مشكلة في هذه الطريقة ففي حال حدوث أي مشكلة تتسبب في توقف الخادم أو إعادة تشغيله فستتوقف الحاوية التي شغلناها عبر هذا الأمر ولن يعاد تشغيلها تلقائيًا، مما يؤدي إلى توقف الخدمة و التطبيق الذي توفره.
يمكننا حل هذه المشكلة وضمان استمرار عمل الحاوية بتحويل الأمر docker run
إلى خدمة نظام لضمان مواصلة عملها بعد إعادة تشغيل الخادم، فالحاوية في هذه الحالة ستكون جزءًا من خدمة وبالتالي يمكن تشغيلها تلقائيًا عند إقلاع الخادم، وإعادة تشغيلها تلقائيًا إذا توقفت لأي سبب كان.
يمكن تحقيق ذلك باستخدام مدير الخدمات systemd، كما يمكننا استخدام supervisord أيضًا أو أي نظام مشابه آخر لإدارة الخدمات، ولكننا اخترنا systemd في مقالنا الحالي لكونه الأكثر استخدمًا وهو معتمد في معظم توزيعات لينكس.
سنغلف حاوية دوكر في خدمة systemd، كي نتمكن من إدارة هذه الحاوية بنفس الطريقة التي ندير بها التطبيقات والخدمات الأخرى على جهازنا، وضمان الأمور التالية:
- إمكانية بدء الخدمة بشكل صحيح عند تشغيل الجهاز
-
إمكانية إعادة تشغيل الحاوية تلقائيًا في حال توقفت لأي سبب كان باستخدام إعداد
Restart=always
في ملف خدمة systemd - سهولة مراجعة وإدارة سجلات الحاوية stdout بواسطة نظام تسجيل السجلات Journald
- استخدام كامل نظام systemd لإدارة المتطلبات كالتبعيات والأوامر التي يجب أن تنفذ قبل وبعد تشغيل الحاوية وتعريف متغيرات البيئة التي تحتاجها الحاوية بكفاءة
يعرض الكود التالي الحد الأدنى لمحتوى ملف خدمة systemd لتشغيل حاوية دوكر تحتوي على خادم nginx والموجود في المسار /etc/systemd/system/nginx.service
:
[Unit] Description=Docker container [Service] ExecStart=/usr/bin/docker run --name nginx \ --net host \ -v /srv/nginx/conf.d:/etc/nginx/conf.d \ -v /srv/nginx/index.html:/usr/share/nginx/html/index.html \ nginx ExecStop=/usr/bin/docker stop nginx ExecStopPost=/usr/bin/docker rm -f nginx [Install] WantedBy=multi-user.target
يحدد هذا الملف الأمور التالية:
- تشغيل الحاوية التي تحتوي على خادم nginx تلقائيًا عند تشغيل النظام
- إيقاف الحاوية بشكل صحيح عند إيقاف الخدمة
- حذف الحاوية بعد إيقافها
- تشغيل الخدمة تلقائيًا عند تشغيل النظام ووصوله لمرحلة multi-user target
لكن هذه الطريقة لها بعض السلبيات، ونحتاج لإجراء بعض التعديلات على ملف الخدمة لضمان عمله بشكل صحيح، فهنا بدأنا تشغيل حاوية Docker باسم nginx، وسحبنا أحدث صورة nginx
من المستودع الرسمي مع تمرير مجلد الإعدادات على شكل volume داخل الحاوية،وفي حال لم يكن موجودًا ستنشئه حاوية دوكر على الخادم.
سيعمل جزء WantedBy
فقط عند تنفيذ الأمر systemctl enable nginx
، وفي هذه الحالة ستفعَّل الخدمة عند بدء التشغيل لأنها ستكون مرتبطة بالوصول لمرحلة multi-user target في systemd، هذا يعني أنه عند تشغيل النظام، ستفعّل الخدمة تلقائيًا.
الحل هو بإضافة الأسطر BindsTo
و After
في ملف الخدمة لضمان أن الخدمة تعتمد على دوكر. بمعنى آخر، إذا توقفت خدمة دوكر أو لم تكن تعمل بعد، فإن هذه الخدمة لن تبدأ بالعمل إلا بعد أن نضمن تشغيل دوكر بنجاح، وسيجري إيقافها أولًا إذا توقفت حاوية دوكر في أي وقت.
أيضًا، في حالتنا nginx التي هي خدمة عديمة الحالة stateless أي أنها لا تحفظ حالتها بين عمليات التشغيل المختلفة، لذا سيكون من الأفضل تحديث صورة دوكر الخاصة بالخدمة لأحدث إصدار متاح كلما شغلنا هذه الخدمة لنضمن بأنها تعمل بأحدث نسخة من الحاوية، ولتحقيق سنضيف أمر ExecStartPre
المسؤول عن تنفيذ docker pull
كي يعمل قبل الأمر ExecStart
.
كما يمكننا فرض إعادة تشغيل الخدمة دائمًا في حال تعرضت لخطأ، وذلك كل 10 ثوانٍ مثلًا، من خلال الأسطر Restart
و RestartSec
والتي تشابه في تأثيرها الخيار restart=on-failure
في أمر docker run
الذي شرحناه في الطريقة الأولى.
أخيرًا هناك مشكلة أخرى سنواجهها في حال استخدمنا systemd لإدارة الحاويات. فعند تشغيل خدمة حاوية دوكر عبر systemd، سنحتاج لتحديد سلوك إعادة التشغيل يدويًا عند إعادة تشغيل الخدمة أو توقفها لأن systemd يفترض بأننا نرغب في الاحتفاظ بالأسلوب المستخدم لإعادة التشغيل، حيث سيوقف الخدمة أولاً ثم يعيد تشغيلها مرة أخرى.
لحل هذه المشكلة يمكن استخدام حل بسيط وهو استخدام الأمر ExecReload
لإعادة تحميل عملية nginx داخل الحاوية فهذا يساعدنا عند الحاجة لتحديث إعدادات nginx أو إعادة تحميلها دون توقف الخدمة بالكامل، كما يمكننا استخدام حل بديل وهو إرسال إشارة باستخدام الأمر kill -s HUP
، لنخبر nginx بأن يعيد تحميل إعداداته دون إيقافه بالكامل.
أخيرًا، يمكننا تغيير الأسماء المستخدمة في ملف الخدمة باستخدام متغيرات البيئة في السطر Environment
. وفي حال وجدنا أن المتغيرات أصبحت كثيرة أو كانت مشتركة بين عدة ملفات خدمة على نفس الخادم، فيمكن استخدام ملف منفصل للمتغيرات باستخدام الخيار EnvironmentFile
.
فيما يلي ملف الخدمة بعد إجراء التحسينات السابقة:
[Unit] Description=Docker container BindsTo=docker.service After=docker.service [Service] Environment=NAME=%N Environment=IMG=nginx Restart=on-failure RestartSec=10 ExecStartPre=-/usr/bin/docker kill ${NAME} ExecStartPre=-/usr/bin/docker rm ${NAME} ExecStart=/usr/bin/docker run --name ${NAME} \ -p 80:80 \ -p 443:443 \ -v /srv/nginx/conf.d:/etc/nginx/conf.d \ -v /srv/nginx/html:/usr/share/nginx/html/ \ ${IMG} ExecStop=/usr/bin/docker stop ${NAME} ExecReload=/usr/bin/docker exec ${NAME} nginx -s reload [Install] WantedBy=multi-user.target
ملاحظة: يجري استبدال N%
تلقائيًا باسم ملف الخدمة، وعلينا أن لا ننسى تشغيل الأمر systemctl daemon-reload
في كل مرة نغير فيها إعدادات ملف الخدمة.
مراقبة سجلات الخدمة
عند تشغيل الخدمات باستخدام systemd مثل خدمة nginx في المثال السابق، ستوجه السجلات التي تنتج عن الخدمة إلى سجلات النظام journald، وهذا يسهل علينا الوصول لها وتحليلها.
على سبيل المثال، يمكننا الوصول إلى سجلات nginx ببساطة لمراقبتها وعرضها في الزمن الحقيقي من خلال الأمر التالي:
journalctl -fu nginx
لا يمنعنا هذا الأسلوب من الحصول على ملفات سجلات منفصلة، فقد نحتاج لذلك كما في حالة استخدام virtualhosts لإدارة عدة مواقع ويب على نفس الخادم باستخدام nginx، في هذه الحالة سيكون من الأنسب لنا مشاركة مجلد السجلات الخاص بالحاوية وهو بشكل افتراضي /var/log/nginx
مع جهاز المضيف حتى لا نفقدها عند إعادة التشغيل.
الخاتمة
تعرفنا في هذا المقال على ثلاث طرق مختلفة لإعادة تشغيل حاويات دوكر تلقائيًا بعد إعادة تشغيل الخادم، يعتمد اختيار الطريقة المناسبة على البئية ومتطلبات العمل، ففي الحالات البسيطة وإعدادات الحاوية الواحدة قد تكون طريقة restart--
في أمر docker run
أو طريقة Docker Compose كافية. لكن في البيئات المعقدة التي تتضمن عدة خدمات ستوفر لنا طريقة systemd تحكمًا أكبر بالحاوية.
ترجمة -وبتصرف- للمقال Restart Docker Container Automatically After Reboot لكاتبه Umair Khurshid
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.