Docker Compose للمطورين: Next.js مع PostgreSQL و Redis

أمر واحد لتشغيل المشروع بالكامل. يتيح لك Docker Compose تعريف تطبيق Next.js وقاعدة بيانات PostgreSQL وذاكرة Redis المؤقتة كوحدة تصريحية واحدة. في هذا الدليل، ستبني بيئة تطوير جاهزة للإنتاج يمكن لأي عضو في الفريق تشغيلها بأمر docker compose up.
ما ستتعلمه
بنهاية هذا الدليل، ستتمكن من:
- إعداد Docker Compose لتنسيق تطبيق متعدد الخدمات
- تغليف تطبيق Next.js 15 مع إعادة التحميل الفوري أثناء التطوير
- تشغيل PostgreSQL 16 مع وحدات تخزين دائمة وتهيئة تلقائية
- إضافة Redis 7 كطبقة تخزين مؤقت مع فحوصات الصحة
- ضبط متغيرات البيئة بشكل آمن عبر الخدمات
- كتابة Dockerfile متعدد المراحل محسّن لبناءات الإنتاج
- تنفيذ فحوصات الصحة وترتيب التبعيات بين الخدمات
- إنشاء إعدادات تطوير وإنتاج منفصلة
المتطلبات الأساسية
قبل البدء، تأكد من توفر:
- Docker Desktop مثبت وقيد التشغيل (الإصدار 4.25 أو أحدث) — حمّله من هنا
- Node.js 20+ مثبت محليًا لإعداد المشروع الأولي
- معرفة أساسية بالطرفية — التنقل بين المجلدات وتنفيذ الأوامر
- إلمام بـ Next.js — الصفحات ومسارات API و Server Components
- محرر أكواد — يُنصح بـ VS Code مع إضافة Docker
لماذا Docker Compose للتطوير الكامل؟
كل مطور واجه مشكلة "يعمل على جهازي". تطبيق Next.js يتصل بقاعدة بيانات PostgreSQL محلية، ويستخدم Redis للتخزين المؤقت، وكل شيء يعمل بشكل مثالي — حتى يستنسخ زميل المستودع ويقضي ساعات في ضبط بيئته.
Docker Compose يحل هذه المشكلة بتعريف مجموعة التطبيق بالكامل في ملف docker-compose.yml واحد. كل خدمة، كل سلسلة اتصال، كل تعيين منفذ — يُعرَّف مرة واحدة ويُستنسخ في كل مكان.
إليك ما يجعل Docker Compose أساسيًا في 2026:
- بيئات متسقة — التطوير والاختبار والإنتاج تستخدم نفس إعدادات الخدمات
- إعداد بأمر واحد — أعضاء الفريق الجدد ينفذون
docker compose upويبدأون البرمجة فورًا - قواعد بيانات معزولة — لا تعارضات بين قواعد بيانات المشاريع المختلفة على نفس الجهاز
- بيئات قابلة للتجديد — هدم وإعادة بناء المجموعة بالكامل في ثوانٍ
- تكامل CI/CD — نفس ملف Compose يعمل في GitHub Actions و GitLab CI والتطوير المحلي
نظرة عامة على المشروع
ستبني واجهة API لإدارة المهام بالهيكل التالي:
┌─────────────────────────────────────────┐
│ Docker Compose │
│ │
│ ┌───────────┐ ┌──────────┐ ┌──────┐ │
│ │ Next.js │→ │PostgreSQL│ │Redis │ │
│ │ :3000 │→ │ :5432 │ │:6379 │ │
│ └───────────┘ └──────────┘ └──────┘ │
│ ↑ ↑ ↑ │
│ └──────── الشبكة ──────────┘ │
└─────────────────────────────────────────┘
- Next.js 15 — App Router مع مسارات API لواجهة CRUD للمهام
- PostgreSQL 16 — مخزن البيانات الأساسي للمهام والمستخدمين
- Redis 7 — طبقة التخزين المؤقت للبيانات المتكررة الوصول
الخطوة 1: تهيئة مشروع Next.js
ابدأ بإنشاء تطبيق Next.js جديد:
npx create-next-app@latest docker-fullstack --typescript --tailwind --app --src-dir --eslint
cd docker-fullstackثبّت مكتبات قاعدة البيانات والتخزين المؤقت:
npm install pg redis
npm install -D @types/pgهيكل المشروع سيبدو هكذا:
docker-fullstack/
├── src/
│ ├── app/
│ │ ├── api/
│ │ ├── layout.tsx
│ │ └── page.tsx
│ └── lib/
├── package.json
├── tsconfig.json
└── next.config.ts
الخطوة 2: إنشاء Dockerfile
أنشئ ملف Dockerfile في جذر المشروع. يستخدم بناء متعدد المراحل للحفاظ على صغر حجم الصورة النهائية:
# المرحلة 1: التبعيات
FROM node:20-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --only=production && cp -R node_modules /prod_deps
RUN npm ci
# المرحلة 2: البناء
FROM node:20-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
# المرحلة 3: التشغيل (الإنتاج)
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT=3000
CMD ["node", "server.js"]لكي تعمل مرحلة الإنتاج مع الإخراج المستقل، حدّث next.config.ts:
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
output: "standalone",
};
export default nextConfig;الخطوة 3: إنشاء Dockerfile للتطوير
للتطوير، تحتاج إعادة التحميل الفوري وتركيب المصادر. أنشئ Dockerfile.dev:
FROM node:20-alpine
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
EXPOSE 3000
CMD ["npm", "run", "dev"]ملف Dockerfile المبسّط هذا يتخطى البناء متعدد المراحل — في التطوير، السرعة وإعادة التحميل الفوري أهم من حجم الصورة.
الخطوة 4: كتابة ملف Docker Compose
أنشئ docker-compose.yml في جذر المشروع:
services:
# تطبيق Next.js
app:
build:
context: .
dockerfile: Dockerfile.dev
ports:
- "3000:3000"
volumes:
- ./src:/app/src
- ./public:/app/public
- /app/node_modules
- /app/.next
environment:
- DATABASE_URL=postgresql://postgres:postgres@db:5432/taskdb
- REDIS_URL=redis://cache:6379
- NODE_ENV=development
depends_on:
db:
condition: service_healthy
cache:
condition: service_healthy
networks:
- app-network
# قاعدة بيانات PostgreSQL
db:
image: postgres:16-alpine
ports:
- "5432:5432"
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: taskdb
volumes:
- postgres_data:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
networks:
- app-network
# ذاكرة Redis المؤقتة
cache:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 5s
retries: 5
command: redis-server --appendonly yes
networks:
- app-network
volumes:
postgres_data:
redis_data:
networks:
app-network:
driver: bridgeلنشرح المفاهيم الأساسية:
تركيب وحدات التخزين (Volume Mounts)
volumes:
- ./src:/app/src # تركيب المصادر لإعادة التحميل الفوري
- ./public:/app/public # تركيب الملفات العامة
- /app/node_modules # وحدة تخزين مجهولة — تحافظ على node_modules الحاوية
- /app/.next # وحدة تخزين مجهولة — تحافظ على ذاكرة البناء المؤقتةوحدات التخزين المجهولة لـ node_modules و .next تمنع ملفاتك المحلية من تجاوز التبعيات المثبتة في الحاوية. هذا أمر حاسم — بدونها، ستتعطل الوحدات الأصلية بسبب اختلاف البنية المعمارية (macOS مقابل Linux).
فحوصات الصحة (Health Checks)
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5فحوصات الصحة تضمن أن PostgreSQL جاهز فعلًا لقبول الاتصالات قبل أن يحاول Next.js الاتصال. بدون هذا، سيتعطل تطبيقك عند البدء لأن المنفذ قد يكون مفتوحًا لكن الخادم لا يزال يتهيأ.
ترتيب التبعيات
depends_on:
db:
condition: service_healthy
cache:
condition: service_healthyاستخدام condition: service_healthy بدلًا من مجرد depends_on: [db] ينتظر حتى يمر فحص الصحة، وليس فقط حتى يبدأ الحاوية.
الخطوة 5: تهيئة قاعدة البيانات
أنشئ init.sql لإعداد مخطط قاعدة البيانات عند التشغيل الأول:
-- إنشاء جدول المهام
CREATE TABLE IF NOT EXISTS tasks (
id SERIAL PRIMARY KEY,
title VARCHAR(255) NOT NULL,
description TEXT,
status VARCHAR(20) DEFAULT 'pending' CHECK (status IN ('pending', 'in_progress', 'completed')),
priority INTEGER DEFAULT 0 CHECK (priority BETWEEN 0 AND 3),
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- إنشاء فهارس للاستعلامات الشائعة
CREATE INDEX idx_tasks_status ON tasks(status);
CREATE INDEX idx_tasks_priority ON tasks(priority);
-- إدراج بيانات نموذجية
INSERT INTO tasks (title, description, status, priority) VALUES
('إعداد بيئة Docker', 'ضبط Docker Compose للمشروع', 'completed', 3),
('تصميم مخطط قاعدة البيانات', 'إنشاء جداول وفهارس PostgreSQL', 'in_progress', 2),
('تنفيذ طبقة التخزين المؤقت', 'إضافة تخزين Redis المؤقت لاستجابات API', 'pending', 1),
('كتابة نقاط نهاية API', 'بناء عمليات CRUD للمهام', 'pending', 2),
('إضافة المصادقة', 'تنفيذ مصادقة JWT', 'pending', 1);
-- إنشاء مشغّل updated_at
CREATE OR REPLACE FUNCTION update_modified_column()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$ language 'plpgsql';
CREATE TRIGGER update_tasks_modtime
BEFORE UPDATE ON tasks
FOR EACH ROW
EXECUTE FUNCTION update_modified_column();هذا الملف يُنفَّذ تلقائيًا عند بدء تشغيل حاوية PostgreSQL لأول مرة (عبر تركيب docker-entrypoint-initdb.d).
الخطوة 6: وحدة اتصال قاعدة البيانات
أنشئ src/lib/db.ts لإدارة مجموعة اتصالات PostgreSQL:
import { Pool } from "pg";
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
max: 20,
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 2000,
});
pool.on("error", (err) => {
console.error("خطأ غير متوقع في مجموعة قاعدة البيانات:", err);
});
export async function query<T>(text: string, params?: unknown[]): Promise<T[]> {
const result = await pool.query(text, params);
return result.rows as T[];
}
export async function queryOne<T>(
text: string,
params?: unknown[]
): Promise<T | null> {
const rows = await query<T>(text, params);
return rows[0] || null;
}
export default pool;الخطوة 7: وحدة اتصال Redis
أنشئ src/lib/redis.ts لطبقة التخزين المؤقت:
import { createClient } from "redis";
const redis = createClient({
url: process.env.REDIS_URL,
});
redis.on("error", (err) => {
console.error("خطأ في اتصال Redis:", err);
});
redis.on("connect", () => {
console.log("تم الاتصال بـ Redis");
});
// الاتصال عند أول استيراد
if (!redis.isOpen) {
redis.connect();
}
export async function getCache<T>(key: string): Promise<T | null> {
const data = await redis.get(key);
if (!data) return null;
return JSON.parse(data) as T;
}
export async function setCache(
key: string,
value: unknown,
ttlSeconds = 60
): Promise<void> {
await redis.set(key, JSON.stringify(value), { EX: ttlSeconds });
}
export async function invalidateCache(pattern: string): Promise<void> {
const keys = await redis.keys(pattern);
if (keys.length > 0) {
await redis.del(keys);
}
}
export default redis;الخطوة 8: بناء مسارات API
أنشئ واجهة API للمهام مع عمليات CRUD الكاملة. ابدأ بـ src/app/api/tasks/route.ts:
import { NextRequest, NextResponse } from "next/server";
import { query } from "@/lib/db";
import { getCache, setCache, invalidateCache } from "@/lib/redis";
interface Task {
id: number;
title: string;
description: string | null;
status: string;
priority: number;
created_at: string;
updated_at: string;
}
// GET /api/tasks
export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url);
const status = searchParams.get("status");
// التحقق من الذاكرة المؤقتة أولاً
const cacheKey = `tasks:${status || "all"}`;
const cached = await getCache<Task[]>(cacheKey);
if (cached) {
return NextResponse.json({ data: cached, source: "cache" });
}
// الاستعلام من قاعدة البيانات
let tasks: Task[];
if (status) {
tasks = await query<Task>(
"SELECT * FROM tasks WHERE status = $1 ORDER BY priority DESC, created_at DESC",
[status]
);
} else {
tasks = await query<Task>(
"SELECT * FROM tasks ORDER BY priority DESC, created_at DESC"
);
}
// التخزين المؤقت لمدة 30 ثانية
await setCache(cacheKey, tasks, 30);
return NextResponse.json({ data: tasks, source: "database" });
}
// POST /api/tasks
export async function POST(request: NextRequest) {
const body = await request.json();
const { title, description, priority } = body;
if (!title) {
return NextResponse.json(
{ error: "العنوان مطلوب" },
{ status: 400 }
);
}
const task = await query<Task>(
"INSERT INTO tasks (title, description, priority) VALUES ($1, $2, $3) RETURNING *",
[title, description || null, priority || 0]
);
// إبطال ذاكرة المهام المؤقتة
await invalidateCache("tasks:*");
return NextResponse.json({ data: task[0] }, { status: 201 });
}ثم أنشئ المسار الديناميكي في src/app/api/tasks/[id]/route.ts:
import { NextRequest, NextResponse } from "next/server";
import { query, queryOne } from "@/lib/db";
import { invalidateCache } from "@/lib/redis";
interface Task {
id: number;
title: string;
description: string | null;
status: string;
priority: number;
created_at: string;
updated_at: string;
}
// GET /api/tasks/:id
export async function GET(
_request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
const { id } = await params;
const task = await queryOne<Task>("SELECT * FROM tasks WHERE id = $1", [id]);
if (!task) {
return NextResponse.json({ error: "المهمة غير موجودة" }, { status: 404 });
}
return NextResponse.json({ data: task });
}
// PATCH /api/tasks/:id
export async function PATCH(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
const { id } = await params;
const body = await request.json();
const { title, description, status, priority } = body;
const task = await queryOne<Task>(
`UPDATE tasks
SET title = COALESCE($1, title),
description = COALESCE($2, description),
status = COALESCE($3, status),
priority = COALESCE($4, priority)
WHERE id = $5
RETURNING *`,
[title, description, status, priority, id]
);
if (!task) {
return NextResponse.json({ error: "المهمة غير موجودة" }, { status: 404 });
}
await invalidateCache("tasks:*");
return NextResponse.json({ data: task });
}
// DELETE /api/tasks/:id
export async function DELETE(
_request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
const { id } = await params;
const task = await queryOne<Task>(
"DELETE FROM tasks WHERE id = $1 RETURNING *",
[id]
);
if (!task) {
return NextResponse.json({ error: "المهمة غير موجودة" }, { status: 404 });
}
await invalidateCache("tasks:*");
return NextResponse.json({ data: task });
}الخطوة 9: إضافة نقطة نهاية فحص الصحة
أنشئ src/app/api/health/route.ts للتحقق من اتصال جميع الخدمات:
import { NextResponse } from "next/server";
import pool from "@/lib/db";
import redis from "@/lib/redis";
export async function GET() {
const health: Record<string, string> = {
status: "ok",
timestamp: new Date().toISOString(),
};
// فحص PostgreSQL
try {
await pool.query("SELECT 1");
health.database = "connected";
} catch {
health.database = "disconnected";
health.status = "degraded";
}
// فحص Redis
try {
await redis.ping();
health.cache = "connected";
} catch {
health.cache = "disconnected";
health.status = "degraded";
}
const statusCode = health.status === "ok" ? 200 : 503;
return NextResponse.json(health, { status: statusCode });
}الخطوة 10: تشغيل المجموعة
كل شيء جاهز. شغّل التطبيق بالكامل بأمر واحد:
docker compose up --buildسترى مخرجات تُظهر بدء تشغيل الخدمات الثلاث:
[+] Running 3/3
✔ Container docker-fullstack-cache-1 Healthy
✔ Container docker-fullstack-db-1 Healthy
✔ Container docker-fullstack-app-1 Started
اختبر نقاط النهاية:
# فحص الصحة
curl http://localhost:3000/api/health
# عرض جميع المهام
curl http://localhost:3000/api/tasks
# إنشاء مهمة جديدة
curl -X POST http://localhost:3000/api/tasks \
-H "Content-Type: application/json" \
-d '{"title": "تعلم Docker Compose", "description": "اتبع الدليل", "priority": 3}'
# التصفية حسب الحالة
curl http://localhost:3000/api/tasks?status=pending
# تحديث مهمة
curl -X PATCH http://localhost:3000/api/tasks/1 \
-H "Content-Type: application/json" \
-d '{"status": "completed"}'
# حذف مهمة
curl -X DELETE http://localhost:3000/api/tasks/1الخطوة 11: نصائح لسير عمل التطوير
عرض السجلات
# جميع الخدمات
docker compose logs -f
# خدمة واحدة
docker compose logs -f app
# آخر 50 سطرًا
docker compose logs --tail 50 dbالوصول إلى طرفية قاعدة البيانات
docker compose exec db psql -U postgres -d taskdbبمجرد الدخول إلى psql، يمكنك تنفيذ الاستعلامات مباشرة:
SELECT * FROM tasks;
\dt -- عرض الجداول
\d tasks -- وصف مخطط الجدولالوصول إلى طرفية Redis
docker compose exec cache redis-cliأوامر Redis مفيدة لتصحيح الأخطاء:
KEYS * # عرض جميع المفاتيح
GET tasks:all # عرض البيانات المخزنة
TTL tasks:all # التحقق من وقت الانتهاء
FLUSHALL # مسح جميع الذاكرة المؤقتة
إعادة البناء بعد تغيير التبعيات
عند إضافة حزم npm جديدة، أعد بناء حاوية التطبيق:
docker compose up --build appإعادة تعيين قاعدة البيانات
لمسح قاعدة البيانات والبدء من جديد:
docker compose down -v # -v يزيل وحدات التخزين
docker compose up --buildالخطوة 12: إعدادات الإنتاج
أنشئ docker-compose.prod.yml لتجاوزات الإنتاج:
services:
app:
build:
context: .
dockerfile: Dockerfile
volumes: [] # بدون تركيب مصادر في الإنتاج
environment:
- NODE_ENV=production
- DATABASE_URL=postgresql://${DB_USER}:${DB_PASSWORD}@db:5432/${DB_NAME}
- REDIS_URL=redis://cache:6379
restart: always
db:
environment:
POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: ${DB_NAME}
restart: always
cache:
command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD}
restart: alwaysأنشئ ملف .env.production (لا تُرسله أبدًا إلى المستودع):
DB_USER=taskapp
DB_PASSWORD=كلمة-مرور-قوية-جداً-هنا
DB_NAME=taskdb_prod
REDIS_PASSWORD=كلمة-مرور-قوية-أخرىنشر التطبيق:
docker compose -f docker-compose.yml -f docker-compose.prod.yml --env-file .env.production up -d --buildعلامات -f تدمج ملفَي Compose، حيث يتجاوز ملف الإنتاج قيم التطوير.
الخطوة 13: إضافة ملف .dockerignore
أنشئ .dockerignore للحفاظ على نظافة الصور:
node_modules
.next
.git
.gitignore
*.md
docker-compose*.yml
.env*
.vscode
coverage
هذا يمنع نسخ المجلدات الكبيرة والملفات الحساسة إلى صورة Docker، مما يقلل وقت البناء وحجم الصورة.
الخطوة 14: المراقبة مع Docker Compose
أضف مراقبة أساسية باستخدام أمر الإحصائيات:
# استخدام الموارد في الوقت الفعلي
docker compose stats
# المخرجات:
# NAME CPU % MEM USAGE / LIMIT NET I/O
# app-1 0.50% 245MiB / 8GiB 1.2kB / 890B
# db-1 0.10% 45MiB / 8GiB 500B / 200B
# cache-1 0.05% 12MiB / 8GiB 300B / 100Bلبيئات الإنتاج، يمكنك إضافة حدود الموارد:
services:
app:
deploy:
resources:
limits:
cpus: "1.0"
memory: 512M
reservations:
cpus: "0.5"
memory: 256Mاستكشاف الأخطاء وإصلاحها
المنفذ مستخدم بالفعل
Error: bind: address already in use
عملية أخرى تستخدم المنفذ. ابحث عنها وأوقفها:
# البحث عن العملية التي تستخدم المنفذ 5432
lsof -i :5432
# أو غيّر تعيين المنفذ في docker-compose.yml
ports:
- "5433:5432" # تعيين إلى 5433 محليًارفض اتصال قاعدة البيانات
إذا بدأ التطبيق قبل جاهزية PostgreSQL، سترى:
Error: connect ECONNREFUSED 172.18.0.2:5432
هذا لا يجب أن يحدث مع فحوصات الصحة المُعدّة، لكن إن حدث، تأكد أن depends_on يستخدم condition: service_healthy.
مشاكل أذونات وحدات التخزين على Linux
# إصلاح أذونات بيانات PostgreSQL
sudo chown -R 999:999 ./postgres_data
# أو استخدم وحدات تخزين مسمّاة (مُنصح بها، مستخدمة بالفعل في هذا الدليل)إعادة التحميل الفوري لا تعمل
تأكد أن تركيب وحدات التخزين يشمل مجلد المصادر:
volumes:
- ./src:/app/srcوتحقق أن خادم تطوير Next.js يراقب التغييرات. إذا كنت تستخدم Docker Desktop على macOS، مراقبة الملفات تعمل تلقائيًا عبر gRPC FUSE.
الخطوات التالية
الآن وبيئة Docker Compose تعمل، فكّر في هذه التحسينات:
- أضف Prisma أو Drizzle ORM — استبدل SQL الخام بـ ORM آمن الأنواع لإدارة الهجرات والمخططات
- نفّذ المصادقة — أضف جدول مستخدمين ومصادقة JWT لحماية مسارات API
- أعدّ Nginx — أضف خدمة وكيل عكسي لإنهاء SSL وموازنة الحمل
- أضف pgAdmin — أضف واجهة إدارة قاعدة البيانات كخدمة Compose أخرى
- اضبط CI/CD — استخدم نفس ملف Compose في GitHub Actions لاختبارات التكامل
- أضف Adminer — أداة خفيفة لإدارة قواعد البيانات كخدمة Compose
الخلاصة
Docker Compose يحوّل التطوير الكامل بالقضاء على تناقضات البيئات. في هذا الدليل، بنيت مجموعة تطبيق كاملة تشمل:
- Next.js 15 يخدم الواجهة البرمجية مع إعادة التحميل الفوري أثناء التطوير
- PostgreSQL 16 مع تخزين دائم وتهيئة تلقائية وفحوصات الصحة
- Redis 7 لتخزين الاستجابات مؤقتًا مع إبطال قائم على TTL
- Dockerfile متعدد المراحل محسّن لكل من التطوير والإنتاج
- إعدادات Compose منفصلة لبيئتَي التطوير والإنتاج
المجموعة بالكامل تبدأ بأمر docker compose up واحد. كل عضو في الفريق يحصل على بيئة متطابقة، ونفس الإعدادات تمتد لخطوط أنابيب CI/CD ونشر الإنتاج.
الأنماط الأساسية التي تعلمتها — فحوصات الصحة، ترتيب التبعيات، تركيب وحدات التخزين، والبناء متعدد المراحل — تنطبق على أي مشروع Docker Compose، بغض النظر عن مجموعة التقنيات.
ناقش مشروعك معنا
نحن هنا للمساعدة في احتياجات تطوير الويب الخاصة بك. حدد موعدًا لمناقشة مشروعك وكيف يمكننا مساعدتك.
دعنا نجد أفضل الحلول لاحتياجاتك.
مقالات ذات صلة

بناء تطبيق متكامل باستخدام Drizzle ORM و Next.js 15: قاعدة بيانات آمنة الأنواع من الصفر إلى الإنتاج
تعلّم كيفية بناء تطبيق متكامل آمن الأنواع باستخدام Drizzle ORM مع Next.js 15. يغطي هذا الدليل العملي تصميم المخططات والترحيلات وServer Actions وعمليات CRUD والنشر مع PostgreSQL.

بناء واجهات REST API جاهزة للإنتاج باستخدام FastAPI و PostgreSQL و Docker
تعلّم كيف تبني وتختبر وتنشر واجهة REST API احترافية باستخدام إطار FastAPI في بايثون مع PostgreSQL و SQLAlchemy و Alembic و Docker Compose — من الصفر حتى النشر.

نشر تطبيق Next.js باستخدام Docker و CI/CD في بيئة الإنتاج
تعلم كيف تُحاوِل تطبيق Next.js باستخدام Docker، وتُعدّ خط أنابيب CI/CD مع GitHub Actions، وتنشر تلقائياً في بيئة الإنتاج على خادم VPS. دليل شامل من التطوير إلى النشر الآلي.