Docker ENTRYPOINT ضد CMD — وعَدَةُ صندوقك
أنت دمجت ENTRYPOINT و CMD في ملف Dockerfile، وبدأ الـ container بالشيء الخاطئ، ووصلت إلى هذا الموضع. إليك التحليل الكامل — كل التوافقات، الفخ في صيغة الشيل مقابل صيغة exec، والأنماط التي تعمل فعلاً.
يحدث الخطأ عند الساعة 2 صباحًا. تبدأ علامة الـ container، وبدلاً من خادم API الخاص بك، تظهر لك نافذة تعليمية أو لا شيء على الإطلاق أو يُنفذ عملية مُغلفة في "الظل" sh الذي يأكل SIGTERM كالحلوى — لذا يستغرق التوقف المُناسب 10 ثوانٍ من انتظار Docker قبل أن يُنهي الأمر ويُرسل SIGKILL.
السبب، في كل مرة تقريبًا: كنت مُخطئًا في ENTRYPOINT و CMDأو جمعت بينها بطريقة تُقبلها Docker بصمت — لكنها ليست كما توقعها.
CMD: القيمة الافتراضية التي يمكنك استبدالها
CMD تُحدد ما يُنفذ عند بدء علامة الـ container — لكنها مجرد اقتراح، وليس قاعدة. إذا أضفت أي شيء بعد اسم الصورة، فإنه يُستبدل بالكامل:
FROM ubuntu
CMD ["echo", "hello from CMD"]
$ docker run myimage
hello from CMD
$ docker run myimage echo goodbye
goodbye
هذا echo goodbye لم يُرفق — بل تم استبداله. أصبحت كل شيء CMD مفقودًا. وهذا هو المقصود: CMD هو السلوك الافتراضي، وليس السلوك المُفرض. أي مُدخل تشغيل يُكسب.
ENTRYPOINT: الجزء الذي يُنفذ دائمًا
ENTRYPOINT يُحدد التنفيذ الذي يُنفذ بغض النظر عن أي شيء. لا يُستبدل المُدخل التشغيلي — بل يُمرر إليه بدلًا من ذلك:
FROM ubuntu
ENTRYPOINT ["echo"]
CMD ["hello"]
$ docker run myimage
hello
$ docker run myimage goodbye
goodbye
$ docker run --entrypoint cat myimage /etc/hostname
mycontainer-abc123
عندما تكون كلاهما مُحددًا، ENTRYPOINT هو التنفيذ، و CMD يصبح مُدخلاته الافتراضية. يمكن تجاوز CMD بشكل حر. يمكن تجاوز ENTRYPOINT فقط إذا قمت بتمرير --entrypoint.
كل تشكيل ممكن لـ ENTRYPOINT + CMD، مُفسر
تتضمن وثائق Docker جدولًا هذا لكنها لا تُركز على الصفوف التي تُسبب مشاكلك في اليوم:
| ENTRYPOINT | CMD | ما يُنفذ بالفعل |
|---|---|---|
| مُحددة دائمًا في البيئة الإنتاجية | مُحددة دائمًا في البيئة الإنتاجية | خطأ — يحتاج الـ container إلى أمر من مكان ما |
| مُحددة دائمًا في البيئة الإنتاجية | ["cmd", "arg"] شكل exec | cmd arg |
| مُحددة دائمًا في البيئة الإنتاجية | cmd arg شكل shell | /bin/sh -c "cmd arg" |
["entry"] شكل exec | مُحددة دائمًا في البيئة الإنتاجية | entry |
["entry"] شكل exec | ["arg1", "arg2"] شكل exec | entry arg1 arg2 ✓ |
["entry"] شكل exec | cmd arg شكل shell | entry /bin/sh -c "cmd arg" — من المرجح أن يكون خطأ |
entry شكل shell | ["arg1"] شكل exec | /bin/sh -c "entry" — يُتجاهل CMD بصمت |
entry شكل shell | cmd arg شكل shell | /bin/sh -c "entry" — يُتجاهل CMD بصمت |
الصفرين المُشَكَّلان بـ "يُتجاهل CMD بصمت" مسؤولان عن نسبة كبيرة من جلسات تشغيل Docker. شكل shell ENTRYPOINT لا يُدمج مع CMD — بل يتجاهله بالكامل. لن يُحذرك Docker من هذا.
شكل shell مقابل شكل exec: الفخ في معالجة الإشارات
تقبل كل من التعليماتان شكلين، ويعتمد الاختيار أكثر من ما يُقرره معظم مراجعات Dockerfile.
شكل exec (نظام المصفوفة):
ENTRYPOINT ["nginx", "-g", "daemon off;"]
يُنفذ البرنامج مباشرة. يصبح رقم PID 1. عندما يُرسل Docker SIGTERM لإيقاف الـ container، يتلقى البرنامج هذه الإشارة. يعمل التوقف بسلاسة. يتم تنظيف السجلات. تُغلق الاتصالات بشكل ناجح.
شكل الشل (نص بسيط):
ENTRYPOINT nginx -g "daemon off;"
يُنفذ Docker هذا كـ /bin/sh -c "nginx -g daemon off;". يكون الـ shell رقم PID 1. عندما تصل SIGTERM إلى النظام، sh يُحصل عليها — و sh لا يُنقل الإشارات إلى العمليات الفرعية. يُبقى الـ container لمدة 10 ثوانٍ، يُحصل على SIGKILL، ويُموت دون تنظيف. كل مرة.
استخدم شكل exec. دائمًا. لكل من ENTRYPOINT و CMD.
أربعة أنماط تعمل فعليًا
النمط 1: تنفيذ ثابت، مع إعدادات قابلة للتعديل
النمط المناسب لمعظم علامات الـ container في البيئة الإنتاجية. يكون البرنامج ثابتًا، والخيارات المُدخلة قابلة للتعديل في وقت التشغيل:
ENTRYPOINT ["/app/server"]
CMD ["--port", "8080", "--env", "production"]
# Use defaults
docker run myimage
# Override at deploy time
docker run myimage --port 9090 --env staging
النمط 2: سكربت مُحيط مع exec
عندما تحتاج إلى إجراءات تمهيد قبل عملية التشغيل الرئيسية (مigrations، إدخال الأسرار، تثبيت الإشارات)، استخدم سكربت مُحيط. السطر المهم هو exec "$@" في النهاية — يُستبدل عملية الـ shell بعملية CMD، لذا يصبح البرنامج رقم PID 1:
#!/bin/sh
set -e
echo "Running migrations..."
/app/migrate
# Hand off to CMD — exec replaces shell, so /app/server becomes PID 1
exec "$@"
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
CMD ["/app/server", "--port", "8080"]
إذا تجاهلت exec "$@"، يبقى الـ shell كرقم PID 1 ويُعود إلى مشاكل معالجة الإشارات.
النمط 3: CMD فقط، بدون ENTRYPOINT
مقبول للصور المُطوّرة أو علامات الـ container التي تُستخدم لتشغيل أوامر عشوائية في بيئة متماثلة:
FROM python:3.12-slim
WORKDIR /app
COPY . .
RUN pip install -r requirements.txt
CMD ["python", "app.py"]
في البيئة الإنتاجية، يكون النمط 1 أو 2 أكثر أمانًا — لا ترغب في أن يُنفذ سكربت تثبيت خاطئ بشكل غير متوقع ويُستبدل خادمك بجلسة الـ shell. docker run myimage bash ما يخص دالة docker exec في هذا السياق
لا شيء.
يُنفذ أمرًا داخل علامة الـ container المُشغلة بالفعل. يتجاوزها بالكامل. لا تحتاج إلى التفكير في docker exec عندما تستخدم ENTRYPOINT لإطلاع على شيء ما — أنت تتحدث مع بيئة الـ container الحالية، وليس على إعدادات البدء. ENTRYPOINT الارتباك غالبًا ما ينشأ من الناس الذين يستخدمون docker exec mycontainer bash ويفحصون أن الأمور تعمل، ثم يتساءلون لماذا
يُظهر سلوكًا مختلفًا. هما مسارات كود منفصلة. docker exec مُراجعة قبل الإطلاق docker run استخدم شكل exec (نظام المصفوفة، وليس نصوص بسيطة)
إذا كان لديك سكربت مُحيط، ينتهي بـ
- كلاهما
ENTRYPOINTوCMDلقد قمت بتجربة - لإثبات أن كل مسار يعمل
exec "$@" - يُكمل في أقل من 2 ثانية (ليس 10 — إذا كان 10، فهذا يعني وجود مشكلة في الإشارات)
docker run myimageوdocker run myimage --your-flagإذا أردت ملاحظات تلقائية قبل أن يُرسل Dockerfile إلى مخزن، docker stop mycontainerIO Tools’ Dockerfile Linter
يُكتشف استخدام شكل shell، نقص في سكربت الـ entrypoint، وأنماط أخرى تؤدي إلى سلوك خاطئ في وقت التشغيل. Docker ENTRYPOINT مقابل CMD — علامة الـ container أضحتت لك 2 exec Docker ENTRYPOINT مقابل CMD — علامة الـ container أضحتت لك 1
تثبيت ملحقاتنا
أضف أدوات IO إلى متصفحك المفضل للوصول الفوري والبحث بشكل أسرع
恵 وصلت لوحة النتائج!
لوحة النتائج هي طريقة ممتعة لتتبع ألعابك، يتم تخزين جميع البيانات في متصفحك. المزيد من الميزات قريبا!
