Laravel المتقدم

استراتيجيات النشر المتقدمة

18 دقيقة الدرس 30 من 40

استراتيجيات النشر المتقدمة

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

أهداف الإنتاج: عدم التوقف أثناء عمليات النشر، والتراجع التلقائي عند حدوث فشل، وسهولة التوسع، والبيئات المتسقة، ودورات النشر السريعة. تضمن هذه الاستراتيجيات بقاء تطبيقك متاحًا وفعّالًا.

أساسيات النشر بدون توقف

تتسبب عمليات النشر التقليدية في فترات توقف عند استبدال الملفات. يستخدم النشر بدون توقف روابط رمزية وتبديل الدليل الذري:

<?php // هيكل الدليل للنشر بدون توقف /var/www/myapp/ ├── current → releases/20240214120000 (رابط رمزي) ├── releases/ │ ├── 20240214120000/ (الأحدث) │ ├── 20240214110000/ │ └── 20240214100000/ ├── shared/ │ ├── storage/ │ │ ├── app/ │ │ ├── logs/ │ │ └── framework/ │ └── .env └── repo/ (مستودع git) // خطوات النشر: // 1. استنساخ/سحب الكود إلى دليل إصدار جديد // 2. تثبيت التبعيات (composer، npm) // 3. إنشاء روابط رمزية للموارد المشتركة // 4. تشغيل migrations والتحسينات // 5. تبديل الرابط الرمزي 'current' ذريًا إلى الإصدار الجديد // 6. إعادة تحميل PHP-FPM/إعادة تشغيل عمال قائمة الانتظار // 7. الاحتفاظ بآخر 3-5 إصدارات للتراجع السريع // مثال على سكريبت النشر bash #!/bin/bash DEPLOY_PATH="/var/www/myapp" RELEASE_NAME=$(date +%Y%m%d%H%M%S) RELEASE_PATH="$DEPLOY_PATH/releases/$RELEASE_NAME" # استنساخ المستودع git clone --depth 1 https://github.com/user/repo.git $RELEASE_PATH # تثبيت التبعيات cd $RELEASE_PATH composer install --no-dev --optimize-autoloader npm ci && npm run build # إنشاء روابط رمزية للموارد المشتركة ln -nfs $DEPLOY_PATH/shared/.env $RELEASE_PATH/.env ln -nfs $DEPLOY_PATH/shared/storage $RELEASE_PATH/storage // تشغيل migrations والتحسينات php artisan migrate --force php artisan config:cache php artisan route:cache php artisan view:cache // التبديل الذري إلى الإصدار الجديد ln -nfs $RELEASE_PATH $DEPLOY_PATH/current // إعادة تحميل الخدمات sudo systemctl reload php8.2-fpm php artisan queue:restart // تنظيف الإصدارات القديمة (الاحتفاظ بآخر 5) cd $DEPLOY_PATH/releases ls -t | tail -n +6 | xargs rm -rf echo "اكتمل النشر: $RELEASE_NAME"
تبديل الرابط الرمزي الذري: الأمر `ln -nfs` يستبدل الرابط الرمزي ذريًا، مما يضمن عدم رؤية المستخدمين أبدًا لتطبيق منشور جزئيًا. تتبع خوادم الويب الرابط الرمزي لخدمة الإصدار الجديد على الفور.

Laravel Envoy - أتمتة النشر الأنيقة

<?php // composer require laravel/envoy --dev // Envoy.blade.php @servers(['web' => 'deploy@example.com']) @setup $repository = 'git@github.com:user/repo.git'; $app_dir = '/var/www/myapp'; $release = date('YmdHis'); $new_release_dir = $app_dir.'/releases/'.$release; @endsetup @story('deploy') clone_repository run_composer run_npm update_symlinks migrate_database optimize_application switch_release reload_services cleanup_old_releases @endstory @task('clone_repository') echo "استنساخ المستودع..." [ -d {{ $app_dir }}/repo ] || git clone {{ $repository }} {{ $app_dir }}/repo git --git-dir={{ $app_dir }}/repo/.git --work-tree={{ $app_dir }}/repo pull origin main mkdir -p {{ $new_release_dir }} cp -r {{ $app_dir }}/repo/* {{ $new_release_dir }} echo "تم استنساخ المستودع" @endtask @task('run_composer') echo "تشغيل composer..." cd {{ $new_release_dir }} composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader @endtask @task('run_npm') echo "بناء الأصول..." cd {{ $new_release_dir }} npm ci npm run build @endtask @task('update_symlinks') echo "ربط storage و .env..." ln -nfs {{ $app_dir }}/shared/.env {{ $new_release_dir }}/.env ln -nfs {{ $app_dir }}/shared/storage {{ $new_release_dir }}/storage # التأكد من وجود bootstrap/cache mkdir -p {{ $new_release_dir }}/bootstrap/cache chmod -R 775 {{ $new_release_dir }}/bootstrap/cache @endtask @task('migrate_database') echo "تشغيل migrations..." cd {{ $new_release_dir }} php artisan migrate --force @endtask @task('optimize_application') echo "تحسين التطبيق..." cd {{ $new_release_dir }} php artisan config:cache php artisan route:cache php artisan view:cache php artisan event:cache @endtask @task('switch_release') echo "التبديل إلى الإصدار الجديد..." ln -nfs {{ $new_release_dir }} {{ $app_dir }}/current @endtask @task('reload_services') echo "إعادة تحميل الخدمات..." sudo systemctl reload php8.2-fpm cd {{ $app_dir }}/current php artisan queue:restart @endtask @task('cleanup_old_releases') echo "تنظيف الإصدارات القديمة..." cd {{ $app_dir }}/releases ls -t | tail -n +6 | xargs rm -rf @endtask @finished echo "اكتمل النشر بنجاح!" @endfinished @error echo "فشل النشر في المهمة: {{ $task }}" @enderror // النشر مع: php vendor/bin/envoy run deploy // التراجع: php vendor/bin/envoy run rollback @story('rollback') rollback_release reload_services @endstory @task('rollback_release') cd {{ $app_dir }}/releases PREVIOUS=$(ls -t | sed -n 2p) echo "التراجع إلى: $PREVIOUS" ln -nfs {{ $app_dir }}/releases/$PREVIOUS {{ $app_dir }}/current @endtask

Laravel Forge - النشر المُدار

<?php // يوفر Forge النشر بنقرة واحدة مع: // - توفير الخادم (DigitalOcean، AWS، Linode) // - إدارة شهادات SSL (Let's Encrypt) // - النسخ الاحتياطي لقاعدة البيانات // - إدارة عمال قائمة الانتظار // - إدارة المهام المجدولة // - عمليات نشر بدون توقف // - التراجع بنقرة واحدة // مثال على سكريبت النشر (يتم إنشاؤه تلقائيًا بواسطة Forge) cd /home/forge/example.com git pull origin main composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader php artisan migrate --force npm ci npm run build php artisan config:cache php artisan route:cache php artisan view:cache php artisan queue:restart if [ -f artisan ]; then php artisan horizon:terminate fi // النشر السريع عبر Forge API use Laravel\Forge\Forge; $forge = new Forge(env('FORGE_API_TOKEN')); // نشر الموقع $forge->deploySite($serverId, $siteId); // فحص حالة النشر $deployment = $forge->siteDeploymentHistory($serverId, $siteId); // تمكين النشر السريع (النشر التلقائي عند دفع git) $forge->enableQuickDeploy($serverId, $siteId);
اختبار النشر: اختبر دائمًا عمليات النشر على بيئات التدريج أولاً. استخدم علامات الميزات لطرح التغييرات تدريجيًا. راقب معدلات الأخطاء ومقاييس الأداء فورًا بعد النشر.

Laravel Vapor - النشر بدون خادم

<?php // composer require laravel/vapor-cli --global // vapor login // تكوين vapor.yml id: 12345 name: my-app environments: production: memory: 1024 cli-memory: 512 runtime: 'php-8.2:al2' build: - 'composer install --no-dev --classmap-authoritative' - 'php artisan event:cache' deploy: - 'php artisan migrate --force' staging: memory: 512 cli-memory: 512 runtime: 'php-8.2:al2' database: my-staging-db cache: my-staging-cache // النشر إلى الإنتاج // vapor deploy production // ميزات Vapor الرئيسية: // - التوسع التلقائي (من 0 إلى ما لا نهاية) // - الدفع فقط مقابل الاستخدام الفعلي // - تكامل AWS Lambda // - CloudFront CDN // - إدارة قاعدة بيانات RDS // - تكامل قائمة انتظار SQS // - تخزين أصول S3 // - إدارة متغيرات البيئة // - النطاقات المخصصة و SSL // تعديلات كود خاصة بـ Vapor // تكوين قرص التخزين 'disks' => [ 's3' => [ 'driver' => 's3', 'key' => env('AWS_ACCESS_KEY_ID'), 'secret' => env('AWS_SECRET_ACCESS_KEY'), 'region' => env('AWS_DEFAULT_REGION'), 'bucket' => env('AWS_BUCKET'), 'visibility' => 'public', ], ], // تكوين قائمة الانتظار لـ Vapor 'connections' => [ 'sqs' => [ 'driver' => 'sqs', 'key' => env('AWS_ACCESS_KEY_ID'), 'secret' => env('AWS_SECRET_ACCESS_KEY'), 'prefix' => env('SQS_PREFIX'), 'queue' => env('SQS_QUEUE'), 'region' => env('AWS_DEFAULT_REGION'), ], ],

نشر Docker و Kubernetes

<?php // Dockerfile لـ Laravel FROM php:8.2-fpm // تثبيت تبعيات النظام RUN apt-get update && apt-get install -y \ git \ curl \ libpng-dev \ libonig-dev \ libxml2-dev \ zip \ unzip // تثبيت امتدادات PHP RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd // تثبيت Composer COPY --from=composer:latest /usr/bin/composer /usr/bin/composer // تعيين دليل العمل WORKDIR /var/www // نسخ التطبيق COPY . /var/www // تثبيت التبعيات RUN composer install --no-dev --optimize-autoloader // بناء الأصول RUN npm ci && npm run build // تعيين الأذونات RUN chown -R www-data:www-data /var/www/storage /var/www/bootstrap/cache EXPOSE 9000 CMD ["php-fpm"] // docker-compose.yml version: '3.8' services: app: build: context: . dockerfile: Dockerfile image: myapp container_name: myapp-app restart: unless-stopped working_dir: /var/www volumes: - ./:/var/www networks: - myapp-network nginx: image: nginx:alpine container_name: myapp-nginx restart: unless-stopped ports: - "80:80" volumes: - ./:/var/www - ./docker/nginx:/etc/nginx/conf.d networks: - myapp-network mysql: image: mysql:8.0 container_name: myapp-mysql restart: unless-stopped environment: MYSQL_DATABASE: ${DB_DATABASE} MYSQL_ROOT_PASSWORD: ${DB_PASSWORD} volumes: - mysql-data:/var/lib/mysql networks: - myapp-network redis: image: redis:alpine container_name: myapp-redis restart: unless-stopped networks: - myapp-network networks: myapp-network: driver: bridge volumes: mysql-data: // Kubernetes deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: laravel-app spec: replicas: 3 selector: matchLabels: app: laravel template: metadata: labels: app: laravel spec: containers: - name: laravel image: myapp:latest ports: - containerPort: 9000 env: - name: APP_ENV value: "production" - name: DB_HOST value: "mysql-service" volumeMounts: - name: storage mountPath: /var/www/storage volumes: - name: storage persistentVolumeClaim: claimName: laravel-storage

استراتيجية النشر الأزرق-الأخضر

<?php // يحافظ النشر الأزرق-الأخضر على بيئتين متطابقتين // واحدة فقط (زرقاء أو خضراء) تخدم حركة الإنتاج في كل مرة // تكوين Nginx للتبديل الأزرق-الأخضر upstream blue { server blue.myapp.internal:80; } upstream green { server green.myapp.internal:80; } # البيئة النشطة الحالية upstream backend { server blue.myapp.internal:80; // التبديل إلى الأخضر عند النشر } server { listen 80; server_name example.com; location / { proxy_pass http://backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } } // عملية النشر: // 1. نشر الإصدار الجديد إلى الأخضر (غير نشط حاليًا) // 2. تشغيل الاختبارات على البيئة الخضراء // 3. تسخين البيئة الخضراء (التخزين المؤقت، اتصالات قاعدة البيانات) // 4. تبديل nginx upstream من الأزرق إلى الأخضر // 5. مراقبة الأخضر للمشاكل // 6. إذا حدثت مشاكل، التبديل مرة أخرى إلى الأزرق (تراجع فوري) // 7. إذا كان مستقرًا، أبقِ الأخضر نشطًا وحدّث الأزرق للنشر التالي // سكريبت تبديل أزرق-أخضر تلقائي #!/bin/bash CURRENT=$(cat /etc/nginx/current_env) NEW_ENV="green" if [ "$CURRENT" == "green" ]; then NEW_ENV="blue" fi // تحديث تكوين nginx sed -i "s/server $CURRENT.myapp.internal/server $NEW_ENV.myapp.internal/" /etc/nginx/sites-enabled/default // إعادة تحميل nginx nginx -t && systemctl reload nginx // تحديث علامة البيئة الحالية echo $NEW_ENV > /etc/nginx/current_env echo "تم التبديل من $CURRENT إلى $NEW_ENV"
تمرين 1: أنشئ سكريبت نشر كامل بدون توقف باستخدام Envoy. قم بتضمين مهام لاستنساخ الكود، وتثبيت التبعيات، وتشغيل migrations، والتحسين، والتبديل الذري للرابط الرمزي. اختبر وظيفة التراجع.
تمرين 2: قم بإعداد بيئة تطوير وإنتاج Laravel قائمة على Docker. أنشئ Dockerfile و docker-compose.yml وسكريبتات النشر. قم بتضمين خدمات للتطبيق و nginx و MySQL و Redis وعمال قائمة الانتظار.
تمرين 3: نفذ استراتيجية نشر أزرق-أخضر لتطبيق Laravel. قم بإعداد بيئتين متطابقتين، وأنشئ سكريبتات تبديل تلقائية، واختبر سيناريوهات التراجع. وثق عملية النشر وإجراءات التجاوز عند الفشل.