يمكنك استضافة عدة تثبيتات مستقلة لـ Discourse على خادم واحد (حاويات منفصلة / منافذ منفصلة / ملفات app.yml منفصلة) دون استخدام ميزة “multisite” في Discourse.
هذه الطريقة تتطلب جهدًا يدويًا أكثر من multisite، لكنها تحافظ على عزلة كل مثيل وتسهل نقل موقع فردي إلى خادمه الخاص لاحقًا.
إحدى الأنماط العملية هي:
• قاعدة بيانات Postgres خارجية (مثيل واحد)
• Redis خارجي (مثيل واحد)
• حاويات ويب متعددة لـ Discourse
• عقدة Sidekiq واحدة
• وكيل عكسي مع فحوصات الصحة
هذا يتجنب multisite تمامًا مع السماح بتوفير التكاليف في الإعدادات ذات الحركة المنخفضة.
دليل تشغيل متعدد الحاويات لـ Discourse
Postgres خارجي + Redis + HAProxy + app1 / app2
- حزم المضيف
| الخطوة | الأمر |
|---|---|
| تحديث النظام | apt-get update |
| تثبيت الأدوات الأساسية | apt-get install -y ca-certificates curl gnupg lsb-release |
| تثبيت HAProxy + certbot + socat | apt-get install -y haproxy certbot socat |
- شبكة Docker (مطلوبة)
مطلوب شبكة Docker محددة بواسطة المستخدم حتى تتمكن الحاويات من حل أسماء بعضها البعض.
| الخطوة | الأمر |
|---|---|
| إنشاء الشبكة | docker network create discourse-net |
| التحقق | docker network ls | grep discourse-net |
هذا يسمح بـ:
• DISCOURSE_DB_HOST=pg
• DISCOURSE_REDIS_HOST=redis
ليعمل بشكل صحيح.
- الأسرار
| الغرض | الأمر |
|---|---|
| مستخدم خارق لـ Postgres | export PG_SUPERPASS='REPLACE_ME_super_strong' |
| كلمة مرور قاعدة بيانات Discourse | export DISCOURSE_DBPASS='REPLACE_ME_discordb_strong' |
| كلمة مرور Redis | export REDIS_PASS='REPLACE_ME_redis_strong' |
| مفتاح السر الأساسي | export SECRET_KEY_BASE="$(openssl rand -hex 64)" |
- حاوية Postgres
| الخطوة | الأمر |
|---|---|
| إنشاء المجلد | mkdir -p /var/discourse/external/postgres |
| تشغيل الحاوية | docker run -d --name pg --restart=always --network=discourse-net -e POSTGRES_PASSWORD="$PG_SUPERPASS" -v /var/discourse/external/postgres:/var/lib/postgresql/data postgres:15 |
| التحقق | docker ps | grep pg |
- إنشاء قاعدة البيانات
| الخطوة | الأمر |
|---|---|
| إنشاء الدور | docker exec -it pg psql -U postgres -c "CREATE ROLE discourse LOGIN PASSWORD '$DISCOURSE_DBPASS';" |
| إنشاء قاعدة البيانات | docker exec -it pg psql -U postgres -c "CREATE DATABASE discourse OWNER discourse ENCODING 'UTF8' TEMPLATE template0;" |
| البحث النصي | docker exec -it pg psql -U postgres -d discourse -c "ALTER DATABASE discourse SET default_text_search_config = 'pg_catalog.english';" |
| اختبار تسجيل الدخول | docker exec -it pg psql -U discourse -d discourse -c "select 1;" |
- امتداد PGVECTOR
مطلوب لإصدارات Discourse الحديثة.
| الخطوة | الأمر |
|---|---|
| التثبيت | docker exec -it pg bash -lc 'apt-get update && apt-get install -y postgresql-15-pgvector && rm -rf /var/lib/apt/lists/*' |
| إنشاء الامتداد | docker exec -it pg psql -U postgres -d discourse -c "CREATE EXTENSION IF NOT EXISTS vector;" |
| التحقق | docker exec -it pg psql -U postgres -d discourse -c "SELECT extname FROM pg_extension WHERE extname='vector';" |
- حاوية Redis
| الخطوة | الأمر |
|---|---|
| إنشاء المجلد | mkdir -p /var/discourse/external/redis |
قالب إعداد Redis:
requirepass REPLACE_ME_REDIS
appendonly yes
save 900 1
save 300 10
save 60 10000
| الخطوة | الأمر |
|---|---|
| كتابة الإعداد | tee /var/discourse/external/redis/redis.conf >/dev/null <<EOF |
| إدراج كلمة المرور | sed -i "s/REPLACE_ME_REDIS/$REDIS_PASS/" /var/discourse/external/redis/redis.conf |
| تشغيل Redis | docker run -d --name redis --restart=always --network=discourse-net -v /var/discourse/external/redis:/data -v /var/discourse/external/redis/redis.conf:/usr/local/etc/redis/redis.conf redis:7-alpine redis-server /usr/local/etc/redis/redis.conf |
| اختبار المصادقة | docker exec -it redis redis-cli -a "$REDIS_PASS" ping |
- تخطيط مجلدات Discourse
| الخطوة | الأمر |
|---|---|
| إنشاء المجلد الأساسي | mkdir -p /var/discourse |
| الدخول | cd /var/discourse |
| استنساخ المستودع | git clone https://github.com/discourse/discourse_docker.git |
| مجلد الحاويات | mkdir -p /var/discourse/containers |
| سجلات مشتركة | mkdir -p /var/discourse/shared/web-only/log/var-log |
| ربط الحاويات | ln -sfn /var/discourse/containers /var/discourse/discourse_docker/containers |
| ربط المشغل | ln -sfn /var/discourse/discourse_docker/launcher /var/discourse/launcher |
- حاويات التطبيق
app1.yml
• web + sidekiq
• المنفذ 8001
docker_args: "--network=discourse-net"
expose:
- "8001:80"
app2.yml
• web فقط
• المنفذ 8002
• sidekiq معطل
docker_args: "--network=discourse-net"
expose:
- "8002:80"
run:
- exec: bash -lc 'mkdir -p /etc/service/sidekiq && touch /etc/service/sidekiq/down'
- التمهيد
| الخطوة | الأمر |
|---|---|
| الدخول | cd /var/discourse/discourse_docker |
| تمهيد app1 | ./launcher bootstrap app1 |
| تشغيل app1 | ./launcher start app1 |
| تمهيد app2 | ./launcher bootstrap app2 |
| تشغيل app2 | ./launcher start app2 |
- فحوصات الصحة
| الخطوة | الأمر |
|---|---|
| app1 | curl -sSf http://127.0.0.1:8001/srv/status |
| app2 | curl -sSf http://127.0.0.1:8002/srv/status |
| sidekiq app1 | docker exec -it app1 pgrep -fa sidekiq |
| sidekiq app2 | `docker exec -it app2 pgrep -fa sidekiq |
- شهادة TLS
| الخطوة | الأمر |
|---|---|
| إيقاف الوكيل | systemctl stop haproxy |
| إصدار الشهادة | certbot certonly --standalone -d example.com --agree-tos -m you@example.com --non-interactive |
| تشغيل الوكيل | systemctl start haproxy |
- منطق HAProxy
frontend fe_discourse
bind :80
bind :443 ssl crt /etc/letsencrypt/live/example.com/haproxy.pem
http-request set-header X-Forwarded-Proto https if { ssl_fc }
http-request set-header X-Forwarded-Proto http if !{ ssl_fc }
redirect scheme https code 301 if !{ ssl_fc }
use_backend be_discourse if { nbsrv(be_discourse) gt 0 }
default_backend be_maint
backend be_discourse
balance roundrobin
option httpchk GET /srv/status
server app1 127.0.0.1:8001 check
server app2 127.0.0.1:8002 check
backend be_maint
http-request return status 503 content-type text/html string "<h1>Maintenance</h1>"
- إعادة البناء بدون توقف
| الخطوة | الأمر |
|---|---|
| تعطيل app1 | echo "disable server be_discourse/app1" | socat stdio /run/haproxy/admin.sock |
| إعادة بناء app1 | ./launcher rebuild app1 |
| تمكين app1 | echo "enable server be_discourse/app1" | socat stdio /run/haproxy/admin.sock |
| الخطوة | الأمر |
|---|---|
| تعطيل app2 | echo "disable server be_discourse/app2" | socat stdio /run/haproxy/admin.sock |
| إعادة بناء app2 | ./launcher rebuild app2 |
| تمكين app2 | echo "enable server be_discourse/app2" | socat stdio /run/haproxy/admin.sock |
النهاية
شبكة Docker مطلوبة
Postgres و Redis خارجيان
pgvector مثبت
Sidekiq معزول في app1
فحوصات صحة HAProxy مفعلة
خيار التراجع عن الأعطال نشط
إعادة البناء المتدحرجة مدعومة
نقل موقع واحد إلى خادمه الخاص لاحقًا
إحدى مزايا تشغيل تثبيتات مستقلة بالكامل لـ Discourse (بدلاً من multisite) هي أن النقل مباشر ومنخفض المخاطر.
كل مثيل لـ Discourse لديه بالفعل:
• حاوية خاصة به
• تحميلات خاصة به
• قاعدة بيانات خاصة به
• استخدام Redis خاص به
• ملف app.yml خاص به
لا حاجة لفك اقتران multisite.
خطوات النقل عالية المستوى
- توفير VPS جديد
ثبت Docker و Discourse بشكل طبيعي على الخادم الجديد.
لا تقم بإعداد multisite.
- إنشاء نسخة احتياطية كاملة
من الموقع المصدر:
الإدارة → النسخ الاحتياطي → إنشاء نسخة احتياطية
قم بتنزيل ملف النسخة الاحتياطية.
يشمل ذلك:
• قاعدة البيانات
• التحميلات
• المستخدمين
• الإعدادات
• السمات
- الاستعادة على الخادم الجديد
على الخادم الجديد:
• إكمال الإعداد الأولي
• تسجيل الدخول كمسؤول
• رفع النسخة الاحتياطية
• الاستعادة
يتعامل Discourse تلقائيًا مع توافق المخطط.
- تبديل DNS
قم بتحديث سجل A للنطاق ليوجه إلى عنوان IP للخادم الجديد.
بمجرد انتشار DNS، ينتقل المستخدمون بشكل شفاف.
- إيقاف الحاوية القديمة
على الخادم الأصلي:
• إيقاف الحاوية القديمة
• إزالتها عند التأكد
التثبيتات الأخرى لـ Discourse على نفس المضيف لا تتأثر.
لماذا هذا أبسط من multisite
في إعدادات multisite، غالبًا ما يتطلب النقل:
• فصل قواعد البيانات
• استخراج البيانات الخاصة بكل موقع
• تعديل ملف multisite.yml
• إعادة هيكلة Sidekiq
• إعادة تكوين التحميلات والبريد الإلكتروني
مع التثبيتات المستقلة، لا حاجة لأي من ذلك.
كل موقع مستقل بالفعل.
ملخص
هذا النهج يضحي ببعض التعقيد التشغيلي في البداية
مقابل فصل بسيط جدًا لاحقًا.
يعمل بشكل خاص جيدًا أثناء التجارب
أو بناء المجتمع في مراحله الأولى.
متى لا يكون هذا النهج خيارًا جيدًا على الأرجح
عادةً لا يكون هذا الإعداد فكرة جيدة إذا:
• تتوقع المواقع حركة مرور متوسطة أو عالية في وقت مبكر
• تعتمد بشكل كبير على الدعم الرسمي لـ Discourse
• غير مرتاح لتصحيح أخطاء Docker أو الشبكات أو الوكلاء العكسيين
• متطلبات وقت التشغيل صارمة أو حرجة للعمل
• المواقع مترابطة تشغيليًا بشكل وثيق
• تتوقع تجارب متكررة للإضافات عبر جميع المثيلات
في هذه الحالات، إما:
• إعداد multisite مدعوم
أو
• تثبيت واحد لـ Discourse لكل خادم
سيؤدي عادةً إلى مفاجآت تشغيلية أقل.
ملاحظة هامة
هذا النهج يزيد من مرونة البنية التحتية،
لكنه يزيد أيضًا من مسؤولية المسؤول.
يعمل بشكل أفضل عندما يكون الشخص المشغّل مريحًا في امتلاك البنية الكاملة
ومعالجة الأعطال العرضية كجزء من عملية التعلم.
إذا كانت الاستقرار والقابلية للدعم هما الأهداف الأساسية،
فإن الإعداد المدعوم هو دائمًا الخيار الأفضل تقريبًا.