ماهو الـDocker ؟
مقدمة
كما نعرف بأن سفن الشحن هي احدى الطرق الرئيسية لتوزيع البضائع حول العالم. قديما, تكلفة نقل البضائع عبر تلك السفن مكلفة نوعاما لان كل شحنة لديها خصائص تختلف عن الشحنات المرافقه لها على نفس السفينه. فمثلا, شحن صندوق من السمك عبر المحيطات يحتاج رعاية خاصه من ناحية التبريد وطريقة التحميل والتنزيل كما انه يحتاج نوع مخصص من الكرينات ( الرافعات), وعليه فلابد ان تكون هناك سفن مخصصه لذلك. بينما في حالة شحن سياره عبر المحيطات فلا يحتاج نفس العناية بصندوق السمك, بل يحتاج نوع مخصص من الرافعات وكذلك عناية خاصه بشحن السيارات, وبالتالي يحتاج سفينه مخصصه لذلك. وقياسا على الأمثله السابقه, كل نوع من الشحنات يحتاج عنايه خاصه لابد ان تتوفر له سفينه مخصصه. وهذا مكلف جدا. اذا ماهو الحل؟
الحاويات (containers), هي الحل لمشكلة التنوع في الشحنات. الحاويات لها شكل واحد وتختلف في الخصائص, فمثلا حاويات الأسماك هي نفسها حاويات السيارات من ناحية الشكل ولاكن تختلف من ناحية الخصائص. فمثلا حاويات الأسماك تكون مزوده بأدوات تبريد, مضادات للبكتيريا, وغيرها. بينما في حاويات السيارات لانحتاج مثل تلك الخصائص. العامل المشترك بين تلك الحاويات هو الشكل مما يساعد على شحنها في سفينة شحن واحده, وايضا الية التحميل والتنزيل موحده (الرافعات ليست مخصصه لكل حاويه, بل تستطيع العمل على جميع الحاويات).
نفس المفهوم يحدث في عالم تطوير البرمجيات. دوكر (Docker) اصبح اداة توصيل البرامج بشكل عام بغض النظر عن هيكلة تلك البرامج أو اعتماد البرامج على برمجيات اخرى (dependencies) أو طريقة تثبيتها.
بدية المشكله ومقترح الحل لها
الـDocker هو مشروع مفتوح المصدر تمت برمجته باستخدام لغة الـGo , لغة برمجه من انتاج شركة Google. وأصبح الـ Docker من اكبر المشاريع مفتوحة المصدر في الوقت الحالي.
إذا ماهي المشكلة الكبيرة التي من أجلها الـ Docker اتى ليقوم بحلها في عالم هندسة البرمجيات (تطوير البرامج)؟ فمثلا, لو نظرنا للتطبيق في الصورة التالي, تطبيق يعتمد على لغة الجافا كلغة برمجه, ويعتمد على الـMySQL كقاعدة بيانات , ويستخدم قاعدة بيانات للذاكرة مثل redis. اذا لدينا ثلاث مكونات من التقنيات المختلفة التي نستخدمها لبناء التطبيق. الان, لنفترض اننا نريد نعمل Deployment للتطبيق بعد الانتهاء منه على خادم (server) وليكن هذا الـServer يعمل عليه نظام Linux ولكي يعمل التطبيق على هذا الخادم, فلابد ان تتوفر جميع التقنيات المستخدمة في التطبيق على السيرفر لكي يعمل بالشكل الصحيح, فمثلا لابد ان تكون التقنيات التي تخص الـjava مثل الـ maven, Gradel وغيرها متوفره, وأيضا لو كان التطبيقي يعمل على الويب فلابد ان يتوفر الـweb server مثل الـApache Webserver وأيضا الـMySQL server لتشغيل قواعد البيانات الخاصة بالتطبيق وهكذ. وأيضا نقطه مهمه هي الإصدارات للتقنيات هذه لابد ان تكون متوافقة مع الإصدارات المستخدمة في التطبيق لكي لا يحصل تعارض ونحافظ على الـcompatibility (مثلا Java 5 تختلف في إصدارها عن Java 8). أي بمعنى لابد ان يحتوي السيرفر على جميع الـDependencies والـFrameworks التي يحتاجها التطبيق الخاص بي لكي يعمل بالشكل الصحيح.
ولك ان تتخيل كم من الجهد المطلوب لعمل كل الـconfiguration للسيرفر لكي يتوافق مع التقيات المستخدمة في التطبيق ولكي يعمل بالشكل الصحيح. كل هذا العمل مجهد ومستهلك للوقت.
ومع تطور تطوير البرمجيات ومفاهيم هندسة البرمجيات اصبح تطوير البرمجيات يحتاج عمل وجهد كبير جدا وأيضا التطبيقات المستهدفة ذات حجم كبير جدا وقد تحتوي على ما يسمى مايكرو سيرفيس (micro-services) وكل micro-service يعمل على نسخه معينه عن الـmicro-service الاخر وجميعها لابد ان يكونو على نفس الخادم, وهذا يخلق مشكله توافر النسخ المتعدده وتعارضها مع بعضا البعض لعدم وجود الـisolation بينهم (او مانسميع بالعزل).
الحل لكل هذه المشاكل, هو الـ Containerisation. فمثلا كما هو مبين في الصورة التأليه, كل مكون من مكونات البرنامج عباره عن image (صورة) فمثلا, الـjava وجميع ما يتعلق بها من تقنيات (libraries, frameworks, etc.) انا احتاجها لبرمجة التطبيق معمول لها packaging داخل هذه الـimage وبنفس الطريقه لـMySQL ولـ redis, ولكي نعمل الان deployment للتطبيق على الخادم فقط نحتاج الـDocker محمل على السيرفر واللذي بدوره يقوم بتشغيل جميع الـ images فقط من دون الاهتمام بأي تقنية كل image تعمل وكيف تعمل ومال الى اخره. والسبب بأن الـ images انشأ لها instances وهذه الـinstances تسمى الـcontainers وممكن ننشأ اكثر من instances من image وحده (مشابه لمفهوم الـobjects من الـclass في البرمجه الشيئية Object-oriented programming). وبالتالي اصبح التطبيق بإمكانه العمل على الخادم بشكل فعال بحيث كل مكون من التطبيق عباره عن image ومعزول عن الـ images الأخرى في الخادم, وبالتالي لا اهتم بمشكله الـcompatibility التي ذكرتها سابقا.
في نهاية المطاف, كل container الان في السيرفر يشترك في نفس الـkernel لنظام التشغيل الموجود على السيرفر مع الـcontainers الأخرى. طبعا لكي يشتغل الـDocker لابد ان يكون نظام التشغيل Linux, ولمن يتسأل ماذا عن الـWindows؟ نفس الطريقه ولاكن يحتاج تشتغل على Virtual Machine مثل VMware ومن ثم تعمل install للـDocker.
اذا بهذه الطريقه, قمنا بإجاد حل لمشكلة الـdeployment اللتي عرضناها سابقا.
قد يسأل شخص, ما لفرق بين الـContainerisation و الـVirtualisation؟
في حالة الـVirtualisation, اي اننا نستخدم الـvirtual tools المعروفه مثل VirtualBox و VMware, وغيرها, نحتاج لكل مكون (component) نصعنه داخل virtual box نظام تشغيل خاص فيه, وذلك لكي نحقق مفهوم الـisolation اللتي ذكرناها سابقا بحيث تكون كل مكون معزوله عن الأخر.
ولاكن في حالة الـContainerisation, لانحتاج نظام تشغيل لكل container مثل ماعملنا مع الـvirtualization, بل أن جميع الـcontainers تتشارك في نفس الـkernel المستخدم لنظام تشغيل الـserver. فقط نحتاج لنظام الـDocker كطبقه ترتيب المهام لعمل الـcontainers.
وكما هو واضح, الـvirtualization يحتاج resources كثير, لان كل نظام تشيغل في كل virtual box مثلا يحتاج إلى حيز في الذاكره, والمعالج والـhard drive, وهذا يستهلك وقت اكثر في عملية الـbooting (التشغيل). وكل هذه المشاكل نستطيع ان نقول ان الـ الـcontainerisation تتغلب عليها, لاننا نعمل على ذاكره واحده, ونظام تشغيل واحد, وكذالك نستخدم او نتشارك في نفس الـresources, وبالتالي, سرعة الأداء وكفاءة العمل على الـDocker تتفوق.
لاكن هناك ميزه يجب ان لا نغفل عنها وتميز الـvirtualization عن الـcontainerisation الا وهي الـsecurity. لماذا؟ كما ذكرنا, بأنه في الـcontainerization جميع الحاويات تشترك على نفس الكيرنل لنظام التشغيل على السيرفر, اي انها تعمل على نظام التشغيل الأساسي للسيرفر, فلو حصل ان يكون هناك ثغره في نظام التشغيل, الخاص بالسيرفر, فمعناه ستكون الثغره مؤثره على جميع الكونتينرز اللتي تعمل على السيرفر. لاكن في الـvirtualization, كل كوبوننت لها الـsecurity configuration الخاص بها, لانها لا تتشارك في نفس نظام التشغيل بعكس الـcontainerization, وبالتالي, اي ثغره على نظام التشغيل على السيرفر لن يؤثر على اي من الـvirtual boxes لانها منعزله بنظام تشغيل مستقل بها.
في الجزء القادم سوف نتكلم بشيء من التفصيل عن الـDocker engine (architecture).
Docker Engine أو محرك نظام الـDocker
الـDocker engine مبني على مفهوم الـ client-server architecture ويتكون ثلاث مكونات (components) رئيسية كالتالي:
- Docker daemon: هو القلب المحرك للـDocker او الـcode المحرك للنظام, وهو مسؤول عن الـDocker objects مثل images, containers, networks وvolumes. من ناحية إضافة، وتعديل, وحذف وغيرها.
- REpresentation State Transfer Application Program Interface (REST API): الهدف الأساسي من الـREST API في الـ Docker هو توفير الية التخاطب للطلبات المرسلة لـ Docker daemon وهي الـhttp requests بين الـclients و الـserver وتحديد الية التعامل معها.
- Command line interface client (CLI): الـCLI تستخدم الـDocker REST API للتحكم والتعامل مع الـDocker daemon من خلال الأوامر عن طريق الـcommand line او بإستخدام الـscripts.
لأكثر تفاصيل عن الـDocker architecture انصح بالرجوع إلى Docker ِِArchitecture Documentation.
بعد ان اطلعنا على مقدمه بسيطه عن الـDocker ومما يتكون وكيفية عمله نظريا. ننتقل في الجزء القادم إلى التطبيق العملي وكيفية استخدام الـDocker وانشاء الـimages وطريقة نشرها ومشاركتها. سوف نتعرف على الـDocker hub, الـregistry , كيفية انشاء الـimages وتشغيل الـcontainer وكذلك نشر الـimages على منصة الـDocker hub.
ماهو الـDocker Hub
الـDocker Hub او ما يسمى بالـDocker registry هو عباره عن مخزن كبير للـimages التي يتم صنعها من قبل المبرمجين والمطورين على الإنترنت ويتم مشاركتها للاستفادة منها. كل مطور برمجيات يستطيع ان ينشأ image ويعمل عليها بشكل locally ومن ثم يستطيع ان يضعها خاصه (private) لا يمكن الاطلاع عليها الا اشخاص معينين او عامه (public) يستطيع ان يستفيد أي شخص منها على الـDocker Hub, سوف نطلع على أمثله بالتفصيل لاحقا.
على نفس المنصة هناك شركات تتيح التطبيقات الخاصة بها على منصة Docker Hub بحيث لا يحتاج المطور ان يعمل configuration لهذه التطبيقات كما يتم في الماضي. مثلا, MySQL من الصورة التالي نشاهد ان هذا التطبيق الان جاهز للإستخدام وكل مافي الأمر هو عمل امر pull ومن ثم تشغيله على الجهاز والاستفادة منه في بناء قواعد البيانات اللتي احتاج اصممها للتطبيق الخاص بي.
أيضا نلاحظ بان انه يوضع علامة official image او verified publisher أي ان شركة MySQL هي من وضعت هذه الـimage وعليه يستطيع المستخدم ان يثق في التطبيق بعكس بعض التطبيقات التي قد تحتوي على مشاكل في الجودة او في الأمن.
مثال على استخدام الـDocker Hub عن طريق الـCLI
لكي يتم استخدام نظام الـDocker بالشكل الصيحيح, لابد ان يتم تحميل النظام وتتبع الخطوات من هذا الرابط: https://docs.docker.com/engine/install/
بعد ان يتم انزال نظام الـDocker على نظام Ubuntu او اي اصدار من توزيعات Linux, تستطيع ان تتحقق بإستخدام الأمر $docker --version
.
بعد ان تأكدنا بأن الـDocker يعمل على الجهاز بالشكل الصحيح, نبدأ بعملية الربط عن طريق CLI بمنصة الـDocker Hub باتباع الخطوات التاليه:
- اولا لابد ان يكون لديك حساب في الـDocker Hub (اسم مستخدم وكلمة مرور). للتسجيل استخدم الرابط التالي: https://hub.docker.com/signup
- عن طريق الـterminal نقوم باستخدام الأمر
$docker login docker.io
وسطلب الـusername و الـpassword الخاصه بك على Docker hub. بعد ذلك ومن مجرد الحصور على Login Succeeded تعتبر الان متصل بجهازك المحلي بمنصة Docker Hub وجاهز لتحميل التطبيقات المساعده لمشروعك او ايضا تحميل او رفع التطبيقات الخاصه بك إلى المنصه.
بناء الـDocker image
الـ Docker image نستطيع مقارنتها بـ git repository. استخدام الـgit من الممكن استضافتها (hosting) على الـ GitHub او الـ Bitbucket اومن الممكن على استضافتها على private repository. بنفس المفهوم، نستطيع استضافة الـDocker image على Docker repository service وهو ما يسمى الـDocker Hub.
اذا الـDocker hub هو خدمه مقدمه من الـDocker لاستضافة (hosting) الـimages, والبحث والمشاركة لجميع الـDocker repositories. وكما ذكرنا سابق في مثال الـgit, الـDocker repository من الممكن ان تكون عامه او خاصه. أخيرا, يطلق على الـDocker Hub وخدمات استضافة الـthird party repository الـregistries. مثلا, RedHat لديها الـسجل (RedHat registry) المخصص لها لاستضافة صور الحاويات (container images) الخاصة بها.
ملحوظه مهمه، بأن السجل الواحد لديه العديد من المخازن (repositories) بينما المخزن الواحد لديه إصدارات متعددة لنفس الصورة. كل هذه السجلات, قد تكون عامه أو خاصه. فالـ Docker Hub مثال على سجل عام (public registry).
الدوكر في حالته العادية هو في الأساس مهيئ على استخدام منصة الدوكر كسجل افتراضي (default registry). بإمكانك تستخدم الأمر $docker info للاطلاع على السجل الذي يستخدمه Docker حاليا. بشكل افتراضي, ستلاحظ انه يشير إلى http://index.docker.io/v1/ وهو موقع السجل على منصة الدوكر (Docker hub).
الصور الرسمية والغير رسميه (official images vs. non-official)
على منصة الـDocker , هناك نوعين من الـimages – رسميه وغير رسمية كما ذكرنا سابقا. الـimage الرسمية، هي عباره عن image موثوقة ومحسنة, ولديها توثيق وشرح مفصل (documentation)، وأيضا تتوفر على صفحتها على الـDocker hub امثله على كيفية الاستخدام, ومصممه بأن تكون للاستخدامات المتعددة الشاملة والمتعارف عليها.
في الجانب الأخر, الـimage الغير رسميه, هي أي image أنشئت من قبل مستخدم عادي وليس شركه او منظمه برمجيه. وبالتالي, Docker لديه بعض المقاييس لكي يتم التمييز بين النوعين. فالـimage الرسمية تحتوي على <image_name>
كإسم للـimage, بينما الـimage الغير رسميه تسميتها تكون بـ<username>/<image_name>
. أيضا¸الصور الرسمية¸ يكتب امامها كلمة official كما هو مبين في المثال التالي:
تحميل image او pull image من منصة الـDocker
استخدام البحث عن images على منصة الـDocker
نستطيع ان نبحث ونجد صور على منصة الدوكر سواء بإستخدام محرك البحث على موقع المنصة او باستخدام الأمر التالي: $docker search <image_name>
فلو اردنا ان نبحث عن الـimage بإسم busybox او Debian:
كما هو واضح من شاشة الـterminal جميع المعلومات المهمة عن الـimage اذا هي رسميه سيضاف لها [ok] تحت عبارة official وعدد الـstars يدل على مدى شعبيتها وقابلية المستخدمين لها, وكذلك وصف بسيط للـimage.
سحب ونسخ image او repository من الـregistry على الجهاز المحلي
لكي نقوم بتحميل image معينه او مجموعه images نستخدم الأمر:
$docker <image_name>:<tag_name>
اذا الـtag غير متوفر, فإن محرك دوكر (docker engine) سيتخم :latest تاق كـtag افتراضي. مثلا, إذا اردنا ان نقوم بعمل pull او نسخ الـimage ذات الإصدار الأخير من نضام التشغيل الأخير debian نقوم بالتالي:
انشاء Docker image
نستطيع ان ننشأ الـimage الخاصه بتطبيقاتنا ونشرها على منصة Docker بطريقتين:
بإستخدام Dockerfile لإنشاء image
الـDockerfile هو عباره عن نص بسيط من مستند يحتوي على سلسله من الأوامر بحيث يقوم Docker باستخدامها لبناء الـimages. مجموعة الأوامر المدعومة في الـDockerfile هي FROM, CMD, ENTRYPOINT, VOLUM, ENV, والعديد منها . مثال بسيط على الـDockerfile كالتالي:
ملاحظه: نقطه مهمه لنتذكرها وهي بأن الملف المخصص لصنع الـimage لابد بأن يكون اسمه Dockerfile. أمر الـdocker build يستخدم لبناء الـimage من الـDockerfile. ولكي نقوم ببناء الـimage للملف اللذي صنعناه في المثال السابق من الـDockerfile نستخدم الأمر التالي:
طبعا هناك بعض الـparameters مع كل امر (commands) نرجو الاطلاع عليها على الرابط .
بعد ان تتم عملية البناء بالشكل الصحيح سيخرج بأن الـimage تم بناؤها بنجاح وعمل لها tag بإسم الـimage والإصدار. نستطيع ان نعمل تشغيل للحاويه (كما بينا سابقا الحاوية او الـcontainer هي عباره عن instance من الـimage) وسوف نجد بأن المثال يقوم بطبعاة التاريخ كالتالي:
انشاء image من container
الطريقه الثانية لإنشاء image هي سحب docker image من منصة الـDocker Hub وانشاء حاوية منها ومن ثم عمل تعديل او تغيير عليها مثال ان نقوم بتحميل التطبيق الخاص بنا على تلك الحاويه. ومن ثم نستخدم الأمر docker commit لعمل انشاء docker commit من الحاويه. لنرى المثال التالي عن كيفية انشاء docker image من حاوية example_appChange it:
فالأمر $docker ps -a
يبين ماهي الـcontainers اللتي تعمل وعدد الـinstances لكل image. فمثلا, قمت بصناعة 3 containers من الـimage docker_exmple_immage.
نشر الـimages على الـDocker hub
لكي نستطيع نشر docker image الخاصه بنا على منصة الـdocker, هناك خطوات لابد ان نتبعها: الخطوة الأولى: قبل ان نعمل push للصوره على منصة الـdocker, نحتاج أولا ان يكون لدينا حساب على المنصة كما بيناه سابقا. الخطوة الثاتيه: انشاء مخزن (repository) على منصة الـDocker, لكي نستطيع تحميل الـimages كالتالي:
- تسجيل الدخول على الموقع.
- اختيار create repository كما هو مين في الصوره التالي:
- قم بإكمال البيانات الخاصه بالـrepository المراد انشاؤه, اسم المخزن example-image وهو اسم الـimage اللتي انشأناها مسبقا في الـDockerfile. تحدد خصاص المخزن عام او خاص (في الغالب عام افضل). واخيرا ننشاء المخزن بالضغط على زر create كما هو موضح في الصورة التالية:
الخطوة الثالثة: عمل push للـimage للـregistry على المنصه كالتالي:
- نقوم بتسجيل الدخول على حساب الـdocker الخاص بنا عبر نافذة الأوامر CLI باستخدام الامر
$docker login
- نعمل tag للـimage. وهذي الخطوه مهمه جدا, قبل ان نقوم بعمل رفع للـimage على المخزن (repository). كما ذكرنا سابقا, بأن الدوكر يتبع سياسة تسميه لتحديد الـimage الرسميه من غير الرسمية (official or not official). وما نقوم ببناءه هو non official image. وبالتالي لابد ان يتم الالتزام بالتسمية الصحيحة على النحو التالي:
<username>/<image_name>:<tag_name>
. في كثير من الحالات نحتاج إلى اعادة التسمية كاmyusername/example_image:latest
.
- نشر الـimage بعد عمل التاق لها إلى المخزن باستخدام الأمر
$docker push
بمجرد الانتهاء من الأمر, سنلاحظ بأن الـimage قد تم رفعها على منصة الـdocker بالوقت والتاريخ. هذا كل مافي الأمر, لقد تم بنجاح نشر أول صوره للتطبيق على الـdocker hub. لو اردت ان تقوم بإختبار الـimage, نستخدم الأوامر التالية وتفعيل التشغيل للحاوية (container) كالتالي:
الملخص
في نهاية هذا الشرح, قدمنا ملخص مبسط عن اساسيات نظام الـDocker وأنه نظام يساعد مطوري البرمجيات على اختبار تطبيقاتهم ومشاركتها بشكل فعال مع الأخرين. وايضا تعرفنا على التعريفات الأساسية لمفاهم الـcontainerization وفرقها عن الـvirtualization. كما اطلعنا على ما هو الـcontainer ومما يتكون وكيفية عمله. ايضا تعلمنا كيفية انشاء الـimages وعمل الـcontainers على منصة الـDocker hub والأوامر الأساسية التي يستخدمها المبرمج او المطور للتعامل مع نظام الـDocker ومنصة الـDocker hub. هناك تفاصيل كثيره قد لاتهم المستخدم العادي لنظام الـdocker ومن أراد الاطلاع عليها على الرابط التالي: https://docs.docker.com/