خطأ ‘exec format error’ في Docker يعني أن kernel لا يستطيع تنفيذ الملف الثنائي أو النص الذي حددته. يحدث هذا عندما لا تطابق المعمارية، الملف ينقصه صلاحيات التنفيذ، أو النص لديه نهايات أسطر خاطئة. هذا الدليل يوضح لك كيفية التشخيص والإصلاح.

الأعراض

تشغيل الحاوية يفشل مع:

docker run myapp:latest
standard_init_linux.go:228: exec user process caused "exec format error"

أو عند تشغيل أمر بشكل واضح:

docker run myapp:latest ./start.sh
exec format error

الخطأ يظهر فورًا بعد docker run بدون أي مخرج من الحاوية.

لماذا يحدث

مرتب حسب الاحتمالية:

  1. عدم تطابق المعمارية (amd64 مقابل arm64) — الصورة تم بناؤها لـ Intel/AMD (amd64) لكنك تشغّلها على Apple Silicon أو خادم ARM أو داخل حاوية على معمارية مختلفة.
  2. ينقص shebang في shell script — النص الثنائي ينقصه السطر الأول #!/bin/sh أو #!/bin/bash، إذًا kernel لا يعرف أي مفسّر يستخدم.
  3. CRLF نهايات أسطر بدلاً من LF — النص أو الملف الثنائي تم إنشاؤه على Windows مع CRLF نهايات أسطر. Linux يتوقع LF، إذًا shebang تالفة.
  4. الملف ليس قابلاً للتنفيذ — النص أو الملف الثنائي ينقصه بت التنفيذ (chmod +x). kernel يرفض تشغيل الملفات غير القابلة للتنفيذ.
  5. الملف الثنائي مبني لنظام تشغيل مختلف — الملف الثنائي لـ Windows أو macOS أو توزيعة Linux مختلفة ولا يستطيع التشغيل في حاويتك.
  6. صورة Base غير متوافقة مع الملف الثنائي — Alpine Linux مقابل Debian/Ubuntu لديها إصدارات libc مختلفة؛ ملف ثنائي مبني لواحد قد لا يعمل في الآخر.

الحل

1. عدم تطابق المعمارية (amd64 مقابل arm64)

تحقق من أي معمارية صورتك مبنية لها:

docker inspect myapp:latest | grep -i architecture

مثال المخرجات:

"Architecture": "amd64"

تحقق من معمارية نظامك:

docker run --rm myapp:latest uname -m
# المخرجات: aarch64 (ARM64) أو x86_64 (amd64)

# أو على مضيفك
uname -m

إذا لم تطابق، أعد بناء الصورة للمعمارية الصحيحة باستخدام Docker BuildX:

# فعّل BuildX (عادة مفعّل بالفعل في Docker Desktop)
docker buildx create --use

# بناء لمعمارية هدفك
docker buildx build --platform linux/arm64 \
  -t myregistry/myapp:arm64 \
  --push .

لبناء معماريات متعددة في نفس الوقت:

docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 \
  -t myregistry/myapp:latest \
  --push .

ثم شغّل مع الوسم الصحيح:

# على Apple Silicon (arm64)
docker run --rm myregistry/myapp:latest

# أو حدّد المنصة بشكل صريح إذا كانت صورة متعددة المعمارية موجودة
docker run --rm --platform linux/arm64 myregistry/myapp:latest

للاختبار بدون إعادة البناء، يمكنك محاكاة المعمارية:

# شغّل صورة amd64 على آلة arm64 (بطيء، محاكاة)
docker run --rm --platform linux/amd64 myapp:latest

في Dockerfile، استخدم صورة base متعددة المعمارية:

FROM node:20-alpine    # هذه الصورة تدعم amd64 و arm64 وغيرها
COPY . /app
WORKDIR /app
CMD ["node", "server.js"]

2. ينقص shebang في shell script

النص يجب أن يبدأ بسطر shebang حتى يعرف kernel أي مفسّر يستخدم.

تحقق من النص:

docker run --rm <image> head -1 /app/entrypoint.sh

إذا كان السطر الأول فارغًا أو لا يبدأ بـ #!، أضف shebang:

#!/bin/sh
echo "Starting app"
exec node /app/server.js

أكثر shebangs شيوعًا:

#!/bin/sh          # POSIX shell (الأصغر، الأكثر توافقًا)
#!/bin/bash        # Bash (ميزات أكثر، Alpine ليس لديها بشكل افتراضي)
#!/usr/bin/env sh  # محمول، يبحث عن sh في PATH
#!/usr/bin/python3 # Python
#!/usr/bin/node    # Node.js

حدّث Dockerfile:

FROM node:20-alpine
COPY entrypoint.sh /app/entrypoint.sh
RUN chmod +x /app/entrypoint.sh
COPY . /app
WORKDIR /app
ENTRYPOINT ["/app/entrypoint.sh"]

وتأكد من أن النص لديه shebang:

#!/bin/sh
set -e
exec node /app/server.js

أعد البناء والاختبار:

docker build -t myapp:latest .
docker run --rm myapp:latest

3. CRLF نهايات أسطر بدلاً من LF

إذا تم إنشاء النص على Windows، قد يكون لديه CRLF نهايات أسطر. تحقق:

docker run --rm <image> file /app/entrypoint.sh
# المخرجات: ASCII text, with CRLF line terminators

أو:

docker run --rm <image> od -c /app/entrypoint.sh | head
# ابحث عن \r\n بدلاً من فقط \n

أصلح في Dockerfile باستخدام dos2unix أو sed:

FROM node:20-alpine
RUN apk add --no-cache dos2unix
COPY entrypoint.sh /app/entrypoint.sh
RUN dos2unix /app/entrypoint.sh && chmod +x /app/entrypoint.sh

أو استخدم sed (بدون تبعية إضافية):

FROM node:20-alpine
COPY entrypoint.sh /app/entrypoint.sh
RUN sed -i 's/\r$//' /app/entrypoint.sh && chmod +x /app/entrypoint.sh

أمر sed يزيل carriage returns (\r).

بدلاً من ذلك، كوّن Git لاستخدام LF على Windows:

git config core.autocrlf false

ثم أضف والتزم الملف مجددًا:

git rm --cached entrypoint.sh
git add entrypoint.sh
git commit -m "Fix line endings"

أعد بناء الصورة:

docker build -t myapp:latest .
docker run --rm myapp:latest

4. الملف ليس قابلاً للتنفيذ

النص يجب أن يكون لديه بت التنفيذ المعيّن. تحقق في Dockerfile:

FROM node:20-alpine
COPY entrypoint.sh /app/entrypoint.sh
RUN chmod +x /app/entrypoint.sh    # اجعله قابلاً للتنفيذ
ENTRYPOINT ["/app/entrypoint.sh"]

تحقق:

docker run --rm <image> ls -la /app/entrypoint.sh
# يجب أن يظهر: -rwxr-xr-x (مع x في الصلاحيات)

تأكد أيضًا من أن الملف تم الالتزام به مع صلاحيات التنفيذ:

# على مضيفك (Linux/macOS)
chmod +x entrypoint.sh
git add entrypoint.sh
git commit -m "Add execute permission"

مستخدمو Windows: إذا كنت على Windows والالتزام إلى Git، كوّن Git للحفاظ على الصلاحيات:

git config core.fileMode true

ثم:

chmod +x entrypoint.sh    # هذا يعمل في Git Bash أو WSL2
git add entrypoint.sh
git commit -m "Add execute permission"

أعد البناء:

docker build -t myapp:latest .
docker run --rm myapp:latest

5. الملف الثنائي مبني لنظام تشغيل مختلف

إذا كان لديك ملف ثنائي مترجم (مثل Go أو Rust أو C++)، يجب أن يكون مترجمًا لـ Linux. تحقق:

# على مضيفك
file /usr/bin/myapp
# يجب أن يظهر: ELF 64-bit LSB executable, x86-64, ...

# لا تبني على macOS وتحاول التشغيل في حاوية Linux
# دائمًا بناء داخل الحاوية أو cross-compile بشكل صحيح

في Dockerfile، بناء الملف الثنائي داخل الحاوية:

# بناء متعدد المراحل
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o myapp .

FROM alpine:3.20
COPY --from=builder /app/myapp /usr/local/bin/myapp
RUN chmod +x /usr/local/bin/myapp
ENTRYPOINT ["myapp"]

الرئيسي هو GOOS=linux GOARCH=amd64 لـ cross-compile لـ Linux. لـ لغات أخرى:

Rust:

FROM rust:latest AS builder
WORKDIR /app
COPY . .
RUN cargo build --target x86_64-unknown-linux-gnu --release

FROM alpine:3.20
COPY --from=builder /app/target/x86_64-unknown-linux-gnu/release/myapp /usr/local/bin/myapp

Node.js (ملفات ثنائية مترجمة):
عادة ليس ضروريًا لـ Node.js، لكن إذا كنت تستخدم وحدات أصلية، أعد بناءها في الحاوية:

FROM node:20-alpine
WORKDIR /app
COPY . .
RUN npm install

6. صورة Base غير متوافقة مع الملف الثنائي

إذا تم بناء ملف ثنائي على Ubuntu لكنك تستخدم Alpine Linux (libc مختلفة)، قد لا يعمل. تحقق:

docker run --rm ubuntu:24.04 ldd /usr/bin/node | head
# يظهر glibc ومكتبات أخرى

docker run --rm alpine:3.20 ldd /usr/bin/node | head
# يظهر musl libc (مختلف عن glibc)

ملف ثنائي مبني مع glibc لن يعمل مع musl libc. استخدم صورة base التي تطابق الملف الثنائي:

# إذا بني لـ glibc (Ubuntu, Debian):
FROM debian:bookworm
COPY myapp /usr/local/bin/myapp
RUN chmod +x /usr/local/bin/myapp

# إذا بني لـ musl (Alpine):
FROM alpine:3.20
COPY myapp /usr/local/bin/myapp
RUN chmod +x /usr/local/bin/myapp

أو أعد بناء الملف الثنائي في صورة base صحيحة:

FROM debian:bookworm AS builder
RUN apt-get update && apt-get install -y build-essential
WORKDIR /app
COPY . .
RUN make build

FROM debian:bookworm
COPY --from=builder /app/myapp /usr/local/bin/myapp

كيف تتجنبه

  1. دائمًا اختبر الصورة قبل الدفع:

    docker build -t myapp:latest .
    docker run --rm myapp:latest
    

  2. استخدم الأبنية متعددة المعمارية من البداية:

    docker buildx build --platform linux/amd64,linux/arm64 \
      -t myregistry/myapp:latest --push .
    

  3. أضف shebangs إلى جميع shell scripts:

    #!/bin/sh
    set -e
    exec mycommand
    

  4. تحقق من نهايات الأسطر عند نسخ الملفات من Windows:

    RUN sed -i 's/\r$//' /app/entrypoint.sh
    

  5. اجعل scripts قابلة للتنفيذ في Dockerfile:

    RUN chmod +x /app/entrypoint.sh
    

  6. استخدم الأبنية متعددة المراحل لتجميع الملفات الثنائية داخل الحاوية:

    FROM golang:1.22 AS builder
    RUN go build -o myapp .
    
    FROM alpine:3.20
    COPY --from=builder /app/myapp /usr/local/bin/myapp
    

  7. طابق صورة base مع libc الملف الثنائي:
    – glibc (Ubuntu, Debian, CentOS) → استخدم تلك الصور الأساسية
    – musl (Alpine) → استخدم Alpine

الأسئلة الشائعة

لماذا تفشل حاويتي مع ‘exec format error’ على Apple Silicon؟

صورتك مبنية لـ amd64 (Intel). أعد البناء مع docker buildx --platform linux/arm64,linux/amd64 أو شغّلها محاكاة مع docker run --platform linux/amd64.

ماذا يعني ‘exec format error’ بالفعل؟

kernel لا يستطيع تنفيذ الملف الثنائي. إما أنها معمارية خاطئة، الملف بلا صلاحيات تنفيذ، ينقصه shebang (للنصوص)، أو لديه CRLF نهايات الأسطر.

كيف أتحقق من المعمارية التي بنيت عليها صورة Docker؟

شغّل docker inspect <image> | grep Architecture أو docker run <image> uname -m. تحقق أيضًا من docker buildx ls لرؤية الأنصات المتاحة.

لماذا فشلت shell script في Docker مع ‘exec format error’؟

النص الثنائي ينقصه shebang (السطر الأول يجب أن يكون #!/bin/sh أو #!/bin/bash)، له CRLF نهايات أسطر (dos2unix يستطيع الإصلاح)، أو ليس قابلاً للتنفيذ (chmod +x في Dockerfile).

كيف تم التحقق من هذا الدليل

كُتب يدويًا من الوثائق الرسمية، وأُعيد فحص كل أمر، ثم رُوجع مرة ثانية قبل النشر. آخر مراجعة في .

المصادر الرسمية التي قُرئت لهذا المقال

شاركها.

التعليقات مغلقة.