الرئيسية المنتدى مركز رفع الصور صفحتنا على الفيس قناة اليوتيوب صفحتنا على تويتر واتس اب قوانين المنتدى
منتدى مجمع التطوير    

العودة   منتدى مجمع التطوير > المنتدى المتخصص > لغـات البرمجـة والمشـاريع الجـاهزة > برمجة الويب web development

الملاحظات

برمجة الويب web development يهتم ببرامج الويب php, Sql, Java ,asp.net ,xml ,html

آخر 10 مشاركات افعل الخير ولا تنظر جزاءه الا من الله (الكاتـب : admin - مشاركات : 0 - المشاهدات : 36 - الوقت: 08:12 PM - التاريخ: 07-27-2021)           »          دليلك لبناء الباك لينك Backlink لموقعك بطريقة صحيحة (الكاتـب : admin - مشاركات : 2 - المشاهدات : 280 - الوقت: 10:15 PM - التاريخ: 07-26-2021)           »          ملف ال robots.txt وتوجيه عناكب محركات البحث لموقعك (الكاتـب : admin - مشاركات : 2 - المشاهدات : 345 - الوقت: 01:24 PM - التاريخ: 07-21-2021)           »          أهلا وسهلا أليت تيم رياك (الكاتـب : رياك مشار - مشاركات : 2 - المشاهدات : 272 - الوقت: 05:58 PM - التاريخ: 07-03-2021)           »          تحميل لعبة فرايدي نايت فانكن Friday Night Funkin للكمبيوتر 2021 (الكاتـب : الادارة كريم - آخر مشاركة : admin - مشاركات : 2 - المشاهدات : 523 - الوقت: 10:41 PM - التاريخ: 07-01-2021)           »          برنامج البيع بالتقسيط مجاني مصمم بالاكسيس (الكاتـب : ابن الوليد - آخر مشاركة : admin - مشاركات : 1 - المشاهدات : 642 - الوقت: 04:37 AM - التاريخ: 06-25-2021)           »          الصحابي الجليل سعد بن معاذ الأنصاري (الكاتـب : admin - مشاركات : 0 - المشاهدات : 290 - الوقت: 01:25 AM - التاريخ: 06-25-2021)           »          كيفية صلاة الحاجة (الكاتـب : admin - مشاركات : 0 - المشاهدات : 263 - الوقت: 12:13 AM - التاريخ: 06-25-2021)           »          Format FactoryV5.7.5.0 (الكاتـب : admin - مشاركات : 0 - المشاهدات : 301 - الوقت: 11:01 PM - التاريخ: 06-24-2021)           »          رجل أقسم بأن لا يتزوج حتى يشاور مائة رجل (الكاتـب : admin - مشاركات : 0 - المشاهدات : 305 - الوقت: 09:57 PM - التاريخ: 06-24-2021)

إضافة رد
 
أدوات الموضوع انواع عرض الموضوع
  #1  
قديم 05-28-2021, 03:37 AM
الصورة الرمزية الادارة كريم
الادارة كريم 
مشرف سابق
 
تاريخ التسجيل: May 2021
الدولة: مصر
المشاركات: 397
معدل تقييم المستوى: 18
الادارة كريم is a splendid one to beholdالادارة كريم is a splendid one to beholdالادارة كريم is a splendid one to beholdالادارة كريم is a splendid one to beholdالادارة كريم is a splendid one to beholdالادارة كريم is a splendid one to beholdالادارة كريم is a splendid one to behold


افتراضي مثال واقعي على استخدام حاويات Docker لتشغيل موقع ويب

 

مقدمة

في هذا الدرس سنقوم بتطبيق ما تعلمناه مسبقا لتوفير بيئة عمل لتطبيق PHP باستخدام إطار العمل Laravel.
كما تعلمنا سابقا، فان من الممارسات الجيدة فصل الحاويات بحيث تقدم كل حاوية خدمة واحدة، ففي هذا المثال سننشئ ثلاث حاويات، حاوية لسيرفر Nginx، وحاوية لمفسر PHP، وأخرى لـ MySql. بعد هذا المثال سيتضح لك بشكل أفضل فائدة فصل الخدمات بعضها عن بعض، فلو احتجت الآن إلى Redis في المشروع فكل ما عليك اضافة الصورة الخاصة به وكتابة بضعة أسطر في ملف Docker Compose لتربطه ببقية الحاويات، كذلك لو احتجت لتغيير نوع قاعدة البيانات، فكل ما عليك فعله تغيير الصورة التي تستدعيها لقاعدة البيانات وهكذا.
إضافة إلى ذلك سنقوم بكتابة سكربت بسيط ليقوم بإنشاء مشروع Laravel بشكل تلقائي في حال عدم توفر مشروع.

ملفات المشروع


هذه هي الملفات التي سنحتاجها لانشاء المشروع وسنوضح محتويات الملفات قبل البدء بشرحها:
  • env. : هو ملف يحتوي على إعدادات Laravel وإعدادات الحاويات التي سنقوم بانشائها.
  • docker-compose.yml: ملف Docker Compose الذي سيقوم بإدارة وانشاء الحاويات.
  • nginx.conf: ملف إعدادات سيرفر Nginx، لتفاصيل أكثر راجع هذه الدروس: (1, 2).
  • php-entry.sh: سكربت bash يقوم بانشاء مشروع Laravel جديد في حال عدم وجود مشروع.
  • php.Dockerfile: الملف الذي سنقوم من خلاله ببناء صورة PHP.
إضافة إلى مجلد www الذي سيحتوي على ملفات المشروع.

إنشاء ملف الإعدادات

تستخدم لارافيل ملف env. لتحديد إعداداتها، وبما أن Docker Compose يدعم هذا النوع من الملفات فسنقوم باستغلال هذا الملف لتحديد بعض الإعدادات التي سنستخدمها لبناء الحاويات، وبعد الانتهاء من بناء الحاويات سنقوم بنسخ ملف env. إلى مجلد لارافيل لاستخدامه في المشروع وهذه صورة للملف مع توضيح المتغيرات التي سنقوم باستخدامها في ملفات Docker و Docker Compose:


باقي المتغيرات هي متغيرات تُستخدم في لارافيل ولا حاجة لنا بها في إنشاء الحاويات.

إنشاء الحاويات

هكذا ستكون بداية ملف docker-compose.yml:
version: '3' services: # هنا سنقوم بإنشاء الخدمات
بما أن Nginx سيحتاج للاتصال بمفسر PHP ليمرر له ملفات PHP التي يقوم بتشغيلها، فإن أول حاوية يجب تشغيلها هي حاوية Nginx، و PHP ستحتاج للاتصال بقاعدة البيانات لتقوم باستخدامها إذا تطلب المشروع ذلك، وقد تعلمنا أننا نحدد ترتيب انشاء الحاويات باستخدام الأمر depends_on، لكن أثناء الشرح، سنشرح أولا الخدمات ذات التفاصيل الأقل، لذلك سنبدأ بشرح انشاء خدمة Mysql ثم خدمة PHP وأخيرا خدمة Nginx، لكن Docker سيقوم بتشغيل الحاويات بالترتيب الصحيح بسبب استخدام depends_on.

إنشاء خدمة Mysql

database: # database إسم الخدمة الذي سنتعامل معها من خلاله هو image: mysql:5.7 # Mysql صورة environment: MYSQL_DATABASE: ${DB_DATABASE} MYSQL_USER: ${DB_USERNAME} MYSQL_PASSWORD: ${DB_PASSWORD} MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
كما ترى فقد قمنا بتحديد الصورة التي سيتم تحميلها وإنشاء حاوية منها، ولاحظ أننا قمنا بإدراج أربعة متغيرات، وهذه المتغيرات هي المسؤولة عن إعدادات Mysql، فالمتغير MYSQL_DATABASE متغيرة يقوم بإنشاء قاعدة بيانات جديدة - إن لم تكن موجودة مسبقا - بنفس الإسم الذي يُزود به، وكما تُلاحظُ فإننا قمنا بتزويده باسم قاعدة البيانات المحددة في المتغير DB_DATABSE في الملف env.، أما المتغير MYSQL_USER فيقوم بإنشاء مستخدم جديد وإعطائه الصلاحيات اللازمة، و MYSQL_PASSWORD تقوم بتحديد كلمة السر لهذا المستخدم، وتختلف عنها MYSQL_ROOT_PASSWORD بأنها تقوم بتحديد كلمة السر للمستخدم root.
يمكنك قراءة المزيد عن هذه المتغيرات وغيرها من التوثيق الرسمي لـ Mysql أو من التوثيق الرسمي لصورة Mysql على Docker Hub.

لا تزال هناك مشكلتان في الخدمة السابقة، فمن غير الممكن الاتصال بالخدمة دون وجود Port مفتوح يمكن حاوية PHP من الاتصال بحاوية Mysql، كذلك فإن البيانات المخزنة في قاعدة البيانات ستُحذف بمجرد إغلاق الحاوية.
لحل المشكلة الأولى نقوم بتحديد البورت الذي تستخدمه Mysql في الخدمة ليُصبح بالإمكان استخدام هذا البورت للاتصال بقاعدة البيانات، والبورت الافتراضي هو البورت 3306 (تفاصيل).
أما لحل المشكلة الثانية فإننا نقوم بإنشاء Volume بين الجهاز المستضيف، وملفات Mysql داخل الحاوية، وإن لم تُرِد أن تحدد مسارا محددا للملفات فقم بإنشاء Volume جديد وستوضع الملفات ضمن مجلد Docker في جهازك.

وبتنفيذ الحلول السابقة يصبح الملف بهذا الشكل:
version: '3' services: database: image: mysql:5.7 ports: - 3306:3306 volumes: - db:/var/lib/mysql environment: MYSQL_DATABASE: ${DB_DATABASE} MYSQL_USER: ${DB_USERNAME} MYSQL_PASSWORD: ${DB_PASSWORD} MYSQL_ROOT_PASSWORD: ${DB_PASSWORD} volumes: db:
إنشاء خدمة PHP


قبل إنشاء خدمة PHP في ملف docker-compose.yml يجب أن نُجهِّزَ الصورة، فقمنا كما رأيت في ترتيب الملفات بإنشاء ملف php.Dockerfile، في هذا المثال سأستخدم نسخة php 7.1، وسأبدأ الملف بالأمر FROM لتحديد الصورة التي سنبني عليها صورتنا:
FROM php:7.1-fpm
من ثم سنقوم بتنصيب الحزم اللازمة بتشغيل مشروع Laravel من مدير الحزم:
RUN apt-get update && apt-get install -y zlib1g-dev \ libmcrypt-dev && docker-php-ext-install pdo_mysql mcrypt zip
لاحظ استخدام الأمر docker-php-ext-install وهذا الأمر توفره صورة PHP الرسمية على Docker Hub لتسهيل تنصيب إضافات PHP، يمكنك معرفة المزيد من التفاصيل من الصفحة الرسمية لصورة PHP على Docker Hub ( من هنا ).
حتى نتمكن من تنصيب وإعداد مشروع Laravel والتعامل مع حزم PHP، فإننا بحاجة لتنصيب Composer ( مدير حزم PHP ):

RUN php -r "readfile('http://getcomposer.org/installer');" | php -- --install-dir=/usr/bin/ --filename=composer ENV COMPOSER_ALLOW_SUPERUSER 1 الأمر الأول هو أمر عادي يقوم بجلب مُنصب Composer من الموقع الرسمي وتمريره لـ PHP لتقوم بتنصيبه ( فهو عبارة عن سكربت مكتوب بلغة PHP )، أما الأمر الثاني فهو يقوم بإنشاء متغير بيئة في الحاوية، وهذا المتغير يسمح لنا باستخدام Composer من حساب يمتلك صلاحيات Root، فبشكل افتراضي لا يمكنك فعل ذلك إلا بتفعيل هذا المتغير ( تفاصيل ).
حتى الآن أصبح لدينا صورة PHP تستطيع تشغيل مشاريع لارافيل وادارة الحزم باستخدام Composer، لكن تبقى لدينا مشكلة في إدارة الصلاحيات، فلكل مستخدم في Linux رقم يميزه يُسمى UID، و PHP يمكنها تعديل ملف معين على السيرفر باستخدام User باسم www-data، لذلك يجب أن يكون المالك (Owner) لملفات المشروع هو المستخدم www-data أو المجموعة www-data، وغالبا في صورة PHP سيكون الـ UID الخاص بالمستخدم www-data هو 33، لكن الـ UID الخاص بجهازك سيكون مختلفا بالغالب ( يمكنك معرفته بتنفيذ هذا الأمر: id -u )، لذلك ستواجه مشكلة في الصلاحيات، فإذا كان المالك لملفات المشروع هو www-data فلن تتمكن من تعديل الملفات من جهازك الشخصي، أما إن كان مالك المشروع هو المستخدم الخاص بجهازك ( ليكن 1000 مثلا ) فلن يتمكن www-data من الوصول إلى الملفات.

فما الحل في هذه الحالة ؟
في الحقيقة يوجد أكثر من حل، كأن تقوم بإنشاء Group على جهازك والـ GID الخاص به يساوي الـ UID الخاص بـ www-data على الحاوية، وتوجد حلول أخرى أيضا، لكن الحل الأسهل من وجهة نظري هو تغيير الـ UID الخاص بالمستخدم www-data في الحاوية، وجعل الـ UID الجديد يساوي الـ UID الخاص بمستخدم جهازك الشخصي، كذلك بالنسبة للـ GID الخاص بالمجموعة www-data لذلك نضيف هذه الأوامر إلى ملف الصورة:
ARG USER_ID RUN usermod -u ${USER_ID} www-data && groupmod -g ${USER_ID} www-data
لاحظ أننا نطلب منه ARG باسم USER_ID، أي أننا سنمرره له أثناء بناء الصورة ( من داخل ملف docker-compose.yml )، ثم نستخدمه بداخل الأمر usermod و groupmod.

وأخيرا ننهي هذا الملف بأمرين بسيطين:
WORKDIR /var/www RUN chown -R www-data:www-data ./ نحدد الـ WORKDIR في الأمر الأول، والأمر الثاني سيقوم بتغيير مالك الملفات الموجودة داخل /var/www ( والذي سنقوم بعمل Volume له بعد قليل) ليجعل مالكها هو www-data وبالطبع يمكنك تعديل الملفات من جهازك الشخصي فالـ UID نفسه.

بهذا نكون قد أنهينا كتابة الأوامر التي ستُبنى الصورة على أساسها، وسنقوم الآن بكتابة الـ Service في ملف docker-compose.yml، أولا نحدد الصورة التي سيتم بناؤها:
php: build: context: ./ dockerfile: php.Dockerfile
لاحظ أن الأمر build هنا قد أخذ خاصيتين وليس خاصية واحدة كما تعلمنا في الدرس السابق، فسابقا كنا نحدد المجلد الذي يحتوي على ملف Dockerfile، أما هنا فبما أننا قمنا بتغيير اسم الملف إلى (php.Dockerfile) فلن يتم التعرف عليه، ففي الخاصية context نحدد المجلد، أما في الخاصية dockerfile نقوم بتحديد اسم الملف.

وكما رأينا سابقا، فإننا بحاجة إلى تمرير ARG باسم USER_ID إلى الصورة:
php: build: context: ./ dockerfile: php.Dockerfile args: USER_ID: ${USER_ID}
لاحظ أننا نمرر لـ USER_ID متغيرا باسم USER_ID أيضا، وستُجلب قيمته من ملف env.، لذلك فعند إعداد ملف env. عليك وضع الـ UID الخاص بالمستخدم في جهازك كقيمة للمتغير.

باقي الأوامر أوامر عادية سنشرحها بشكل عابر:
php: build: context: ./ dockerfile: php.Dockerfile args: USER_ID: ${USER_ID} volumes: - ./www:/var/www - ./php-entry.sh:/php-entry.sh - ./.env:/.env environment: APP_NAME: ${APP_NAME} ports: - 9000:9000 depends_on: - database working_dir: /var/www user: '1000' entrypoint: '/php-entry.sh'
قمنا بتعريف 3 Volumes، الأول لملفات المشروع ( كما قلنا في بداية الشرح أن المجلد www سيحتوي ملفات المشروع ) ويربط ملفات المشروع بالمسار var/www/ في الحاوية، والثاني ينقل ملف php-entry.sh إلى الحاوية ( لنقوم باستخدامه كـ entrypoint ) أما الثالث فسينقل ملف env. إلى الحاوية لنقوم باستخدامه فيما بعد كملف إعدادات لمشروع Laravel.

من ثم قمنا بإدخال متغير البيئة APP_NAME والذي يحتوي اسم المشروع ( سنستخدم هذا المتغير في الـ Entrypont )، إضافة إلى تحديد البورت الذي تستخدمه php ( لتمكين Nginx من التواصل مع PHP )، وحددنا أن الحاوية ستعتمد على حاوية قاعدتة البيانات.

أما user فيُستخدم لتحديد المستخدم الافتراضي الذي سيتم إدخالنا إليه في حال استخدام الأمر exec.

وفي النهاية حددنا السكربت الذي سيستخدم كـ Entrypoint للحاوية، وهذا هو سكربت الحاوية مشروحا بالتعليقات داخل السكربت ( لاحظ استخدام المتغير APP_NAME الذي عرفناه مسبقا للتعامل مع مجلد المشروع المناسب ).
#! /bin/bash if [ ! -f /var/www/${APP_NAME}/public/index.php ]; then # هذا الشرط يتحقق من وجود مشروع أم لا # وسينفذ الشرط في حالة عدم وجود مشروع composer create-project --prefer-dist laravel/laravel ${APP_NAME}; # في حال عدم وجود مشروع سيقوم أولا بانشاء مشروع جديد rm ./${APP_NAME}/.env; cp /.env ./${APP_NAME}/.env; # ثم نحذف ملف الإعدادات الافتراضي وننقل ملف الاعدادات الخاص بنا cd ${APP_NAME}; php artisan key:generate; # وهذا أمر خاص بلارافيل لتكوين مفتاح جديد fi docker-php-entrypoint php-fpm; # الافتراضية الخاصة بالصورة entrypoint هذه هي الـ # ونقوم بتنفيذها لتعمل الصورة كما يجب بعد انتهاء الشرط الخاص بنا

إنشاء خدمة Nginx



بداية فإننا بحاجة لجلب صورة Nginx وجعل الحاوية تعتمد على حاوية PHP ليتم تشغيل الحاويات بالترتيب المناسب وإعداد البورت، وتذكر أننا قمنا بإنشاء متغير في ملف env. لتحديد البورت الذي سنقوم باستخدامه لـ Nginx ( ملاحظة: في كل الأحوال سنستخدم البورت 80 في إعدادات Nginx، لكننا سنقوم بربطه بالبورت المحدد في ملف env. لنتمكن من الوصل إليه من جهازنا الشخصي):
nginx: image: nginx:1.10.3 ports: - ${NGINX_PORT}:80 depends_on: - php working_dir: /var/www
بالطبع فإن Nginx سيكون بحاجة للوصول إلى ملفات المشروع، لذلك سننشئ Volume لملفات المشروع، وآخر لملف إعدادات Nginx لكن قبل ذلك دعنا نكتب محتويات ملف الإعدادات ودقق على الملاحظات التي سنكتبها ضمن التعليقات:
server { listen 80; server_name localhost; root /var/www/${project_name}/public; # في ملف المشروع public موجود في المجلد index.php ملف # Composer ولاحظ أننا حددنا ملف المشروع الذي سيتم إنشاؤه بواسطة # في الحاوية Environment Variables عن طريق استخدام متغير سنعرفه ضمن index index.html index.htm index.php; charset utf-8; location / { try_files $uri $uri/ /index.php?$query_string; } error_page 404 /index.php; location ~ \.php$ { fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass php:9000; # بمسار nginx بدلا من تزويد php لاحظ أننا مررنا اسم الخدمة وهو # php وقمنا باستخدام البورت 9000 الذي حددناه في خدمة fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; } location ~ /\.(?!well-known).* { deny all; } }
كما تلاحظ أعلاه فإننا بحاجة إلى استخدام متغير بيئة داخل ملف nginx.conf، لكن المشكلة أن ملفات إعدادات Nginx لا تدعم متغيرات البيئة، لذلك فالحل في استخدام أداة envsubst والتي تقوم باستبدال المتغيرات الموجودة في أي ملف نصي بقيمها في متغيرات البيئة، وقد تم شرح ذلك في التوثيق الرسمي لصورة Nginx في Docker Hub، وسنستخدم هذه الأداة كأمر (command) في الخدمة بحيث يقوم بمعالجة المتغير ثم نقل ملف إعدادات nginx إلى مكانه المناسب وأخيرا يشغل خدمة Nginx:

nginx: image: nginx:1.10.3 ports: - ${NGINX_PORT}:80 depends_on: - php working_dir: /var/www volumes: - ./www:/var/www - ./nginx.conf:/default.conf environment: project_name: ${APP_NAME} command: /bin/bash -c "envsubst '$$project_name' < /default.conf > /etc/nginx/conf.d/default.conf && exec nginx -g 'daemon off;'"
لاحظ أننا في الـ Volume جعلنا الملف يُربط بمسار آخر غير مسار إعدادات Nginx، وذلك لأننا سننقله لاحقا إلى المسار المناسب بعد تبديل قيم المتغيرات باستخدام envsubst، وقمنا كذلك بإضافة متغير البيئة الذي سنستخدمه، ثم نفذنا الأمر الذي يعالج المتغير باستخدام envsubst ثم يَنقُلُ الناتج إلى ملف إعدادات nginx، وبعدها يشغل خدمة Nginx.

والآن قم بتشغيل الخدمة باستخدام docker-compose up وانتظر قيام Composer بانشاء المشروع، ثم شغل localhost:8000 وسيعمل الموقع:


للحصول على الأوامر والملفات السابقة كاملة راجع هذا المستودع:
https://github.com/3mmarg97/Laravel-LEMP-Docker-Compose


الموضوع الأصلي : مثال واقعي على استخدام حاويات Docker لتشغيل موقع ويب || الكاتب : الادارة كريم || المصدر : منتدى مجمع التطوير

 

رد مع اقتباس
إضافة رد

مواقع النشر (المفضلة)

أدوات الموضوع
انواع عرض الموضوع

تعليمات المشاركة
لا تستطيع إضافة مواضيع جديدة
لا تستطيع الرد على المواضيع
لا تستطيع إرفاق ملفات
لا تستطيع تعديل مشاركاتك

BB code is متاحة
كود [IMG] متاحة
كود HTML معطلة

الانتقال السريع

المواضيع المتشابهه
الموضوع كاتب الموضوع المنتدى مشاركات آخر مشاركة
[شرح] استخدام Docker Compose لإنشاء عدة حاويات الادارة كريم برمجة الويب web development 0 05-28-2021 03:36 AM
[شرح] ما هي برمجة الويب ؟ الادارة كريم برمجة الويب web development 0 05-28-2021 02:35 AM
[C#] مشروع سي شارب استخراج النص من الصورة ويدعم اللغه العربية admin برامج Microsoft Visual 0 04-08-2021 03:37 PM
تشغيل اليوتيوب في الخلفية في كل من أجهزة الأندرويد والآيفون admin مـواقع البحـث ومواقع التواصـل الاجـتماعي 0 03-28-2021 10:11 PM
قوانين منتدى مجمع التطوير admin استخدامات المنتدى والقرارات العامة الادارية 0 03-27-2021 02:28 PM

 

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

  • الرئــيســية

  • الــمنــتـدى

  • مركز الرفع

  • التسـجـيل

  • قوانين المنتدى

  • التعـلـيمـات

  • الترقيات

check pagerank

 Flag Counter

كلمة الإدارة  منتدى مجمع التطوير غير مسئول عن أي طرح من الأعضاء فتلك الموضوعات تعبر عن رأى صاحبها ومن خلال وضع قوانين وتعليمات المشاركة بالمنتدى نسعى جاهدين لتطبيق تلك التعليمات. والمنتدى أيضا غير مسئول عن أي اتفاق مالي أو تجارى بين الأعضاء وبذلك تعد هذه الصيغة إخلاء مسئولية من جانب إدارة المنتدى وفقنا ووفقكم الله لما فيه الخير
 
الساعة الآن 09:05 AM


Powered by vBulletin® Copyright ©2000 - 2021, Jelsoft Enterprises Ltd.
جميع الحقوق محفوظة لمجمع التطوير