|
تُرجمت هذه الوثيقة آليًا من الإنجليزية باستخدام Claude AI. ينبغي مراجعة المصطلحات المتعلقة بمجال WMO/الأرصاد الجوية من قِبل متحدث أصلي قبل الاستخدام الإنتاجي. راجع النسخة الإنجليزية الأصلية للاطلاع على النسخة المرجعية. |
1. النشر
1.1. المتطلبات الأساسية
-
Docker 20.10+
-
Docker Compose 2.0+
-
4 جيجابايت ذاكرة RAM كحد أدنى (يُنصح بـ 8 جيجابايت للإنتاج)
-
وصول شبكي إلى الوسيط العالمي WIS2 (المنفذ 443)
1.2. التثبيت
git clone https://github.com/World-Meteorological-Organization/wis2downloader
cd wis2downloader
cp default.env .env
عدِّل .env لتهيئة نشرك، ثم ابدأ التشغيل:
docker-compose up -d
1.3. تهيئة الأمان
قبل النشر في بيئة الإنتاج، يجب عليك تهيئة إعدادات الأمان التالية في ملف .env:
|
-
REDIS_PASSWORD- عيِّن كلمة مرور قوية وفريدة (مطلوب) -
FLASK_SECRET_KEY- عيِّن مفتاح سري عشوائي من الناحية التشفيرية (مطلوب) -
FLASK_DEBUG- عيِّن القيمةfalseفي الإنتاج -
كلمة مرور Grafana - غيِّر القيمة الافتراضية
admin/adminبعد تسجيل الدخول الأول
يحتوي ملف .env على بيانات اعتماد حساسة. تأكد من أنه:
-
غير مُرفَق بنظام التحكم في الإصدارات (أضفه إلى
.gitignore) -
قابل للقراءة فقط من قِبل مستخدم النشر (
chmod 600 .env) -
مدعوم نسخًا احتياطيًا بشكل آمن
1.3.1. التحكم في الوصول
| لا يُطبِّق WIS2 Downloader أي مصادقة أو تفويض على واجهة المستخدم أو REST API لمدير الاشتراكات. يستطيع أي شخص يملك وصولًا شبكيًا إلى الواجهة (المنفذ 8080) أو API (المنفذ 5002) عرض الاشتراكات وإنشاؤها وتعديلها وحذفها. |
في بيئة مشتركة أو مواجهة للإنترنت، يجب حتمًا تقييد الوصول على مستوى الشبكة. الأساليب الموصى بها:
-
وكيل عكسي مع مصادقة — ضع nginx أو Caddy أمام منفذَي الواجهة وAPI، مع إلزامية HTTP Basic Auth أو OAuth قبل إعادة توجيه الطلبات. تهيئة nginx بسيطة:
location / { auth_basic "WIS2 Downloader"; auth_basic_user_file /etc/nginx/.htpasswd; proxy_pass http://localhost:8080; } -
قواعد جدار الحماية — قيِّد المنفذَين 8080 و5002 على نطاقات IP موثوقة باستخدام
iptablesأوufwأو مجموعات أمان سحابية. -
VPN / شبكة خاصة — انشر النظام خلف VPN بحيث لا يتمكن من الوصول إليه إلا مستخدمو VPN المصادَق عليهم.
إذا كان النظام يعمل على خادم بدلًا من localhost، فتأكد من عدم إمكانية الوصول العام إلى المنفذَين 8080 و5002 قبل إضافة أحد أساليب التحكم في الوصول المذكورة أعلاه.
1.4. أذونات وحدات التخزين
تعمل حاويات التطبيق بوصفها مستخدمًا غير جذر (wis2) لأغراض الأمان. للسماح للحاويات بكتابة الملفات المُنزَّلة على نظام ملفات المضيف، يجب تهيئة الأذونات المناسبة على مجلد downloads/.
1.4.1. تفاصيل مستخدم الحاوية
| الخاصية | القيمة |
|---|---|
اسم المستخدم |
|
معرّف المستخدم (UID) |
|
اسم المجموعة |
|
معرّف المجموعة (GID) |
|
يكتب عامل Celery الملفات في /data داخل الحاوية، والذي يُخصَّص لمسار ./downloads/ على المضيف.
1.4.2. الخيار 1: إنشاء مستخدم/مجموعة مطابقَيْن (موصى به)
أنشئ مستخدمًا ومجموعة على النظام المضيف بحيث يتطابقان مع UID/GID للحاوية:
# إنشاء مجموعة wis2 بـ GID 988
sudo groupadd -g 988 wis2
# إنشاء مستخدم wis2 بـ UID 10001
sudo useradd -u 10001 -g wis2 -M -s /usr/sbin/nologin wis2
# تعيين الملكية على دليل التنزيلات
sudo mkdir -p downloads
sudo chown -R wis2:wis2 downloads
1.4.3. الخيار 2: استخدام قوائم التحكم في الوصول (ACL) (مرن)
إذا لم تتمكن من إنشاء مستخدم/مجموعة مطابقَيْن، استخدم قوائم التحكم في الوصول POSIX لمنح UID الحاوية حق الوصول للكتابة:
# التأكد من تثبيت دعم ACL
sudo apt-get install acl # Debian/Ubuntu
sudo yum install acl # RHEL/CentOS
# إنشاء دليل التنزيلات
mkdir -p downloads
# منح UID 10001 وصولاً كاملاً
sudo setfacl -R -m u:10001:rwx downloads
sudo setfacl -R -d -m u:10001:rwx downloads # Default ACL for new files
1.4.4. الخيار 3: مجلد متساهل في الأذونات
| هذا النهج أقل أمانًا ولا يُنصح به في بيئة الإنتاج. |
mkdir -p downloads
chmod 777 downloads
1.4.5. التحقق من الأذونات
اختبر أن الحاوية تستطيع الكتابة في وحدة التخزين:
# تشغيل حاوية celery
docker-compose up -d celery
# اختبار صلاحية الكتابة من داخل الحاوية
docker exec celery touch /data/test-write && echo "Write OK" || echo "Write FAILED"
# التنظيف
docker exec celery rm -f /data/test-write
إذا فشل الاختبار بخطأ "Permission denied"، راجع ملكية وأذونات مجلد downloads/ على المضيف.
1.4.6. الوصول المشترك مع مستخدمي المضيف
إذا كنت بحاجة إلى أن يتمكن مستخدمو المضيف من قراءة الملفات المُنزَّلة، أضفهم إلى مجموعة wis2:
# إضافة مستخدمك إلى مجموعة wis2
sudo usermod -aG wis2 $USER
# تسجيل الخروج والدخول مجدداً لتفعيل عضوية المجموعة
# ثم التحقق
groups # Should include 'wis2'
بدلًا من ذلك، يمكنك استخدام ACL لمنح حق القراءة لمستخدمين محددين:
# منح صلاحية القراءة لمستخدم محدد
sudo setfacl -R -m u:youruser:rx downloads
sudo setfacl -R -d -m u:youruser:rx downloads
1.5. التحقق من النشر
تستخدم الأمثلة التالية localhost. إذا كان النظام منشورًا على خادم بعيد، استبدل localhost باسم مضيف الخادم أو عنوان IP الخاص به. يمكن تغيير المنافذ (5002 لـ API، و8080 للواجهة) في docker-compose.yaml إذا لزم الأمر.
|
# التحقق من تشغيل جميع الخدمات
docker-compose ps
# التحقق من نقطة نهاية الصحة (مدير الاشتراكات)
curl http://localhost:5002/health
# التحقق من تشغيل واجهة الويب
curl -o /dev/null -s -w "%{http_code}" http://localhost:8080
# عرض السجلات
docker-compose logs -f subscriber
2. التهيئة
2.1. متغيرات البيئة
2.1.1. إعدادات التطبيق
| المتغير | النوع | القيمة الافتراضية | الوصف |
|---|---|---|---|
|
string |
|
مستوى التسجيل: DEBUG، INFO، WARNING، ERROR |
|
path |
|
المجلد الأساسي للملفات المُنزَّلة |
|
string |
مطلوب |
مفتاح سري لجلسة Flask (يفشل التطبيق إذا لم يُعيَّن) |
|
boolean |
|
تفعيل وضع تصحيح أخطاء Flask (عيِّن |
|
string |
|
عنوان ربط API |
|
integer |
|
منفذ ربط API (داخلي) |
2.1.2. إعدادات الوسيط MQTT
| المتغير | النوع | القيمة الافتراضية | الوصف |
|---|---|---|---|
|
string |
مطلوب |
اسم مضيف الوسيط العالمي WIS2 (مثال: |
|
integer |
|
منفذ الوسيط |
|
string |
|
اسم مستخدم MQTT |
|
string |
|
كلمة مرور MQTT |
|
string |
|
بروتوكول النقل: |
|
string |
يُولَّد تلقائيًا |
معرّف جلسة دائم للاستئناف بعد إعادة التشغيل |
2.1.3. إعدادات Redis
| المتغير | النوع | القيمة الافتراضية | الوصف |
|---|---|---|---|
|
string |
|
اسم مضيف خادم Redis |
|
integer |
|
منفذ خادم Redis |
|
integer |
|
رقم قاعدة بيانات Redis |
|
string |
مطلوب |
كلمة مرور مصادقة Redis (يفشل التطبيق إذا لم تُعيَّن) |
|
integer |
|
مدة البقاء لمفاتيح إزالة التكرار |
|
integer |
|
انتهاء صلاحية القفل لمنع التنزيلات المتزامنة |
2.1.4. إعدادات Celery
| المتغير | النوع | القيمة الافتراضية | الوصف |
|---|---|---|---|
|
string |
|
سلسلة اتصال الوسيط Celery (لاحظ تنسيق كلمة المرور) |
|
string |
|
سلسلة اتصال خلفية النتائج (لاحظ تنسيق كلمة المرور) |
2.1.5. تصفية التنزيلات
| المتغير | النوع | القيمة الافتراضية | الوصف |
|---|---|---|---|
|
string |
فارغ |
قائمة مفصولة بفواصل لأسماء مضيفي ذاكرة التخزين المؤقت العالمية المراد استبعادها |
2.1.6. إعدادات واجهة المستخدم
| المتغير | النوع | القيمة الافتراضية | الوصف |
|---|---|---|---|
|
string |
عنوان URL الداخلي الذي تستخدمه الواجهة للوصول إلى API مدير الاشتراكات |
|
|
string |
عنوان URL المستخدم لتضمين لوحات Grafana في عرض لوحة التحكم |
|
|
string |
|
مفتاح سري لتخزين جلسة NiceGUI — عيِّن قيمة فريدة في الإنتاج |
|
integer |
|
ثوانٍ لتخزين بيانات كتالوج GDC مؤقتًا في Redis (6 ساعات افتراضيًا)؛ عيِّن |
2.2. هيكل الملفات
wis2downloader/
├── config/
│ ├── grafana/provisioning/ # Grafana datasource config
│ ├── loki/ # Loki configuration
│ └── prometheus/ # Prometheus scrape config
├── containers/ # Dockerfiles
├── downloads/ # Downloaded data (mounted volume)
├── modules/ # Python modules
│ ├── shared/ # Shared utilities
│ ├── subscriber/ # MQTT subscriber
│ ├── subscription_manager/ # REST API
│ ├── task_manager/ # Celery tasks
│ └── ui/ # NiceGUI web interface
├── docker-compose.yaml
├── default.env
└── README.adoc
3. التوسع
3.1. Redis
يستخدم النظام نسخة واحدة من Redis لـ:
-
تبادل الرسائل pub/sub بين الخدمات
-
تخزين حالة الاشتراكات
-
تتبع إزالة التكرار
-
قائمة انتظار مهام Celery والنتائج
يستخدم النظام نسخة Redis واحدة ولا يوفر توافرًا عاليًا. بصرف النظر عن الحجم، تأكد من ديمومة بيانات Redis:
-
تفعيل استمرارية Redis (AOF + RDB)
-
تنفيذ نسخ احتياطية منتظمة لـ dump Redis
3.2. توسع العمال
زيادة تزامن عامل Celery لإنتاجية أعلى:
# docker-compose.yaml
celery:
command: ["... --concurrency=16 ..."]
أو تشغيل حاويات عمال متعددة:
docker-compose up -d --scale celery=3
3.3. إضافة وسطاء عالميين
تتصل كل خدمة مشترك بوسيط عالمي WIS2 واحد. لاستقبال البيانات من وسطاء متعددين، أضف خدمات مشترك إضافية في docker-compose.yaml:
subscriber-france:
container_name: subscriber-france
restart: always
build:
context: .
dockerfile: ./containers/subscriber/Dockerfile
env_file: *default-env
environment:
GLOBAL_BROKER_HOST: globalbroker.meteo.fr
GLOBAL_BROKER_PORT: 443
GLOBAL_BROKER_USERNAME: everyone
GLOBAL_BROKER_PASSWORD: everyone
MQTT_PROTOCOL: websockets
depends_on:
- redis
networks:
- redis-net
logging: *default-logging
subscriber-china:
container_name: subscriber-china
restart: always
build:
context: .
dockerfile: ./containers/subscriber/Dockerfile
env_file: *default-env
environment:
GLOBAL_BROKER_HOST: gb.wis.cma.cn
GLOBAL_BROKER_PORT: 443
GLOBAL_BROKER_USERNAME: everyone
GLOBAL_BROKER_PASSWORD: everyone
MQTT_PROTOCOL: websockets
depends_on:
- redis
networks:
- redis-net
logging: *default-logging
يجب أن يكون لكل مشترك container_name فريد.
|
تشترك جميع المشتركين في نفس خلفية Redis، لذا يتلقى جميع نُسخ المشتركين الاشتراكات التي أُنشئت عبر API. يتم إزالة التكرار من التنزيلات، لذلك لن يُنزَّل نفس الملف مرتين حتى لو وصلت إشعارات من وسطاء متعددين.
4. المراقبة
4.1. مقاييس Prometheus
نقطة نهاية المقاييس: http://localhost:5002/metrics
تُخزَّن المقاييس بصورة ذرية في Redis باستخدام HINCRBYFLOAT وتُعرَض عند نقطة النهاية /metrics بتنسيق نص Prometheus. هذا الأسلوب آمن عبر حاويات عامل Celery المتعددة دون الحاجة إلى نظام ملفات مشترك أو مجلد متعدد العمليات لـ prometheus_client.
|
4.1.1. المقاييس المتاحة
| المقياس | النوع | الوصف |
|---|---|---|
|
Counter |
رسائل MQTT المستلَمة قبل وضعها في قائمة الانتظار (التسميات: broker)؛ قارنها بـ |
|
Counter |
إجمالي الإشعارات المعالَجة بواسطة Celery (التسميات: status) |
|
Counter |
الملفات المُنزَّلة بنجاح (التسميات: cache, media_type) |
|
Counter |
إجمالي البايتات المُنزَّلة (التسميات: cache, media_type) |
|
Counter |
الإشعارات المتخطاة حسب السبب (التسميات: reason) |
|
Counter |
التنزيلات الفاشلة (التسميات: cache, reason) |
|
Counter |
إخفاقات وضع مهمة Celery في قائمة الانتظار من قِبل المشترك (التسميات: broker) |
|
Gauge |
العدد الحالي للمهام في قائمة انتظار Celery |
|
Gauge |
إجمالي سعة القرص لوحدة تخزين التنزيل |
|
Gauge |
مساحة القرص المستخدَمة على وحدة تخزين التنزيل |
|
Gauge |
مساحة القرص المتاحة على وحدة تخزين التنزيل |
|
Gauge |
البايتات المستخدَمة من قِبل الملفات المُنزَّلة (يُتتبَّع بصورة تراكمية) |
4.1.2. أمثلة على الاستعلامات
# معدل التنزيل في الدقيقة
sum(rate(wis2downloader_downloads_total[1m]))
# التنزيلات حسب الذاكرة المؤقتة
sum by (cache) (rate(wis2downloader_downloads_total[5m]))
# التنزيلات الفاشلة حسب السبب
sum by (reason) (wis2downloader_failed_total)
# البايتات المنزَّلة في الساعة الأخيرة
sum(increase(wis2downloader_downloads_bytes_total[1h]))
# عمق قائمة الانتظار
wis2downloader_celery_queue_length
4.2. Grafana
الوصول إلى Grafana على http://localhost:3000 (بيانات الاعتماد الافتراضية: admin/admin)
غيِّر كلمة مرور Grafana الافتراضية بعد تسجيل الدخول الأول أو هيِّئها عبر متغير البيئة GF_SECURITY_ADMIN_PASSWORD في عمليات نشر الإنتاج.
|
مصادر البيانات المُهيَّأة مسبقًا:
-
Prometheus -
http://prometheus:9090 -
Loki -
http://loki:3100
4.3. التسجيل
تُسجِّل جميع الخدمات إلى stdout بطوابع زمنية UTC:
2026-01-28T10:15:30.123Z subscriber INFO Connected successfully
عرض السجلات عبر Docker:
# جميع الخدمات
docker-compose logs -f
# خدمة محددة
docker-compose logs -f celery
# مع الطوابع الزمنية
docker-compose logs -f -t subscriber
تُجمِّع Loki السجلات أيضًا ويمكن الاستعلام عنها في Grafana.
5. الصيانة
5.1. النسخ الاحتياطي
5.1.1. بيانات Redis
# تشغيل لقطة RDB
docker exec redis redis-cli -a $REDIS_PASSWORD BGSAVE
# نسخ اللقطة
docker cp redis:/data/dump.rdb ./backup/
5.1.2. الملفات المُنزَّلة
# النسخ الاحتياطي لدليل التنزيلات
tar -czf wis2-downloads-$(date +%Y%m%d).tar.gz downloads/
5.2. مسح البيانات
| هذه العمليات مدمِّرة. |
# مسح جميع الاشتراكات
docker exec redis redis-cli -a $REDIS_PASSWORD DEL global:subscriptions
# مسح ذاكرة إزالة التكرار (يتيح إعادة تنزيل جميع البيانات)
docker exec redis redis-cli -a $REDIS_PASSWORD KEYS "wis2:notifications:*" | xargs -r docker exec -i redis redis-cli -a $REDIS_PASSWORD DEL
# مسح الملفات المنزَّلة
rm -rf downloads/*
5.3. إعادة تشغيل الخدمات
# إعادة تشغيل خدمة واحدة
docker-compose restart subscriber
# إعادة تشغيل جميع الخدمات
docker-compose restart
# إعادة بناء كاملة (بعد تغييرات الكود)
docker-compose build && docker-compose up -d
6. استكشاف الأخطاء وإصلاحها
6.1. الخدمة لا تبدأ
# فحص السجلات
docker-compose logs <service-name>
# فحص حالة الحاوية
docker-compose ps
# التحقق من البيئة
docker-compose config
6.2. لا توجد تنزيلات
-
تحقق من أن المشترك متصل:
docker-compose logs subscriber | grep -i connect -
تحقق من وجود الاشتراك:
curl http://localhost:5002/subscriptions -
تحقق من أن عامل Celery يعمل:
docker-compose logs celery | tail -50 -
تحقق من عمق قائمة الانتظار:
curl -s http://localhost:5002/metrics | grep queue_length
6.3. أخطاء اتصال Redis
-
تحقق من تشغيل Redis:
docker exec redis redis-cli -a $REDIS_PASSWORD PING -
تحقق من اتصالية الشبكة:
docker network inspect wis2downloader_redis-net
6.4. الاستخدام العالي للذاكرة
-
زيادة
max-tasks-per-childلإعادة تدوير العمال بتكرار أكبر -
تقليل تزامن العمال
-
التحقق من الملفات الكبيرة التي تُرهق العمال
6.5. المقاييس لا تتحدث
-
فترة استقراء Prometheus هي 15 ثانية افتراضيًا
-
تحقق من أهداف Prometheus عبر Grafana: Connections → Data sources → Prometheus → Save & test
-
تحقق من نقطة نهاية المقاييس:
curl http://localhost:5002/metrics
6.6. واجهة المستخدم: بيانات الكتالوج غير محمَّلة
إذا كانت طرق عرض البحث في الكتالوج أو البحث بالشجرة تُظهر "Catalogue data not loaded":
-
تحقق مما إذا كانت حاوية الواجهة قد بدأت بنجاح:
docker-compose logs wis2downloader-ui | tail -30 -
تأكد من وجود مفاتيح ذاكرة التخزين المؤقت GDC في Redis:
docker exec redis redis-cli -a $REDIS_PASSWORD KEYS "gdc:cache:*"إذا لم تكن هناك مفاتيح، فقد فشل الجلب الأولي أو لا يزال قيد التنفيذ. انتظر لحظة وأعد تحميل الصفحة.
-
أجبر على جلب جديد من طريقة عرض الإعدادات (
http://localhost:8080) بالنقر على تحديث بيانات GDC، أو بإعادة تشغيل حاوية الواجهة:docker-compose restart wis2downloader-ui -
إذا كان جلب GDC يفشل باستمرار، تحقق من اتصالية الشبكة من حاوية الواجهة إلى نقاط نهاية GDC العامة:
docker exec wis2downloader-ui curl -s -o /dev/null -w "%{http_code}" \ https://wis2-gdc.weather.gc.ca/collections/wis2-discovery-metadata/items?limit=1