Laravel المتقدم
استراتيجيات النشر المتقدمة
استراتيجيات النشر المتقدمة
يتطلب نشر 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. قم بإعداد بيئتين متطابقتين، وأنشئ سكريبتات تبديل تلقائية، واختبر سيناريوهات التراجع. وثق عملية النشر وإجراءات التجاوز عند الفشل.