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 دقائق — ثلاث حاويات، شبكة واحدة، ووحدتا تخزين.
# نظرة عامة على البنية: Nginx → PHP-FPM → MySQL
docker network create lemp-net
docker volume create mysql-data
(3) الفائدة: 10 دقائق مقابل نصف يوم
يستغرق النشر اليدوي نصف يوم (ولا يمكن إعادة استخدامه)، بينما يستغرق النشر بـ Docker 10 دقائق (البرامج النصية قابلة لإعادة الاستخدام ويمكن مشاركتها مع الفريق). تم تقليل انضمام أعضاء الفريق الجدد من يوم كامل لإعداد البيئة إلى 15 دقيقة.
3. تصميم بنية LEMP
(1) نظرة عامة على البنية
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 لأسماء الحاويات.
# إنشاء شبكة bridge مخصصة لمجموعة LEMP
docker network create lemp-net
# التحقق
docker network ls | grep lemp-net
(2) إنشاء وحدة تخزين بيانات
يجب أن تكون بيانات MySQL دائمة — يجب ألا تُفقد البيانات عند حذف الحاوية.
# إنشاء وحدة تخزين مسماة لبيانات 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 |
بيانات مؤقتة، معلومات حساسة | ❌ تختفي عند توقف الحاوية |
- نشر حاوية MySQL
▶ مثال: بدء حاوية MySQL (الصعوبة: ⭐⭐)
# بدء 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
docker logs -f lemp-mysql لمراقبة تقدم التهيئة؛ بمجرد رؤية "ready for connections"، تكون التهيئة قد اكتملت.
(1) التحقق من أن MySQL قيد التشغيل
# التحقق من أن 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 وملف اختبار اتصال قاعدة البيانات.
# إنشاء دليل المشروع
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 (الصعوبة: ⭐⭐)
# بدء حاوية 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.
# إنشاء إعدادات 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 (الصعوبة: ⭐⭐)
# بدء حاوية 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 للتحقق (الصعوبة: ⭐)
# اختبار صفحة معلومات PHP
curl http://localhost:8080/info.php | head -5
# اختبار اتصال قاعدة البيانات
curl http://localhost:8080/db-test.php
# curl http://localhost:8080/db-test.php
Connected to MySQL successfully!
MySQL version: 8.0.35
(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 (الصعوبة: ⭐⭐⭐)
# 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;"
+----+-------+
| 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
# ============================================
# شرح كامل: نشر مجموعة 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
# 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>
❓ أسئلة شائعة
docker run هذه. البدء يدويًا قبل الانتقال إلى الأتمتة يساعدك على اكتساب فهم أعمق.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 فتح نوافذ طرفية متعددة لمراقبتها في وقت واحد؛ ③ إعادة توجيه السجلات إلى ملف ودمجها للعرض.📖 ملخص
- مجموعة LEMP = Linux + Nginx + MySQL + PHP، بنية تطبيق ويب كلاسيكية
- شبكة مخصصة
lemp-net: تمكّن الاتصال بين الحاويات عبر تحليل DNS باستخدام أسماء الحاويات - وحدة تخزين مسماة لاستمرار بيانات قاعدة البيانات؛ تركيب Bind لمزامنة كود التطوير
- ترتيب بدء التشغيل: MySQL (يبدأ أولاً) → PHP-FPM → Nginx (يكشف المنفذ آخراً)
fastcgi_pass lemp-php:9000تنفيذ اتصال FastCGI بين الحاويات- التحقق من استمرارية البيانات: بعد حذف حاوية MySQL وإعادة إنشائها، تبقى البيانات في وحدة التخزين المسماة سليمة.
📝 تمارين
- تمرين أساسي (الصعوبة: ⭐): انشر مجموعة LEMP من الصفر، ثم قم بزيارة
http://localhost:8080/index.phpللتحقق من أن الحاويات الثلاث تتعاون بشكل صحيح. - تمرين متقدم (الصعوبة: ⭐⭐): قم بمحاكاة تعطل حاوية MySQL (
docker stop lemp-mysql && docker rm lemp-mysql)، وبعد إعادة بنائها، تحقق مما إذا كانت بيانات قاعدة البيانات قد استمرت. - تحدٍ (الصعوبة: ⭐⭐⭐): أضف حاوية Adminer (أداة إدارة قواعد البيانات) للاتصال بـ MySQL، واستخدم واجهة الويب لإدارة قاعدة البيانات. تلميح:
docker run -d --name adminer --network lemp-net -p 8081:8080 adminer؛ قم بزيارةhttp://localhost:8081وسجل الدخول باستخدام appuser/apppass.