مدونة عمار الخوالده

التواصل بين الـ Containers عبر الشبكة - Docker Networking


التواصل بين الـ Containers عبر الشبكة - Docker Networking
سلسلة أساسيات Docker

تمهيد

تكلمنا في المقالات السابقة في السلسلة عن الـ Port Binding، وشرحنا امكانية فتح الـ Port بين الحاوية والجهاز الذي نعمل عليه.

ثم شرحنا بعدها التعامل مع الـ Detached Container، وكيف نتعامل مع الحاويات التي تعمل في الخلفية.

في مقدمة هذه السلسلة، ذكرنا أن من فوائد Docker امكانية تشغيل كل أداة أو جزء من المشروع على حاوية مستقلة، بحيث يسهل استبدال وإدارة أي جزء من المشروع، في هذا المقال سنقوم بعمل مشروع PHP يتصل بقاعدة بيانات MariaDB.

تجهيز المشروع

تشغيل حاوية MariaDB

لنقم أولا بتشغيل MariaDB عن طريق الصورة الرسمية لها على Docker Hub :

1docker run --name=project_db -d -e MARIADB_ROOT_PASSWORD=password mariadb

بعد تنفيذ هذا الأمر ستعمل حاوية MariaDB في الخلفية.

الخيار -e أو --env يستخدم لتعيين قيمة الـ Environment Variables داخل الحاوية.

تجهيز مشروع PHP

قمت بإضافة ملف database_test.php وهذا الـ Code الذي تمت كتابته فيه:

 1<?php
 2
 3$host = 'project_db';
 4$username = 'root';
 5$password = 'password';
 6
 7try {
 8    $conn = new PDO("mysql:host=$host", $username, $password);
 9    $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
10
11    echo "Connected successfully\n";
12
13} catch(PDOException $e) {
14    echo 'Connection failed: ' . $e->getMessage();
15}

الـ Code السابق يقوم فقط بالاتصال بقاعدة البيانات، في حال نجح الاتصال سيطبع “Connected Successfully”، أو سيطبع نص الخطأ في حال لم يتم الاتصال.

لاحظ أننا استخدمنا اسم حاوية MariaDB الذي قمنا بتحديده عند إنشاء الحاوية كـ Hostname للاتصال بقاعدة البيانات في مشروع PHP، لكن ما هو السبب؟

عند الاتصال بين الانظمة على الشبكة، نقوم باستخدام الـ IP والـ Port للاتصال، لكن لضمان استمرار عمل الأنظمة حتى في حال تغير الـ IP قد نستخدم الـ Domain Names، أو الـ Host Names.

إذا رغبت بفهم هذا النقطة أكثر وفهم طريقة عمل الـ Domain Names يمكنك قراءة مقال شرح الـ DNS وآلية عمله وبعض تطبيقاته

في حالة Docker، فهو يقوم بإعطاء Internal IP خاص بكل حاوية، لكن من الصعب تحديد هذا الـ IP لأنه يتغير، لذلك يقوم Docker بشكل افتراضي بإعطاء الحاوية Hostname بنفس اسمها.

تشغيل حاوية PHP

لنقم الآن بتشغيل حاوية PHP وتنفيذ الملف الذي كتبناه، يفترض أن تكون النتيجة طباعة “Connected Successfully”:

1docker run -v /home/ammar/project:/app ammardev0/php:8.1 php /app/database_test.php

قم بتعديل الـ Volumes بالمسار الصحيح على جهازك قبل التنفيذ.

ملاحظة: لم أقم باستخدام الصورة الرسمية الخاصة بـ PHP لأن MySQL Extension لم تعد متوفرة بشكل افتراضي ويجب تثبيتها، لذلك قمت باستخدام صورة مخصصة تحتوي على الـ Extensions المطلوبة.

هذه هي نتيجة تنفيذ الأمر:

نتيجة تنفيذ أمر php على Docker ويظهر خطأً في معالجة الـ Hostname

الخطأ Name does not resolve يعني أن الـ DNS Resolver لم يتعرف على هذا الـ Hostname.

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

الشبكات في Docker

توجد العديد من المزايا المتعلقة بالشبكات في Docker، عند إنشاء أي Container دون تحديد خيارات الـ Networking الخاصة بها، يقوم Docker تلقائيا بربط هذه الحاوية بالشبكة الافتراضية (bridge).

في المثال السابق، لم نقم بتحديد أي خيارات متعلقة بالشبكة، فحاوية MariaDB وحاوية PHP تمت إضافتهما إلى شبكة bridge الافتراضية، لكن مع ذلك لم تتمكن حاوية PHP من معالجة الـ Hostname الخاص بحاوية MariaDB.

السبب في ذلك يمكن إيجاده في توثيق Docker: “User-defined bridges provide automatic DNS resolution between containers” - رابط

الشبكة الافتراضية لا توفر DNS Resolution بين الحاويات، لكننا نحتاج الـ DNS لأن الـ IP Addresses قد تتغير، فالحل هنا هو إنشاء Network خاصة وربط الحاويات بها.

بشكل عام وبحسب توثيق Docker، لا ينصح باستخدام الشبكة الافتراضية في بيئات Production لعدة أسباب تم توضيحها في التوثيق

إنشاء شبكة خاصة وربطها بالحاويات

يمكن إنشاء شبكة جديدة باستخدام الأمر الآتي:

1docker network create my-project-network

سنقوم الآن بربط الشبكة التي قمنا بإنشائها بحاوية MariaDB، هذا الأمر يستخدم لربط حاوية موجودة بالفعل بشبكة مخصصة:

docker network connect my-project-network project_db

إذا قمت بتنفيذ الأمر docker inspect project_db ستحصل على الكثير من المعلومات عن الحاوية، ومن بينها الشبكات المرتبطة بها، ستجد أن الحاوية الآن مرتبطة بشبكة bridge الافتراضية وشبكة my-project-network التي قمنا بإنشائها.

بالنسبة لحاوية PHP، فسنقوم بإنشاء حاوية جديدة، الخيار --network يستخدم مع الأمر docker run لتحديد الشبكة الخاصة بالحاوية:

1docker run --network my-project-network -v /home/ammar/project:/app ammardev0/php:8.1 php /app/database_test.php

بما أن الحاويتين يعملان على نفس الشبكة، يفترض أن يعمل الـ DNS Resolving وتعمل الحاوية بشكل سليم، هذه نتيجة تشغيل حاوية PHP:

نتيجة تشغيل حاوية PHP بعد ربطها بالشبكة التي تم إنشاؤها

لاحظ أن الاتصال تم دون عمل Port Binding في أي حاوية، في المقال السابق الذي شغلنا فيه Nginx، أردنا الوصول إلى السيرفر من المتصفح على الـ Host Machine، الـ Port Binding يقوم بتحويل الاتصالات على بورت محدد على الـ Host إلى Port آخر (أو نفس الـ Port) على الحاوية.

بينما في المثال المستخدم في هذا المقال، نحن لم نكن بحاجة لأي اتصال بين الـ Host والحاوية، إنما احتجنا الاتصال بين الحاويات بعضها ببعض.

خلاصة المقال

  • عدم تحديد شبكة للحاوية عند تشغيلها يعني ربط الحاوية بالشبكة الافتراضية لـ Docker.

  • لا ينصح باستخدام الشبكة الافتراضية على الـ Production.

  • الـ DNS Resolving بين الحاويات لا يعمل على الشبكة الافتراضية.

  • الـ Port Binding لا يلزم للاتصال بين الحاويات، فائدته في الاتصال عبر الشبكة بين الحاوية والجهاز الذي يشغل Docker.

  • ملاحظة: إدارة الشبكات في Docker موضوع كبير جدا، وتوجد العديد من أنواع الشبكات إضافة إلى العديد من الخيارات التي يمكن استخدامها، لم يتم شرح الشبكات بالتفصيل لأن المقال ضمن سلسلة أساسيات Docker والهدف منه تزويد القارئ بنبذة سريعة عن الشبكات في Docker،

5 التعامل مع الحاويات التي تعمل في الخلفية - Detached Containers
7 إدارة تشغيل الـ Containers باستخدام Docker Compose

اقرأ أيضا