نشر واجهات برمجة التطبيقات وإدارة العمليات
نشر واجهات برمجة التطبيقات وإدارة العمليات
يتطلب نشر واجهات برمجة التطبيقات إلى بيئة الإنتاج تخطيطًا دقيقًا وأتمتة ومراقبة. في هذا الدرس، سنستكشف ممارسات DevOps الحديثة لنشر واجهات برمجة التطبيقات، بما في ذلك الحاويات، وخطوط CI/CD، وتكوين البيئات، والنشر بدون توقف.
فهم تحديات نشر واجهات برمجة التطبيقات
تواجه عمليات نشر واجهات برمجة التطبيقات تحديات فريدة مقارنة بتطبيقات الويب التقليدية:
- عدم التوقف: يجب أن تظل واجهات برمجة التطبيقات متاحة أثناء عمليات النشر حيث يعتمد عليها العملاء على مدار الساعة
- التوافق العكسي: التغييرات الكبيرة يمكن أن تعطل التكاملات الحالية
- تماثل البيئات: يجب أن تكون بيئات التطوير والتجريب والإنتاج متسقة
- ترحيلات قواعد البيانات: يجب تنسيق تغييرات المخطط مع عمليات نشر الكود
- إدارة التكوين: يجب أن تكون الأسرار والإعدادات الخاصة بالبيئة آمنة
الحاويات باستخدام Docker
يتيح Docker عمليات نشر متسقة عبر جميع البيئات. إليك Dockerfile جاهز للإنتاج لواجهة برمجة تطبيقات Laravel:
<!-- بناء متعدد المراحل للصور المحسنة -->
FROM php:8.2-fpm-alpine AS base
# تثبيت تبعيات النظام
RUN apk add --no-cache \
nginx \
postgresql-dev \
redis \
git \
zip \
unzip \
curl
# تثبيت امتدادات PHP
RUN docker-php-ext-install pdo pdo_pgsql opcache
# تثبيت Composer
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
WORKDIR /var/www/html
# نسخ ملفات composer
COPY composer.json composer.lock ./
# تثبيت التبعيات (تخطي dev في الإنتاج)
RUN composer install --no-dev --optimize-autoloader --no-scripts
# نسخ كود التطبيق
COPY . .
# تعيين الأذونات
RUN chown -R www-data:www-data /var/www/html \
&& chmod -R 755 /var/www/html/storage
# إنشاء autoloader محسّن
RUN composer dump-autoload --optimize
# ذاكرة التخزين المؤقت للتكوين
RUN php artisan config:cache \
&& php artisan route:cache \
&& php artisan view:cache
EXPOSE 9000
CMD ["php-fpm"]
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile
volumes:
- .:/var/www/html
environment:
- APP_ENV=local
- APP_DEBUG=true
depends_on:
- database
- redis
database:
image: postgres:15-alpine
environment:
POSTGRES_DB: api_db
POSTGRES_USER: api_user
POSTGRES_PASSWORD: secret
volumes:
- db_data:/var/lib/postgresql/data
ports:
- "5432:5432"
redis:
image: redis:7-alpine
ports:
- "6379:6379"
nginx:
image: nginx:alpine
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- .:/var/www/html
ports:
- "8000:80"
depends_on:
- app
volumes:
db_data:
إدارة تكوين البيئة
لا تقم أبدًا بإرسال الأسرار إلى نظام التحكم في الإصدارات. استخدم متغيرات البيئة وأنظمة إدارة الأسرار:
APP_NAME="My API"
APP_ENV=production
APP_KEY=
APP_DEBUG=false
APP_URL=https://api.example.com
LOG_CHANNEL=stack
LOG_LEVEL=error
DB_CONNECTION=pgsql
DB_HOST=database.example.com
DB_PORT=5432
DB_DATABASE=api_production
DB_USERNAME=
DB_PASSWORD=
REDIS_HOST=redis.example.com
REDIS_PASSWORD=
REDIS_PORT=6379
CACHE_DRIVER=redis
QUEUE_CONNECTION=redis
SESSION_DRIVER=redis
JWT_SECRET=
JWT_TTL=60
MAIL_MAILER=smtp
MAIL_HOST=smtp.example.com
MAIL_PORT=587
MAIL_USERNAME=
MAIL_PASSWORD=
MAIL_ENCRYPTION=tls
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
SENTRY_LARAVEL_DSN=
خط CI/CD باستخدام GitHub Actions
أتمتة الاختبار والبناء والنشر باستخدام خطوط CI/CD:
name: Deploy API
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:15
env:
POSTGRES_PASSWORD: postgres
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
steps:
- uses: actions/checkout@v3
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: 8.2
extensions: pdo, pdo_pgsql, redis
coverage: xdebug
- name: Install Dependencies
run: composer install --prefer-dist --no-progress
- name: Copy .env
run: cp .env.ci .env
- name: Generate Application Key
run: php artisan key:generate
- name: Run Migrations
run: php artisan migrate --force
- name: Run Tests
run: php artisan test --coverage --min=80
- name: Run Static Analysis
run: ./vendor/bin/phpstan analyse
build:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to Container Registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and Push
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: |
ghcr.io/${{ github.repository }}:latest
ghcr.io/${{ github.repository }}:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
deploy:
needs: build
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- name: Deploy to Production
uses: appleboy/ssh-action@v0.1.10
with:
host: ${{ secrets.PROD_HOST }}
username: ${{ secrets.PROD_USER }}
key: ${{ secrets.PROD_SSH_KEY }}
script: |
cd /var/www/api
docker-compose pull
docker-compose up -d --no-deps --build app
docker-compose exec -T app php artisan migrate --force
docker-compose exec -T app php artisan config:cache
docker-compose exec -T app php artisan route:cache
docker-compose exec -T app php artisan queue:restart
استراتيجيات النشر بدون توقف
قم بتطبيق استراتيجيات نشر تحافظ على توفر واجهة برمجة التطبيقات الخاصة بك أثناء التحديثات:
1. النشر الأزرق-الأخضر
#!/bin/bash
# نشر أزرق-أخضر لواجهة برمجة التطبيقات
CURRENT=$(docker ps --filter "name=api-blue" -q)
if [ -z "$CURRENT" ]; then
NEW_COLOR="blue"
OLD_COLOR="green"
else
NEW_COLOR="green"
OLD_COLOR="blue"
fi
echo "جاري النشر إلى بيئة $NEW_COLOR..."
# بدء البيئة الجديدة
docker-compose -f docker-compose.$NEW_COLOR.yml up -d
# انتظار فحص الصحة
echo "انتظار فحص الصحة..."
for i in {1..30}; do
if curl -f http://localhost:8001/health > /dev/null 2>&1; then
echo "نجح فحص الصحة!"
break
fi
sleep 2
done
# تبديل حركة المرور
echo "تبديل حركة المرور إلى $NEW_COLOR..."
# تحديث موازن التحميل أو تكوين الوكيل العكسي
nginx -s reload
# إيقاف البيئة القديمة
echo "إيقاف بيئة $OLD_COLOR..."
docker-compose -f docker-compose.$OLD_COLOR.yml down
echo "اكتمل النشر!"
2. النشر المتدحرج
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-deployment
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # إضافة pod إضافي واحد أثناء التحديث
maxUnavailable: 1 # السماح بعدم توفر pod واحد
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: api
image: ghcr.io/myorg/api:latest
ports:
- containerPort: 9000
livenessProbe:
httpGet:
path: /health
port: 9000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 9000
initialDelaySeconds: 5
periodSeconds: 5
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
استراتيجيات ترحيل قواعد البيانات
تعامل مع تغييرات قاعدة البيانات بأمان أثناء عمليات النشر:
- اجعل دائمًا الترحيلات متوافقة مع الإصدارات السابقة
- استخدم ترحيلات متعددة المراحل للتغييرات الكبيرة
- اختبر الترحيلات على بيانات شبيهة بالإنتاج
- حافظ على سرعة الترحيلات (استخدم وظائف الخلفية لتغييرات البيانات الكبيرة)
- احتفظ دائمًا بخطة للرجوع
<?php
// المرحلة 1: إضافة عمود جديد (قابل للإلغاء، متوافق مع الإصدارات السابقة)
Schema::table('users', function (Blueprint $table) {
$table->string('new_email')->nullable();
});
// نشر الكود الذي يكتب إلى كل من الأعمدة القديمة والجديدة
// المرحلة 2: ملء البيانات (تشغيل كوظيفة خلفية)
User::chunk(1000, function ($users) {
foreach ($users as $user) {
$user->new_email = $user->email;
$user->save();
}
});
// المرحلة 3: جعل العمود الجديد غير قابل للإلغاء
Schema::table('users', function (Blueprint $table) {
$table->string('new_email')->nullable(false)->change();
});
// نشر الكود الذي يستخدم العمود الجديد فقط
// المرحلة 4: حذف العمود القديم
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('email');
$table->renameColumn('new_email', 'email');
});
المراقبة وفحوصات الصحة
قم بتطبيق فحوصات صحة شاملة للمراقبة التلقائية:
<?php
// فحص الحياة - هل التطبيق يعمل؟
Route::get('/health', function () {
return response()->json([
'status' => 'healthy',
'timestamp' => now()->toIso8601String(),
]);
});
// فحص الجاهزية - هل يمكن للتطبيق خدمة حركة المرور؟
Route::get('/ready', function () {
$checks = [
'database' => false,
'redis' => false,
'storage' => false,
];
try {
DB::connection()->getPdo();
$checks['database'] = true;
} catch (\Exception $e) {
Log::error('فشل فحص قاعدة البيانات: ' . $e->getMessage());
}
try {
Redis::ping();
$checks['redis'] = true;
} catch (\Exception $e) {
Log::error('فشل فحص Redis: ' . $e->getMessage());
}
try {
Storage::disk('local')->exists('test');
$checks['storage'] = true;
} catch (\Exception $e) {
Log::error('فشل فحص التخزين: ' . $e->getMessage());
}
$allHealthy = array_reduce($checks, fn($carry, $check) => $carry && $check, true);
return response()->json([
'status' => $allHealthy ? 'ready' : 'not_ready',
'checks' => $checks,
'timestamp' => now()->toIso8601String(),
], $allHealthy ? 200 : 503);
});
// نقطة نهاية الحالة التفصيلية (مصادق عليها)
Route::middleware(['auth:api', 'admin'])->get('/status', function () {
return response()->json([
'app' => [
'name' => config('app.name'),
'env' => config('app.env'),
'version' => config('app.version'),
],
'system' => [
'php_version' => PHP_VERSION,
'laravel_version' => app()->version(),
'memory_usage' => memory_get_usage(true) / 1024 / 1024 . ' MB',
'peak_memory' => memory_get_peak_usage(true) / 1024 / 1024 . ' MB',
],
'cache' => [
'driver' => config('cache.default'),
'redis_connection' => Redis::connection()->ping(),
],
'database' => [
'connection' => DB::connection()->getName(),
'version' => DB::select('SELECT version()')[0]->version,
],
'queue' => [
'connection' => config('queue.default'),
'failed_jobs' => DB::table('failed_jobs')->count(),
],
]);
});
قائمة التحقق من النشر
- ✓ اجتياز جميع الاختبارات (الوحدة، التكامل، من البداية للنهاية)
- ✓ الموافقة على مراجعة الكود
- ✓ اكتمال فحص الأمان (لا توجد ثغرات حرجة)
- ✓ اختبار ترحيلات قاعدة البيانات على التجريب
- ✓ التحقق من التوافق العكسي
- ✓ تحديث الوثائق
- ✓ تكوين تنبيهات المراقبة
- ✓ توثيق خطة الرجوع
- ✓ إخطار أصحاب المصلحة
- ✓ اجتياز فحوصات الصحة
- ✓ تنفيذ اختبارات الدخان
- ✓ معدلات الأخطاء ضمن النطاق الطبيعي
- ✓ أوقات الاستجابة مقبولة
- ✓ تحسين استعلامات قاعدة البيانات
- ✓ تسخين ذاكرة التخزين المؤقت
- ✓ تشغيل وظائف الخلفية
- ✓ مراقبة السجلات للأخطاء
- ✓ التحقق من تكاملات العملاء
- ✓ توثيق النشر
إجراءات الرجوع
#!/bin/bash
echo "بدء الرجوع..."
# الحصول على الإصدار السابق
PREVIOUS_VERSION=$(git describe --abbrev=0 --tags $(git rev-list --tags --skip=1 --max-count=1))
echo "الرجوع إلى الإصدار: $PREVIOUS_VERSION"
# الخروج إلى الإصدار السابق
git checkout $PREVIOUS_VERSION
# إعادة البناء والنشر
docker-compose build
docker-compose up -d
# الرجوع عن الترحيلات (إذا لزم الأمر)
# php artisan migrate:rollback --step=1
# مسح ذاكرات التخزين المؤقت
docker-compose exec app php artisan cache:clear
docker-compose exec app php artisan config:cache
docker-compose exec app php artisan route:cache
echo "اكتمل الرجوع!"
echo "يرجى التحقق من أن التطبيق يعمل بشكل صحيح."
الملخص
يتطلب نشر واجهات برمجة التطبيقات وDevOps:
- الحاويات للاتساق عبر البيئات
- خطوط CI/CD تلقائية للاختبار والنشر
- إدارة تكوين آمنة بدون أسرار مشفرة
- استراتيجيات النشر بدون توقف (أزرق-أخضر، تحديثات متدحرجة)
- أنماط ترحيل قاعدة بيانات آمنة مع خطط الرجوع
- فحوصات صحة ومراقبة شاملة
- إجراءات نشر ورجوع موثقة
في الدرس التالي، سنستكشف أنماط تصميم واجهات برمجة التطبيقات بما في ذلك نمط المستودع، وDTOs، وفئات الإجراءات لكود واجهة برمجة تطبيقات أكثر نظافة وقابلية للصيانة.