انتقال للمقال
وقت القراءة: ≈ 20 دقيقة (بمعدل فنجان واحد من القهوة 😊)

التعامل مع الـ Docker Image والـ Container

السلام عليكم ورحمة الله وبركاته


المقدمة

حسنًا، في المقالة السابقة تعرفنا على ما هو الـ Docker وما المشاكل التي يحلها
وتعرفنا على المفاهيم الأساسية مثل الـ Image والـ Container والـ Dockerfile والـ Docker Hub

في هذه المقالة سنبدأ بالجانب العملي ونتعلم كيف نتعامل مع الـ Docker بشكل فعلي
سنبدأ بتثبيت الـ Docker ثم نتعرف على أوامر التعامل مع الـ Images والـ Containers
مع أمثلة عملية بسيطة خطوة بخطوة

تثبيت الـ Docker

أول خطوة هي تثبيت الـ Docker على جهازك
يمكنك الذهاب إلى الموقع الرسمي للـ Docker واتباع التعليمات الخاصة بنظام التشغيل الخاص بك

https://docs.docker.com/get-docker/

ستجد تعليمات تثبيت الـ Docker على Windows و MacOS و Linux
اتبع الخطوات الموجودة في الموقع وستكون جاهزًا للعمل مع الـ Docker

التأكد من تثبيت الـ Docker

بعد تثبيت الـ Docker يمكننا التأكد من أنه تم تثبيته بشكل صحيح
نقوم بفتح الـ Shell أو الـ Command Prompt ونكتب الأمر التالي:

> docker

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


للتعرف على إصدار الـ Docker المثبت على جهازك يمكنك تشغيل الأمر التالي:

> docker version
Client:
 Version:           28.0.4
 API version:       1.48
 Go version:        go1.23.7
 Git commit:        b8034c0
 Built:             Tue Mar 25 15:06:08 2025
 OS/Arch:           linux/amd64
 Context:           default

Server: Docker Desktop 4.0.0 ()
 Engine:
  Version:          28.0.4
  API version:      1.48 (minimum version 1.24)
  Go version:       go1.23.7
  Git commit:       6430e49
  Experimental:     false
 containerd:
  Version:          1.7.26
  GitCommit:        753481ec61c7c8955a23d6ff7bc8e4daed455734
 runc:
  Version:          1.2.5
  GitCommit:        v1.2.5-0-g59923ef
 docker-init:

لاحظ أن المخرجات تحتوي على جزئين أساسيين:

  • الـ Client: وهو أداة الـ CLI التي نستخدمها للتفاعل مع الـ Docker وكتابة الأوامر
  • الـ Server: وهو الـ Docker Daemon أو الـ Docker Engine الذي يقوم بتنفيذ الأوامر وإدارة الـ Containers والـ Images وغيرها من الأمور

الـ Client يتواصل مع الـ Server لإدارة والتحكم في الـ Containers
بمعنى أننا عندما نكتب أمر معين في الـ Shell فإن الـ Client يرسل هذا الأمر إلى الـ Server الذي ينفذه


للحصول على معلومات أكثر تفصيلًا عن الـ Docker المثبت يمكنك تشغيل الأمر التالي:

> docker info

هذا الأمر يعطيك معلومات مفصلة جدًا عن الـ Docker
مثل عدد الـ Containers والـ Images ونوع الـ Storage Driver وغيرها من المعلومات
التفاصيل ستكون طويلة جدًا لذلك لن نعرضها هنا، يمكنك تجربة الأمر بنفسك لترى التفاصيل


قبل أن نبدأ بالأوامر العملية دعنا نفهم الفرق بين الـ Image والـ Container بشكل أعمق

ما هو الـ Docker Image ؟

الـ Docker Image هو قالب أو مخطط لإنشاء الـ Docker Container
يمكننا تشبيه الـ Image بأنه نسخة ثابتة من تطبيق أو برنامج معين
هذا البرنامج يمكن أن يكون أي شيء:

  • نظام تشغيل مثل Ubuntu أو Alpine Linux أو Debian
  • تطبيق مثل Nginx أو Redis أو MongoDB
  • لغة برمجة مثل Node.js أو Python أو PHP
  • قاعدة بيانات مثل MySQL أو PostgreSQL
  • أو مزيج من كل ما سبق حسب احتياجات تطبيقك
  • يمكنك أيضًا إنشاء Image خاصة بك تحتوي على كل ما تحتاجه لتشغيل تطبيقك

معظم الـ Images تكون مخزنة في الـ Docker Registry مثل الـ Docker Hub
وهو كما قلنا أشبه بـ GitHub ولكن للـ Docker Images أو كأنه Package Manager للـ Images
يمكنك تحميل Images جاهزة من هناك أو رفع Images خاصة بك

ستجد فيه آلاف الـ Images الجاهزة التي يمكنك تحميلها واستخدامها مباشرة
مثل MySQL و Redis و PostgreSQL و Nginx و PHP و Python وغيرها الكثير
والتي تكون مدعومة رسميًا من قبل الشركات أو المجتمعات المختصة بها

يمكنك أيضًا إنشاء Images خاصة بك باستخدام الـ Dockerfile ورفعها على الـ Docker Hub
وسنتحدث عن الـ Dockerfile بالتفصيل في المقالة القادمة

ما هو الـ Docker Container ؟

الـ Docker Container هو نسخة تنفيذية من الـ Docker Image
عندما نقوم بتشغيل Image فإنه ينشئ Container وهو التطبيق الذي ينفذ الـ Image

يمكننا تشبيه الأمر بمفهوم الـ Classes والـ Objects في الـ OOP:

  • الـ Image هو مثل الـ Class أو الـ Interface يعمل كقالب أو مخطط
  • الـ Container هو مثل الـ Object أو الـ Instance الذي يتم إنشاؤه من هذا القالب

يمكنك إنشاء عدة Containers من نفس الـ Image
كل Container يكون معزول عن الآخر ويعمل بشكل مستقل بذاته وله ملفاته وموارده الخاصة به مثل الـ Object مع الـ Class يمكننا إنشاء عدة Objects من نفس الـ Class وكل Object يعمل بشكل مستقل بذاته


الـ Containers لها العديد من الخصائص:

  • يمكن تشغيلها وإيقافها وإعادة تشغيلها وحذفها
  • يكون معزول عن النظام الأساسي لجهازك وعن كل الـ Containers الأخرى
  • يمكن أن يكون لها الـ Network الخاص بها
  • يمكن أن يكون لها الـ Storage الخاصة بها
  • يمكن أن يكون لها الـ Environment Variables الخاصة بها
  • وخصائص ومميزات أخرى كثيرة سنتعرف عليها في المقالات القادمة

أوامر الـ Docker Image

الـ Docker CLI يوفر لنا العديد من الأوامر للتعامل مع الـ Images والـ Containers
وهى مقسمة إلى أوامر خاصة بكل منهما والأوامر يمكنك فهمها بسهولة من خلال اسم الأمر

سنتعامل مع ثلاثة أوامر أساسية للـ Images:

  • docker image pull: لتحميل Image من الـ Docker Hub
  • docker image list: لعرض جميع الـ Images الموجودة على جهازك
  • docker image remove: لحذف Image من جهازك

هناك أوامر أخرى مثل docker image build لإنشاء Image من Dockerfile
لكن سنتعرف عليه في المقالة القادمة عندما نتحدث عن الـ Dockerfile
أو أمر docker image inspect لعرض تفاصيل الـ Image

docker image pull

الأمر docker image pull يستخدم لتحميل Image في حالة إذا كانت غير موجودة على جهازك
والأمر قد يختصر إلى docker pull فقط

> docker image pull <image-name>

دعنا نجرب تحميل Image لنظام Alpine Linux
الـ Alpine Linux هى توزيعة للـ Linux خفيفة جدًا وحجمها صغير جدًا
وتكون مجرد Operating System خام بدون أي برامج مثبتة مسبقًا
وأي Image ستجدها هكذا، مجرد البرنامج أو التطبيق بدون أي إضافات زائدة

> docker image pull alpine:latest
latest: Pulling from library/alpine
1074353eec0d: Pull complete
Digest: sha256:865b95f46d98cf867a156fe4a135ad3fe50d2056aa3f25ed31662dff6da4eb62
Status: Downloaded newer image for alpine:latest
docker.io/library/alpine:latest

هنا قمنا بتحميل Image الخاصة بـ Alpine Linux بإصدار latest
الـ latest هو الإصدار الافتراضي إذا لم نحدد إصدار معين
الـ Docker قام بتحميل الـ Image من الـ Docker Hub على جهازنا


يمكننا تحديد إصدار معين من الـ Image عند التحميل
على سبيل المثال، لتحميل إصدار 3.19 من Alpine Linux:

> docker image pull alpine:3.19
docker image pull alpine:3.19
3.19: Pulling from library/alpine
17a39c0ba978: Pull complete
Digest: sha256:6baf43584bcb78f2e5847d1de515f23499913ac9f12bdf834811a3145eb11ca1
Status: Downloaded newer image for alpine:3.19
docker.io/library/alpine:3.19

حاليًا لدينا على جهازنا إصدارين من Alpine Linux اصدار الـ latest والـ 3.19

docker image list

لعرض جميع الـ Images الموجودة على جهازك نستخدم الأمر docker image list أو docker images

> docker image list
REPOSITORY   TAG       IMAGE ID       CREATED        SIZE
alpine       latest    865b95f46d98   4 weeks ago    13MB
alpine       3.19      6baf43584bcb   3 months ago   11.5MB

هنا الأمر عرض لنا كل الـ Images الموجودة على جهازنا
ويعرض لنا عدة معلومات مهمة عن كل Image منها اسم الـ Image في خانة REPOSITORY
والـ id الخاص بها في خانة IMAGE ID

ولدينا TAG وهو يعبر عن مسمى للنسخة الـ Image التي قمنا بتحميلها
والـ TAG ليس شرطًا أن يعبر عن إصدار، بمعنى أنني قد أقوم بإنشاء Image خاصة بي تستخدم لغرض معين وأعطيها TAG باسم tabarani-image-tag مثلاً
وكلمة latest مجرد مسمى أيضًا ولا يشترط أن يكون latest هو أحدث إصدار من الـ Image، فهو مجرد اسم
بمعنى الـ Image الذي افترضنا أننا قمنا بإنشائه وقلنا أن اسمه tabarani-image-tag
إذا لم أعطيه أي TAG فسيكون اسمه الافتراضي latest أيضًا برغم من أنني قد أكون أنشأت هذه الـ Image منذ فترة طويلة ولم أقم بتحديثها منذ شهور
وقد أكون استخدم اصدارات قديمة فيها

لاحظ أن حجم كل Image صغير جدًا برغم من كونه Operating System، لأنه كما قلنا الـ images تكون خالية من أي برامج إضافية
وأيضًا الـ Alpine Linux هي توزيعة خفيفة جدًا بشكل عام كما ذكرنا مقارنة بغيرها من التوزيعات

docker image remove

لحذف Image نستخدم الأمر docker image remove أو docker image rm

> docker image remove 865b95f46d98
Untagged: alpine:latest
Deleted: sha256:865b95f46d98cf867a156fe4a135ad3fe50d2056aa3f25ed31662dff6da4eb62

لاحظ أننا استخدمنا الـ IMAGE ID لحذف الـ Image
لنعرض مرة أخرى الـ Images الموجودة على جهازنا:

> docker image list
REPOSITORY   TAG       IMAGE ID       CREATED        SIZE
alpine       3.19      6baf43584bcb   3 months ago   11.5MB

لاحظ أن الـ Image الخاصة بـ latest قد تم حذفها بنجاح

ويمكننا أيضًا حذف الـ Image باستخدام اسمها مع الإصدار:

docker image remove alpine:3.19
Untagged: alpine:3.19
Deleted: sha256:6baf43584bcb78f2e5847d1de515f23499913ac9f12bdf834811a3145eb11ca1

الآن لو عرضنا الـ Images مرة أخرى:

> docker image list
REPOSITORY   TAG       IMAGE ID   CREATED   SIZE

ستلاحظ أننا لم يعد لدينا أي Images على جهازنا

لنعيد تحميل الـ Image الخاصة بـ Alpine Linux مرة أخرى لكي نتمكن من استخدامها في الأمثلة القادمة:

> docker image pull alpine:latest
latest: Pulling from library/alpine
1074353eec0d: Pull complete
Digest: sha256:865b95f46d98cf867a156fe4a135ad3fe50d2056aa3f25ed31662dff6da4eb62
Status: Downloaded newer image for alpine:latest
docker.io/library/alpine:latest

لنرى الـ Images مرة أخرى:

> docker image list
REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
alpine       latest    865b95f46d98   4 weeks ago   13MB

لاحظ أن الـ Image ID الخاصة بـ Alpine Linux لم تتغير بعد حذفها وإعادة تحميلها مرة أخرى
هذا لأن الـ Image ID هو hash يتم توليده عندما نقوم بإنشاء الـ Image أو في كل مرة نقوم بعمل build لها
لأننا هنا لا نقوم ببناء Image جديدة بل نقوم بتحميل نفس الـ Image من الـ Docker Hub
فالـ Image الخاصة بـ Alpine Linux لم تتغير وبالتالي الـ Image ID لم يتغير أيضًا
كل مرة نقوم بتحميل نفس الـ Image من الـ Docker Hub سنحصل على نفس الـ Image ID
لكن في تم تعديل الـ Image وإنشاء نسخة جديدة منها فسيتم توليد Image ID جديد مختلف
على أي حال سنتعرف على هذا الأمر أكثر عندما نتحدث عن إنشاء الـ Images الخاصة بنا في المقالات القادمة

وأيضًا عمود CREATED يوضح لنا متى تم إنشاء أو تعديل هذه النسخة من الـ Image وليس متى تم تحميلها على جهازنا
لذا نفهم أن نسخة alpine:latest آخر تعديل لها كان منذ 4 أسابيع

أوامر الـ Docker Container

الآن بعد أن تعرفنا على الـ Images وكيفية تحميلها وعرضها وحذفها
دعنا نتعرف على الـ Containers وكيفية التعامل معها

سنتعرف على بعض الأوامر الأساسية للـ Containers:

  • docker container run: لإنشاء وتشغيل Container جديد من Image
  • docker container list: لعرض جميع الـ Containers الموجودة على جهازك
  • docker container stop: لإيقاف Container قيد التشغيل
  • docker container start: لتشغيل Container متوقف
  • docker container remove: لحذف Container متوقف
  • docker container exec: لتنفيذ أوامر داخل Container قيد التشغيل

docker container run

لإنشاء Container جديد من Image وتشغيله نستخدم الأمر docker container run أو docker run فقط

> docker container run <image-name>

هنا نحن نريد إنشاء Container من الـ Image الخاصة بـ Alpine Linux التي قمنا بتحميلها سابقًا

> docker container run alpine:latest

الآن الـ Docker قام بإنشاء Container جديد من الـ Image الخاصة بـ Alpine Linux
حسنًا لنتأكد من ذلك ونرى الـ Containers الموجودة على جهازنا عن طريق الأمر docker container list

ملحوظة: إذا قمت بتشغيل الأمر docker run alpine:latest والـ Image غير موجودة على جهازك
هنا Docker سيقوم تلقائيًا بتحميل الـ Image من الـ Docker Hub قبل تشغيلها
لذلك يمكنك انشاء الـ Container حتى بدون تحميل الـ Image مسبقًا وسيقوم الـ Docker بتحميلها نيابة عنك في حالة عدم وجودها

docker container list

لعرض قائمة الـ Containers نستخدم الأمر docker container list

> docker container list
CONTAINER ID   IMAGE           COMMAND     CREATED          STATUS                      NAMES

ستلاحظ أن القائمة فارغة ولا يوجد أي Container معروض
هذا لأن أمر docker container list يعرض فقط الـ Containers التي تعمل حاليًا
أما الـ Container الذي أنشأناه من قبل فلقد انتهى وأغلق نفسه فورًا بعد إنشائه
وسنعرف السبب في ذلك لاحقًا

لذلك لعرض جميع الـ Containers سواء كانت تعمل أو متوقفة نستخدم الـ flag المسمى --all أو -a

> docker container list --all
CONTAINER ID   IMAGE           COMMAND     CREATED         STATUS                     PORTS     NAMES
6f5bce75aa7c   alpine:latest   "/bin/sh"   7 minutes ago   Exited (0) 7 minutes ago             affectionate_ishizaka

الآن نرى الـ Container الذي أنشأناه سابقًا معروض في القائمة
لدينا هنا معلومات عنه مثل الـ CONTAINER ID والـ IMAGE التي تم إنشاؤه منها
والـ STATUS الخاص به والـ NAMES الذي تم تعيينه له تلقائيًا من قبل الـ Docker ولدينا أيضًا الـ CREATED الذي يوضح متى تم إنشاؤه

لاحظ أن قيمة الـ STATUS هى Exited (0) مما يعني أن الـ Container انتهى وأغلق نفسه فورًا بعد إنشائه
لكن لما أغلق نفسه ؟
حسنًا أنظر إلى خانة الـ COMMAND والتي توضح الأمر الذي تم تنفيذه داخل الـ Container عند إنشائه باستخدام أمر docker container run
سنرى هنا أن الأمر هو "/bin/sh" وهو الـ Shell الخاص بـ Alpine Linux
بالتالي عندما تم إنشاء الـ Container تم تشغيل الـ Shell الخاص به
لكن لسبب ما الـ Shell أغلق نفسه فورًا بعد إنشائه
هذا لأننا نحتاج أن نجعل الـ Shell يبقى مفتوحًا للتفاعل معه عن طريق بعض الـ flags الإضافية وهى -i و -t
والتي سنتعرف عليها ونشرحها لاحقًا في فقرة مخصصة في المقالة


على أي حال، في حالة تكرار تنفيذ الأمر docker container run alpine:latest عدة مرات
سيتم إنشاء Container جديد في كل مرة بنفس الطريقة
ويمكننا استخدام الـ IMAGE ID بدلاً من اسم الـ Image عند إنشاء الـ Container

لنحاول تجربة الأمر وإنشاء أكثر من Container:

> docker container run alpine:latest
> docker container run 865b95f46d98

الآن لو عرضنا الـ Containers مرة أخرى:

> docker container list --all
docker container list --all
CONTAINER ID   IMAGE           COMMAND     CREATED          STATUS                      PORTS     NAMES
937bbfedef93   alpine:latest   "/bin/sh"   3 seconds ago    Exited (0) 3 seconds ago              distracted_ardinghelli
a1bf2589d733   865b95f46d98    "/bin/sh"   8 seconds ago    Exited (0) 8 seconds ago              compassionate_volhard
6f5bce75aa7c   alpine:latest   "/bin/sh"   12 minutes ago   Exited (0) 12 minutes ago             affectionate_ishizaka

الآن لدينا ثلاثة Containers من نفس الـ Image
لاحظ أن الـ Container الذي تم إنشاؤه باستخدام الـ IMAGE ID له نفس الـ IMAGE ID في خانة الـ IMAGE
والذي تم إنشاؤه باستخدام اسم الـ Image له اسم الـ Image في خانة الـ IMAGE

وكما ذكرنا سابقًا، فإن كل Container يعمل بشكل مستقل عن الآخر ومعزول بشكل تام عن جهازك وباقي الـ Containers

docker container remove

بعد ما أصبح لدينا عدة Containers على جهازنا
نريد حذف بعضها للتخلص من الـ Containers غير المستخدمة

لحذف Container نستخدم الأمر docker container rm أو docker rm أو docker container remove أو docker remove
في docker ستلاحظ وجود عدة أوامر تؤدي نفس الوظيفة مع اختلاف في التسمية فقط

> docker container remove 937bbfedef93
937bbfedef93

الآن لو عرضنا الـ Containers:

> docker container list --all
CONTAINER ID   IMAGE           COMMAND     CREATED          STATUS                      PORTS     NAMES
a1bf2589d733   865b95f46d98    "/bin/sh"   7 minutes ago    Exited (0) 7 minutes ago              compassionate_volhard
6f5bce75aa7c   alpine:latest   "/bin/sh"   20 minutes ago   Exited (0) 20 minutes ago             affectionate_ishizaka

ستلاحظ أن الـ Container الذي حذفناه لم يعد موجود في القائمة

يمكنك حذف عدة Containers دفعة واحدة:

> docker container remove a1bf2589d733 6f5bce75aa7c
a1bf2589d733
6f5bce75aa7c

الآن لو عرضنا الـ Containers مرة أخرى:

> docker container list --all
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

الآن جميع الـ Containers قد تم حذفها بنجاح

تسمية الـ Container

ستلاحظ أنه عند إنشاء Container جديد من الـ Image يتم تعيين اسم تلقائي له في عمود NAMES
بمعنى أن جميع الـ Containers التي أنشأناها سابقًا كان لها أسماء مثل distracted_ardinghelli و compassionate_volhard و affectionate_ishizaka
هذه الأسماء يتم توليدها تلقائيًا من قبل الـ Docker عند إنشاء الـ Container
وتكون بديلة عن الـ CONTAINER ID الطويلة والصعبة التذكر

الـ Docker يقوم بتوليد اسم عشوائي يتكون من كلمتين: صفة و اسم عالم أو شخصية مشهورة
مثل sleepy_borg أو happy_einstein أو angry_tesla
وهذا يجعل الأسماء سهلة التذكر نوعًا ما مقارنة بالـ Container ID


يمكنك استخدام اسم الـ Container بدلاً من الـ Container ID في معظم الأوامر
على سبيل المثال، بدلاً من كتابة:

> docker container remove 937bbfedef93

يمكنك كتابة:

> docker container remove compassionate_volhard

وهذا يسهل التعامل مع الـ Containers خاصة عندما يكون لديك عدة Containers تعمل في نفس الوقت


لكن نحن لا نريدأن نعتمد على الأسماء التلقائية للـ Container
بل نحتاج في بعض الأحيان إلى تعيين أسماء مخصصة للـ Containers الخاصة بنا
لذا يوفر لنا الـ Docker طريقة لتحديد اسم مخصص للـ Container عند إنشائه
باستخدام الـ flag المسمى --name

> docker container run --name <container-name> <image-name>

لنجرب إنشاء Container جديد باسم my-alpine-container:

> docker container run --name my-alpine-container alpine:latest

الآن لو عرضنا الـ Containers:

> docker container list --all
CONTAINER ID   IMAGE           COMMAND     CREATED          STATUS                      PORTS     NAMES
8f3d2a1b5c7e   alpine:latest   "/bin/sh"   3 seconds ago    Exited (0) 3 seconds ago              my-alpine-container

لاحظ أن الـ Container الجديد له الاسم الذي حددناه وهو my-alpine-container
الآن يمكننا استخدام هذا الاسم في أي أمر عوضًا عن الـ Container ID

> docker container remove my-alpine-container
my-alpine-container

ملحوظة: اسم الـ Container يجب أن لا يتكرر
بمعنى أنه لا يمكنك إنشاء Container جديد بنفس اسم Container موجود بالفعل
حتى لو كان الـ Container الموجود متوقفًا، يجب حذفه أولًا أو استخدام اسم مختلف

من الآن فصاعدًا سنقوم بإنشاء Containers بأسماء مخصصة لتسهيل التعامل معها في الأمثلة القادمة
وأيضًا لنعتاد على تسمية الـ Containers بأسماء واضحة تعبر عن وظيفتها
وأيضًا ستعرف أن اسم الـ Container مهم جدًا عندما نتعامل مع معظم خصائص الـ Docker لأننا يمكننا استخدام اسم الـ Container بدلاً من الـ Container ID أو بدلًا من الـ IP Address الخاص بالـ Container في الأوامر
وخصوصًا أن الأسماء تكون ثابتة وسهلة التذكر مقارنة بالـ Container ID أو الـ IP Address التي قد تتغير في كل مرة نقوم فيها بإنشاء Container جديد

تشغيل Shell داخل الـ Container

قلنا أننا عندما نقوم بتشغيل Container من Alpine Linux فإنه يبدأ وينتهي فورًا
وعرفنا من خانة الـ COMMAND أن الأمر الذي حاول الـ Alpine تشغيله هو الـ Shell الخاص به /bin/sh
لكن الـ Shell أغلق نفسه لسبب ما

عليك أن تعرف أن كل Image نحمله ثم نقوم بإنشاء Container منها
فهذا الـ Image يقوم بتنفيذ أمر معين عند تشغيل الـ Container منه
وفي حالة الـ Alpine Linux فإن الأمر الافتراضي هو تشغيل الـ Shell الخاص به
وغالبًا أي Image لنظام تشغيل Linux سيكون الأمر الافتراضي هو تشغيل الـ Shell الخاص به
وكل Image أخرى سيكون لها أمر افتراضي مختلف حسب طبيعة هذا الـ Image

على أي حال قلنا أن الـ Shell أغلق نفسه فورًا لأننا لم نحدد له أن يبقى مفتوحًا للتفاعل معه
ولكي نجعل الـ Shell يبقى مفتوحًا ونتفاعل معه علينا استخدام الأمر docker container run مع بعض الـ flags الإضافية:

  • -i اختصار لـ interactive وهي تسمح بالتفاعل مع الـ Container
    بمعنى أخر أنها تبقي الـ STDIN مفتوحًا للتفاعل مع الـ Container وكتابة الأوامر داخله
  • -t اختصار لـ tty وهي اختصار لـ teletypewriter وتقوم بتخصيص Shell داخل الـ Container
    بمعنى أنها تقوم بمحاكاة بيئة Terminal داخل الـ Container مما يجعل التفاعل معه أسهل

يمكننا دمج الـ -i و -t في flag واحدة -it

> docker container run -it --name my-alpine-container alpine:latest
/ #

عندما نستخدم الـ -it مع الأمر docker container run
سيقوم بإنشاء Container جديد من الـ Image الخاصة بـ Alpine Linux
وسيقوم الـ Alpine بتنفيذ الأمر الافتراضي الخاص به وهو تشغيل الـ Shell الخاص به /bin/sh
ثم بفضل الـ -it سيبقي الـ Shell مفتوحًا لكي نتفاعل معه

عندما تقوم بتنفيذ هذا الأمر ستدخل مباشرة إلى الـ Shell الخاص بالـ Container داخل الـ Alpine Linux
يمكنك كتابة أوامر Linux هنا وتنفيذها داخل الـ Container

مثل أمر ls لعرض الملفات والمجلدات التي توجد في الـ Container:

/ # ls
bin    dev    etc    home   lib    media  mnt    opt    proc   root   run    sbin   srv    sys    tmp    usr    var

ستلاحظ أن هذه هي الملفات والمجلدات الأساسية لأي نظام تشغيل Linux
وهذا يؤكد لنا أننا داخل نظام Alpine Linux داخل الـ Container

يمكنك التحقق من إصدار نظام التشغيل باستخدام الأمر cat /etc/os-release:

/ # cat /etc/os-release
NAME="Alpine Linux"
ID=alpine
VERSION_ID=3.23.2
PRETTY_NAME="Alpine Linux v3.23"
HOME_URL="https://alpinelinux.org/"
BUG_REPORT_URL="https://gitlab.alpinelinux.org/alpine/aports/-/issues"

أنت الآن لديك نظام Alpine Linux يعمل داخل Container ويمكنك التفاعل معه كما تريد
وتنفيذ أي أوامر Linux تريدها داخل هذا الـ Container

للخروج من الـ Container اكتب exit:

/ # exit

الآن بعد الخروج من الـ Container
إذا قمت بعرض الـ Containers مرة أخرى:

> docker container list --all
CONTAINER ID   IMAGE           COMMAND     CREATED         STATUS                      PORTS     NAMES
16704b093612   alpine:latest   "/bin/sh"   3 minutes ago   Exited (0) 13 seconds ago             my-alpine-container

ستلاحظ أن الـ Container الذي كنا فيه قد تم إنشاؤه منذ 3 دقائق وحالته الآن Exited لأنه انتهى منذ 13 ثانية
بمعنى أننا كنا داخل هذا الـ Container لمدة 3 دقائق تقريبًا قبل أن نخرج منه

ملحوظة: عندما ننشيء Container باستخدام -it كما فعلنا الآن
فإن الـ Container يحتفظ بإعدادات الـ -it هذه كإعدادات دائمة للـ Container
وهذا يعطي خصائص معينة للـ Container لن تجدها في حالة إنشاء Container بدون -it


وتذكر أن الـ -it تستتخدم مع أمر الـ shell للدخول فيه والتفاعل معه
لكن لو كان الأمر الافتراضي للـ Image لا يقوم بتشغيل shell بل يقوم بتشغيل أمر آخر
فإن الـ -it لن يكون له أي تأثير لأننا لا نقوم بتشغيل shell داخل الـ Container من الأساس

docker container start

لتشغيل Container متوقف نستخدم الأمر docker container start أو docker start

> docker container start my-alpine-container
my-alpine-container

هنا قمنا بتشغيل الـ Container الذي أنشأناه سابقًا باسم my-alpine-container
وبالطبع يمكننا استخدام الـ CONTAINER ID بدلاً من الاسم
لكن كما ذكرنًا سنتعامل مع أسماء الـ Containers من الآن فصاعدًا
لكن لاحظ أن الـ Container لم يجعلنا ندخل إلى الـ Shell الخاص به مرة أخرى

> docker container list --all
CONTAINER ID   IMAGE           COMMAND     CREATED          STATUS         PORTS     NAMES
16704b093612   alpine:latest   "/bin/sh"   22 minutes ago   Up 3 seconds             my-alpine-container

لاحظ أن الـ STATUS الآن يظهر Up 3 seconds مما يعني أنه يعمل حاليًا
بمعنى أن الـ Container يعمل الآن في الخلفية

إذا أردنا الدخول إلى الـ Container والتفاعل معه مرة أخرى بعد إنشائه
نستخدم الأمر docker container start مع الـ flags الخاصة بالتفاعل -a و -i:

  • -a (اختصار لـ attach): لربط الـ Shell الخاص بك بالـ Container
  • -i (اختصار لـ interactive): للسماح بالتفاعل مع الـ Container

ويمكننا دمجهما في flag واحدة -ai

لاحظ أن -a يشبه -t في أنه يربط الـ Shell الخاص بك بالـ Container
لكن الفرق هو أن -t يستخدم لإنشاء Shell جديد داخل الـ Container خلال عملية الإنشاء run
أما -a يستخدم لربط الـ Shell الخاص بك بالـ Container الموجود بالفعل خلال عملية التشغيل start

> docker container start -ai my-alpine-container
/ #

الآن نحن داخل الـ Container مرة أخرى ويمكننا التفاعل معه كما فعلنا سابقًا
مثل تنفيذ أمر ls وغيره من أوامر Linux داخل الـ Container كما فعلنا سابقًا

الآن للخروج من الـ Container نكتب exit:

/ # exit

هناك أمور عليك أن تعرفها عند استخدام أمر docker container start مع الـ -a و -i
وهى أنك لا تستطيع استخدام -a في حالة إذا أنشأت الـ Container بدون -t في البداية

وإذا قمت بإنشاء Container بدون -it في البداية ثم قمت بعمل start ستجد أن حالة الـ Container تصبح Exited وليس Up
أما عندما تقوم بعمل start على Container تم إنشاؤه بـ -it في البداية فستجد أن حالة الـ Container تصبح Up وتبقى تعمل في الخلفية

وهذا السلوك بسبب أن هذه الـ flags يتم حفظها كإعدادات دائمة للـ Container عند إنشائه باستخدام docker container run -it
بالتالي فكأنها تعطي خصائص دائمة للـ Container بأننا فعلنا خواص التفاعل معه باستخدام -it
بالتالي عندما نقوم بعمل start فإنه يبدأ في الخلفية بسبب الـ flags المحفوظة سابقًا

أما في حالة إذا أنشأنا الـ Container بدون -it في البداية
فإننا عندما نقوم بعمل start فإنه يبدأ وينتهي فورًا مرة أخرى دون أن يبقى يعمل في الخلفية
لأن هذا الـ Container لا يملك إعدادات التفاعل معه عند إنشائه

ملحوظة: الـ flags مثل -it و -ai نستخدمها عندما ننفذ shell أو bash أو ما شابه داخل الـ Container
ونريد أن نتفاعل مع هذا الـ shell أو bash
لكن في حالة إذا كان الأمر الافتراضي للـ Image لا يقوم بتشغيل shell أو bash بل يقوم بتشغيل أمر آخر
أو كان الـ Container ينفذ أمر معين عند تشغيله فإن هذه الـ flags لن يكون لها أي تأثير أو فائدة
لذا استخدمها فقط عندما تنفذ shell أو bash داخل الـ Container والتفاعل معه

وكما قلنا ليس كل Image تقوم بتشغيل shell أو bash عند تشغيل Container منها
سنقوم بشرح وفهم هذه الأمور أكثر عندما نتحدث عن االـ Dockerfile وبناء الـ Images الخاصة بنا في المقالات القادمة

docker container exec

لاحظ أننا عندما نريد تنفيذ أمر معين داخل Container نضطر إلى الدخول إلى الـ Shell الخاص به أولًا
ثم كتابة الأمر الذي نريد تنفيذه ثم نخرج من الـ Shell مرة أخرى عن طريق كتابة exit

لكن ماذا لو أردنا فقط تنفيذ أمر معين داخل Container دون الحاجة إلى الدخول إلى الـ Shell الخاص به؟
هنا تأتي فكرة أمر الـ exec بحيث يمكننا تنفيذ أوامر داخل Container بدون الدخول إلى الـ Shell الخاص به

الأمر docker container exec أو docker exec يتيح لنا ذلك
يتيح لنا هذا الأمر تنفيذ أوامر داخل Container بشرط أن يكون الـ Container يعمل حاليًا وليس متوقفًا

في حالتنا الآن فالـ Container الخاص بـ Alpine Linux ليس متوقف ويمكننا التحقق من ذلك:

> docker container list --all
CONTAINER ID   IMAGE           COMMAND     CREATED          STATUS         PORTS     NAMES
16704b093612   alpine:latest   "/bin/sh"   24 minutes ago   Up 2 minutes             my-alpine-container

كما نرى الـ STATUS قيمتها Up مما يعني أنه يعمل حاليًا

أمر الـ exec مثل أخوته يكتب بالشكل التالي:

> docker container exec <container-id> <command>

لنفترض أننا نريد تنفيذ أمر ls داخل الـ Container:

> docker container exec my-alpine-container ls
bin dev etc home lib media mnt opt proc root ...

هنا قمنا بتنفيذ أمر ls داخل الـ Container دون الدخول إليه
الأمر تم تنفيذه وتم عرض النتيجة مباشرة من الـ Terminal الخاص بنا

docker container stop

لإيقاف Container يعمل حاليًا نستخدم الأمر docker container stop أو docker stop

> docker container stop my-alpine-container
my-alpine-container

دعنا نتحقق من أن الـ Container قد توقف

> docker container list --all
CONTAINER ID   IMAGE           COMMAND     CREATED          STATUS                      PORTS     NAMES
16704b093612   alpine:latest   "/bin/sh"   26 minutes ago   Exited (0) 39 seconds ago             my-alpine-container

ستلاحظ أن الـ STATUS أصبح Exited مما يعني أن الـ Container قد توقف بنجاح

ملخص الأوامر

لا أريد أن استفيض في الشرح أكثر من ذلك في هذه المقالة
لأنه مازال هناك تفاصيل وأوامر كثرة، لذا سنكتفي بهذا القدر في هذه المقالة

في هذه المقالة تعرفنا على أساسيات التعامل مع الـ Docker
وتعرفنا على الفرق بين الـ Image والـ Container بشكل عملي بسيط
ثم تعلمنا الأوامر الأساسية للتعامل مع الـ Images والـ Containers
في المقالة القادمة سنشرح مثال عملي أخر باستخدام Nginx وسنقوم بإنشاء Container لتشغيل Nginx داخل Docker

إليك ملخص سريع لأهم الأوامر التي تعلمناها في هذه المقالة:

الأمر الوظيفة
docker image pull <image> تحميل Image من الـ Docker Hub
docker image list عرض جميع الـ Images
docker image remove <image> حذف Image
docker container run <image> إنشاء وتشغيل Container جديد
docker container run --name <name> <image> إنشاء Container مع تعيين اسم مخصص
docker container run -it <image> تشغيل Container مع الدخول للـ Shell
docker container exec <container> <command> تنفيذ أمر داخل Container
docker container list عرض الـ Containers العاملة
docker container list --all عرض جميع الـ Containers
docker container start <container> تشغيل Container متوقف
docker container stop <container> إيقاف Container قيد التشغيل
docker container remove <container> حذف Container متوقف

ملحوظة: لا يمكنك حذف Container يعمل حاليًا، لذا يجب عليك إيقافه أولًا باستخدام الأمر docker container stop
ثم بعد ذلك يمكنك حذفه باستخدام الأمر docker container remove
ولا يمكنك حذف Image إذا كان هناك Container تم إنشاءه منها
لذا يجب عليك حذف جميع الـ Containers التي تم إنشاؤها من هذه الـ Image أولًا، سواء كانت الـ Containers تعمل أو متوقفة
ثم بعد ذلك يمكنك حذف الـ Image

خاتمة

يوجد العديد من الأوامر والتفاصيل الأخرى التي يمكننا تعلمها في الـ Docker
لكن لا أريد أن أطيل عليكم بالأوامر والتفاصيل في هذه المقالة
تعرفنا في هذه المقالة على أهم الأوامر والأمور التي ستحتاجها في البداية
في باقي المقالات أو حتى في طريقك في تعلم الـ Docker ستتعلم المزيد من الأوامر والتفاصيل الأخرى