Docker: مشروع المرحلة الأولى

آخر تحديث: 2026-08-26

الآن دعنا نربط ما تعلمناه في الدروس الخمسة الأولى — نشر مجموعة LEMP كاملة (Linux + Nginx + MySQL + PHP) من الصفر. هذا هو المشروع النهائي الشامل للمرحلة الأولى.

1. ما ستتعلمه



2. قصة حقيقية لمطور خلفية

(1) نقطة الألم: إعداد بيئة تطوير يدويًا يستغرق نصف يوم

تحتاج أليس إلى إعداد بيئة تطوير لنظام الإدارة الداخلي للشركة: سيخدم Nginx المحتوى الثابت، وسيتعامل PHP-FPM مع الطلبات الديناميكية، وسيخزن MySQL البيانات. يتطلب التثبيت اليدوي إعداد مضيفات Nginx الافتراضية، وتثبيت PHP-FPM وامتداداته، وتثبيت وتهيئة MySQL، وإعداد مسارات الاتصال بين المكونات الثلاثة. هناك عقبات محتملة في كل خطوة؛ استغرق زميل يومًا كاملاً لإعداد كل شيء.

(2) حلول النشر متعدد الحاويات في Docker

استخدمت أليس Docker لإعداد مجموعة LEMP كاملة في 10 دقائق — ثلاث حاويات، شبكة واحدة، ووحدتا تخزين.

BASH
# نظرة عامة على البنية: Nginx → PHP-FPM → MySQL
docker network create lemp-net
docker volume create mysql-data

(3) الفائدة: 10 دقائق مقابل نصف يوم

يستغرق النشر اليدوي نصف يوم (ولا يمكن إعادة استخدامه)، بينما يستغرق النشر بـ Docker 10 دقائق (البرامج النصية قابلة لإعادة الاستخدام ويمكن مشاركتها مع الفريق). تم تقليل انضمام أعضاء الفريق الجدد من يوم كامل لإعداد البيئة إلى 15 دقيقة.



3. تصميم بنية LEMP

(1) نظرة عامة على البنية

100%
graph TB
    USER["متصفح المستخدم<br/>:8080"] --> NGX["حاوية Nginx<br/>:80<br/>ملفات ثابتة + وكيل PHP"]
    NGX -->|"FastCGI :9000"| PHP["حاوية PHP-FPM<br/>:9000<br/>معالجة PHP"]
    PHP -->|"MySQL :3306"| DB["حاوية MySQL<br/>:3306<br/>قاعدة البيانات"]
    DB --> VOL["وحدة تخزين مسماة<br/>mysql-data<br/>تخزين دائم"]
    PHP --> PHPV["تركيب Bind<br/>./src → /var/www/html<br/>كود PHP المصدر"]
    NGX --> NGXV["تركيب Bind<br/>./nginx.conf<br/>إعدادات Nginx"]

(2) مقارنة النشر اليدوي مقابل النشر بـ Docker

البُعد النشر اليدوي النشر بـ Docker
الوقت المطلوب 4–8 ساعات 10 دقائق
قابلية إعادة الإنتاج منخفضة (تعتمد على خبرة المشغل) عالية (البرامج النصية/الأوامر يمكن إعادة استخدامها)
قابلية النقل منخفضة (محددة بنظام التشغيل) عالية (متسقة عبر المنصات)
عزل البيئة لا يوجد (بيئة نظام مشتركة) معزولة تمامًا
التراجع صعب (إلغاء التثبيت وإعادة التثبيت) سهل (حذف الحاوية وإعادة البناء)
مشاركة الفريق كتابة وثائق + اتباع التعليمات مشاركة الأوامر/البرامج النصية


4. التحضيرات: الشبكة ووحدات التخزين

(1) إنشاء شبكة مخصصة

يتطلب الاتصال بين الحاويات شبكة bridge مخصصة — شبكة bridge الافتراضية لا تدعم تحليل DNS لأسماء الحاويات.

BASH
# إنشاء شبكة bridge مخصصة لمجموعة LEMP
docker network create lemp-net

# التحقق
docker network ls | grep lemp-net

(2) إنشاء وحدة تخزين بيانات

يجب أن تكون بيانات MySQL دائمة — يجب ألا تُفقد البيانات عند حذف الحاوية.

BASH
# إنشاء وحدة تخزين مسماة لبيانات MySQL
docker volume create mysql-data

# التحقق
docker volume ls | grep mysql-data

(3) مقارنة استراتيجيات تركيب وحدات التخزين

الاستراتيجية الصيغة حالات الاستخدام الاستمرارية
وحدة تخزين مسماه -v mysql-data:/var/lib/mysql قاعدة البيانات، التخزين الدائم ✅ محتفظ بها بعد حذف الحاوية
تركيب Bind -v ./src:/var/www/html مزامنة كود بيئة التطوير ✅ تعيين إلى دليل المضيف
tmpfs --tmpfs /tmp بيانات مؤقتة، معلومات حساسة ❌ تختفي عند توقف الحاوية


  1. نشر حاوية MySQL

▶ مثال: بدء حاوية MySQL (الصعوبة: ⭐⭐)

BASH
# بدء MySQL 8.0 مع تخزين دائم
docker run -d \
  --name lemp-mysql \
  --network lemp-net \
  -e MYSQL_ROOT_PASSWORD=rootsecret \
  -e MYSQL_DATABASE=appdb \
  -e MYSQL_USER=appuser \
  -e MYSQL_PASSWORD=apppass \
  -v mysql-data:/var/lib/mysql \
  --restart=unless-stopped \
  mysql:8.0
💡 نصيحة: ابدأ MySQL أولاً، وانتظر حتى يكتمل التهيئة (حوالي 30 ثانية) قبل بدء الحاويات الأخرى. يمكنك استخدام docker logs -f lemp-mysql لمراقبة تقدم التهيئة؛ بمجرد رؤية "ready for connections"، تكون التهيئة قد اكتملت.

(1) التحقق من أن MySQL قيد التشغيل

BASH
# التحقق من أن MySQL قيد التشغيل
docker ps --filter name=lemp-mysql

# الاتصال بـ MySQL للتحقق
docker exec -it lemp-mysql mysql -uappuser -papppass appdb -e "SELECT 1;"


5. نشر حاوية PHP-FPM

(1) تحضير كود PHP

أولاً، أنشئ ملف اختبار PHP وملف اختبار اتصال قاعدة البيانات.

BASH
# إنشاء دليل المشروع
mkdir -p lemp-project/src

# إنشاء صفحة معلومات PHP
cat > lemp-project/src/info.php << 'EOF'
<?php
phpinfo();
?>
EOF

# إنشاء اختبار اتصال قاعدة البيانات
cat > lemp-project/src/db-test.php << 'EOF'
<?php
$host = 'lemp-mysql';
$db   = 'appdb';
$user = 'appuser';
$pass = 'apppass';

try {
    $pdo = new PDO("mysql:host=$host;dbname=$db", $user, $pass);
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    echo "Connected to MySQL successfully!<br>";
    echo "MySQL version: " . $pdo->query("SELECT VERSION()")->fetchColumn();
} catch (PDOException $e) {
    echo "Connection failed: " . $e->getMessage();
}
?>
EOF

▶ مثال: بدء حاوية PHP-FPM (الصعوبة: ⭐⭐)

BASH
# بدء حاوية PHP-FPM
docker run -d \
  --name lemp-php \
  --network lemp-net \
  -v $(pwd)/lemp-project/src:/var/www/html \
  --restart=unless-stopped \
  php:8.2-fpm
📌 نقطة أساسية: -v $(pwd)/lemp-project/src:/var/www/html هو تركيب bind — يعين دليل الكود المحلي إلى الحاوية، لذا فإن التغييرات التي تُجرى على الملفات المحلية تسري فورًا داخل الحاوية، مما يجعله مناسبًا للتطوير وتصحيح الأخطاء.



6. نشر حاوية Nginx

(1) تحضير إعدادات Nginx

يحتاج Nginx إلى التهيئة كوكيل FastCGI لإعادة توجيه طلبات PHP إلى حاوية PHP-FPM.

BASH
# إنشاء إعدادات Nginx
cat > lemp-project/nginx.conf << 'EOF'
server {
    listen 80;
    server_name localhost;
    root /var/www/html;
    index index.php index.html;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        fastcgi_pass lemp-php:9000;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.ht {
        deny all;
    }
}
EOF
📌 نقطة أساسية: lemp-php في fastcgi_pass lemp-php:9000 هو اسم حاوية PHP-FPM. نظرًا لأنهما في نفس الشبكة المخصصة lemp-net، يقوم DNS Docker تلقائيًا بتحليل اسم الحاوية إلى عنوان IP الخاص بالحاوية.

▶ مثال: بدء حاوية Nginx (الصعوبة: ⭐⭐)

BASH
# بدء حاوية Nginx
docker run -d \
  --name lemp-nginx \
  --network lemp-net \
  -p 8080:80 \
  -v $(pwd)/lemp-project/src:/var/www/html \
  -v $(pwd)/lemp-project/nginx.conf:/etc/nginx/conf.d/default.conf \
  --restart=unless-stopped \
  nginx:1.25-alpine


7. التحقق والاختبار

▶ مثال: الوصول إلى صفحة PHP للتحقق (الصعوبة: ⭐)

BASH
# اختبار صفحة معلومات PHP
curl http://localhost:8080/info.php | head -5

# اختبار اتصال قاعدة البيانات
curl http://localhost:8080/db-test.php
💻 المخرجات:

TEXT 📖 للعرض فقط
# curl http://localhost:8080/db-test.php
Connected to MySQL successfully!
MySQL version: 8.0.35
✅ نقطة تحقق: إذا رأيت "Connected to MySQL successfully!"، فهذا يعني أن الحاويات الثلاث تتعاون بنجاح: Nginx → PHP-FPM → MySQL.

(1) قائمة التحقق الكاملة

عنصر الاختبار الأمر النتيجة المتوقعة
جميع الحاويات الثلاث قيد التشغيل docker ps 3 حاويات في حالة "Up"
صفحة PHP تعرض بشكل طبيعي curl localhost:8080/info.php إرجاع معلومات إعدادات PHP
اتصال قاعدة البيانات طبيعي curl localhost:8080/db-test.php "Connected successfully"
تحليل DNS بين الحاويات docker exec lemp-nginx ping -c 2 lemp-php يمكن ping بنجاح
وحدة تخزين البيانات موجودة docker volume ls mysql-data في القائمة


8. التحقق من استمرارية البيانات

▶ مثال: محاكاة تعطل حاوية MySQL (الصعوبة: ⭐⭐⭐)

BASH
# 1. إنشاء جدول اختبار مع بيانات
docker exec -it lemp-mysql mysql -uappuser -papppass appdb -e \
  "CREATE TABLE test_items (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(100)); INSERT INTO test_items (name) VALUES ('Alice'), ('Bob');"

# 2. التحقق من وجود البيانات
docker exec -it lemp-mysql mysql -uappuser -papppass appdb -e \
  "SELECT * FROM test_items;"

# 3. إيقاف وإزالة حاوية MySQL
docker stop lemp-mysql && docker rm lemp-mysql

# 4. إعادة إنشاء حاوية MySQL بنفس وحدة التخزين
docker run -d \
  --name lemp-mysql \
  --network lemp-net \
  -e MYSQL_ROOT_PASSWORD=rootsecret \
  -e MYSQL_DATABASE=appdb \
  -e MYSQL_USER=appuser \
  -e MYSQL_PASSWORD=apppass \
  -v mysql-data:/var/lib/mysql \
  --restart=unless-stopped \
  mysql:8.0

# 5. انتظر حتى يبدأ MySQL، ثم تحقق من أن البيانات لا تزال موجودة
docker exec -it lemp-mysql mysql -uappuser -papppass appdb -e \
  "SELECT * FROM test_items;"
💻 المخرجات:

TEXT 📖 للعرض فقط
+----+-------+
| id | name  |
+----+-------+
|  1 | Alice |
|  2 | Bob   |
+----+-------+
✅ نقطة تحقق: تبقى البيانات بعد حذف الحاوية وإعادة إنشائها — استمرارية وحدة التخزين المسماة تعمل.



9. سياسات ترتيب الحاويات والاعتماديات

الاستراتيجية الطريقة الموثوقية
بدء تشغيل تسلسلي يدوي MySQL → PHP → Nginx متوسطة (تعتمد على الخبرة)
فحص الصحة المعلق انتظار MySQL جاهز قبل إعادة تشغيل PHP عالية (آلية)
استراتيجية إعادة التشغيل --restart=on-failure إعادة محاولة تلقائية للخدمات الفاشلة متوسطة (الترتيب غير مضمون)
Docker Compose depends_on + فحوصات الصحة (دراسة المرحلة الثالثة) عالية (تصريحية)
💡 نصيحة: يستخدم هذا الدرس تسلسل بدء تشغيل يدوي. في المرحلة الثالثة، ستتعلم عن depends_on في Docker Compose لأتمتة إدارة الاعتماديات.



10. مثال كامل: برنامج نصي للنشر بنقرة واحدة لمجموعة LEMP

BASH
# ============================================
# شرح كامل: نشر مجموعة LEMP من الصفر
# يغطي: الشبكة، وحدة التخزين، MySQL، PHP، Nginx
# ============================================

# --- الإعداد ---
# إنشاء الشبكة ووحدة التخزين
docker network create lemp-net
docker volume create mysql-data

# إنشاء دليل المشروع والملفات
mkdir -p lemp-project/src

cat > lemp-project/src/index.php << 'PHPEOF'
<?php
$host = 'lemp-mysql';
$db   = 'appdb';
$user = 'appuser';
$pass = 'apppass';
try {
    $pdo = new PDO("mysql:host=$host;dbname=$db", $user, $pass);
    echo "<h1>LEMP Stack is working!</h1>";
    echo "<p>MySQL version: " . $pdo->query("SELECT VERSION()")->fetchColumn() . "</p>";
} catch (PDOException $e) {
    echo "<h1>Database connection failed</h1><p>" . $e->getMessage() . "</p>";
}
?>
PHPEOF

cat > lemp-project/nginx.conf << 'NGINXEOF'
server {
    listen 80;
    server_name localhost;
    root /var/www/html;
    index index.php index.html;
    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }
    location ~ \.php$ {
        fastcgi_pass lemp-php:9000;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}
NGINXEOF

# --- النشر ---
# 1. بدء MySQL (يجب أن يكون أولاً)
docker run -d \
  --name lemp-mysql \
  --network lemp-net \
  -e MYSQL_ROOT_PASSWORD=rootsecret \
  -e MYSQL_DATABASE=appdb \
  -e MYSQL_USER=appuser \
  -e MYSQL_PASSWORD=apppass \
  -v mysql-data:/var/lib/mysql \
  --restart=unless-stopped \
  mysql:8.0

# 2. انتظار تهيئة MySQL (~30 ثانية)
echo "Waiting for MySQL to initialize..."
sleep 30

# 3. بدء PHP-FPM
docker run -d \
  --name lemp-php \
  --network lemp-net \
  -v $(pwd)/lemp-project/src:/var/www/html \
  --restart=unless-stopped \
  php:8.2-fpm

# 4. بدء Nginx
docker run -d \
  --name lemp-nginx \
  --network lemp-net \
  -p 8080:80 \
  -v $(pwd)/lemp-project/src:/var/www/html \
  -v $(pwd)/lemp-project/nginx.conf:/etc/nginx/conf.d/default.conf \
  --restart=unless-stopped \
  nginx:1.25-alpine

# --- التحقق ---
echo "Verifying deployment..."
docker ps
curl -s http://localhost:8080/index.php
💻 المخرجات (مقتطف):

TEXT 📖 للعرض فقط
# docker ps
CONTAINER ID   IMAGE            STATUS         PORTS                  NAMES
c1d2e3f4       nginx:1.25-alpine  Up 5 min      0.0.0.0:8080->80/tcp   lemp-nginx
b2c3d4e5       php:8.2-fpm      Up 5 min       9000/tcp               lemp-php
a1b2c3d4       mysql:8.0        Up 5 min       3306/tcp, 33060/tcp    lemp-mysql

# curl http://localhost:8080/index.php
<h1>LEMP Stack is working!</h1><p>MySQL version: 8.0.35</p>

❓ أسئلة شائعة

س لماذا نبدأها بشكل منفصل بدلاً من استخدام Compose؟
ج هذا تمرين شامل للمرحلة الأولى، مصمم لمساعدتك على فهم كيفية إنشاء كل حاوية وربطها بشكل مستقل. في المرحلة الثالثة، ستتعلم عن Docker Compose، الذي يستخدم ملف YAML واحدًا ليحل محل أوامر docker run هذه. البدء يدويًا قبل الانتقال إلى الأتمتة يساعدك على اكتساب فهم أعمق.
س كيف يتصل Nginx بـ PHP-FPM؟
ج من خلال بروتوكول FastCGI. في إعدادات Nginx، يشير fastcgi_pass lemp-php:9000 إلى حاوية PHP-FPM. lemp-php هو اسم الحاوية؛ داخل نفس الشبكة المخصصة، يحلل DNS Docker تلقائيًا إلى عنوان IP الخاص بالحاوية، لذا لا حاجة لترميز عنوان IP.
س هل لا تزال البيانات موجودة بعد إعادة تشغيل الحاوية؟
ج البيانات المركبة باستخدام وحدة تخزين مسماة (-v mysql-data:/var/lib/mysql) تبقى حتى بعد حذف الحاوية. ومع ذلك، الكود المصدري لتركيب Bind (-v ./src:/var/www/html) معين إلى دليل المضيف؛ طالما لم يتم حذف دليل المضيف، لن يُفقد الكود.
س كيف أعرف أي حاوية تسبب الخطأ؟
ج طريقة استكشاف الأخطاء بثلاث خطوات: ① docker ps -a تحقق من الحاوية التي حالتها ليست "Up"؛ ② docker logs <حاوية> تحقق من سجلات الأخطاء؛ ③ docker inspect <حاوية> --format='{{.State.ExitCode}}' تحقق من رمز الخروج. المشكلة الشائعة: أخطاء اتصال PHP تحدث عندما لا يكون MySQL جاهزًا؛ ببساطة انتظر حتى ينتهي MySQL من التهيئة.
س كيف يمكنني عرض سجلات حاويات متعددة في نفس الوقت؟
ج docker compose logs (المتعلم في المرحلة الثالثة) يسمح بالتجميع والعرض. حاليًا، يمكنك: ① docker logs لكل حاوية بشكل فردي؛ ② docker logs -f فتح نوافذ طرفية متعددة لمراقبتها في وقت واحد؛ ③ إعادة توجيه السجلات إلى ملف ودمجها للعرض.

📖 ملخص


📝 تمارين

  1. تمرين أساسي (الصعوبة: ⭐): انشر مجموعة LEMP من الصفر، ثم قم بزيارة http://localhost:8080/index.php للتحقق من أن الحاويات الثلاث تتعاون بشكل صحيح.
  2. تمرين متقدم (الصعوبة: ⭐⭐): قم بمحاكاة تعطل حاوية MySQL (docker stop lemp-mysql && docker rm lemp-mysql)، وبعد إعادة بنائها، تحقق مما إذا كانت بيانات قاعدة البيانات قد استمرت.
  3. تحدٍ (الصعوبة: ⭐⭐⭐): أضف حاوية Adminer (أداة إدارة قواعد البيانات) للاتصال بـ MySQL، واستخدم واجهة الويب لإدارة قاعدة البيانات. تلميح: docker run -d --name adminer --network lemp-net -p 8081:8080 adminer؛ قم بزيارة http://localhost:8081 وسجل الدخول باستخدام appuser/apppass.
Web-Tutorial.com

فريق Web-Tutorial التقني

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

100%