حاوية Discourse مع UnixSocket لـ Redis؟

هل يتساءل أحد ما إذا كان أي شخص يستخدم unixsocket /var/discourse/shared/... لـ Redis في حاويته المستقلة؟ يبدو أن redis-rb يدعم أيضًا sokتات النطاق مع:

redis = Redis.new(path: "/var/discourse/shared/standalone/redis.sock")

وفقًا لـ هذه المقالة على Medium، فإن الاتصال عبر Sokت النطاق يونكس أسرع بحوالي 20% مقارنة باستخدام Sokتات TCP…

أيضًا، استخدام Sokتات النطاق يونكس سيُسهّل تشغيل حاويات مستقلة متعددة دون فصل حاويات الويب عن حاويات البيانات… وإلا، أعتقد أنك ستواجه تضاربات مع Redis المستمع على 127.0.0.1…

أحاول حاليًا إعداد حاويتين مستقلتين تمامًا على مضيف واحد، وبما أنهما مخصصتان لمواقع صغيرة جدًا، فأنا أفضل مرونة إبقائهما في حاويات مستقلة… للأسف، استماع Redis على 127.0.0.1 (وربما Postgres أيضًا) سيُسبب تضاربات…

مرحبًا @ryanerwin

يبدو أنك لم تفهم تمامًا مفهوم الحاويات ودوكر (Docker)، لذا دعني أساعدك.

تعمل خدمة ريديس (Redis) افتراضيًا في الحاوية المستقلة على المنفذ 6379:

cd /var/discourse
./launcher enter app
apt install net-tools
netstat -an | grep :6379 |wc -l 

74

الآن، لنخرج من الحاوية ونتحقق من حالة خدمة ريديس باستخدام أمر netstat:

exit
root@localhost:/var/discourse# netstat -an | grep :6379 |wc -l 
0

كما تلاحظ، فإن خدمة ريديس تستمع على localhost داخل الحاوية؛ و localhost داخل الحاوية لم يتم (ولا يتم) تعريضها خارج الحاوية.

لذلك، إذا كان لديك العديد من حاويات discourse المستقلة قيد التشغيل، فلن توجد أي تعارضات بين الحاويات فيما يتعلق بخدمة ريديس، لأن خدمة ريديس لم تُعرَّض خارج كل حاوية.

لهذا السبب تُسمى “حاوية”… :slight_smile:

يجب تعريض كل مقبس (socket) داخل الحاوية بشكل صريح ليصبح متاحًا خارجها.

آمل أن يكون هذا مفيدًا ولو بشكل بسيط.


لاحظ أن مقابس نطاق يونكس (Unix domain sockets) رائعة جدًا… لقد تناولت فقط تعليقك حول التعارضات المتصورة بين حاويات ريديس المستقلة، ولم أتطرق إلى موضوع مقابس نطاق يونكس.

هذا ما كنت أتخيل أنه سيحدث عند تشغيل discourse داخل Docker أيضًا، ومع ذلك، عندما قمت بتشغيله فعليًا، رأيت أثناء عملية التمهيد:

INFO -- : > cd /var/www/discourse && git reset --hard
# oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
# Redis version=5.0.5, bits=64, commit=00000000, modified=0, pid=195, just started
# Configuration loaded
# Could not create server TCP listening socket *:6379: bind: Address already in use
Checking out files: 100% (27893/27893), done.

وقد وجدت هذا الموضوع حول “فشل التثبيت بسبب حاوية Redis أخرى”, ولكن المشكلة الفعلية كانت نقص مساحة القرص… قمت بإعادة تنظيم بعض الأشياء، ويعمل الآن بشكل ممتاز مع حاويات مستقلة متعددة.

عزيزي @ryanerwin،

من الرائع أن نقرأ أنك قد عثرت على المشكلة الجذرية وأدركت الآن أن Redis يعمل “داخل الحاوية” ولا يتم تعريضه (من منظور إدخال/إخراج المقبس) خارج الحاوية (كما هو مضبوط افتراضيًا من الصندوق).

بشأن استخدام مقابس النطاقات المحلية (unix domain sockets) مع Redis، أعتقد أن هذه فكرة ممتازة. لم أقم بإعداد ذلك بعد، ولم أقرأ أي تقرير يشرح كيفية إعداده لـ Discourse، لذا أشجعك على مواصلة استكشاف هذا الخيار.

إذا توفّر لي الوقت، سأقوم بالبحث في هذا الأمر أكثر وحاول إعداد Redis لاستخدام مقبس نطاق محلي في Discourse على خادم تجريبي. وفي غضون ذلك، إذا تمكنت من حل هذه المسألة ونشر نتائجك، فسأكون ممتنًا للغاية. أنا متأكد من أن الكثيرين آخرين مهتمون أيضًا بهذا الموضوع المثير حول Redis.

شكرًا لك.

مرحبًا @ryanerwin،

آمل أن يسعدك سماع أن لدي Discourse يعمل مع Redis باستخدام مقبس Unix في حاوية مستقلة، تمامًا كما سألت عنه.

تفقد أسفل هذه اللقطة من sidekiq:

بالإضافة إلى ذلك، إليك بعض لقطات الشاشة الإضافية من بناء التطبيق:

خطواتي التالية هي:

  1. نقل مقبس Unix إلى الحجم المشترك حتى يمكن الوصول إليه خارج الحاوية.
  2. إعادة الاختبار باستخدام متغيرات البيئة (ENV vars) الدنيا للحصول على التغييرات “الأساسية” اللازمة لتشغيل هذا النظام.

بشكل عام، قمت بإنشاء قالب جديد:

-rw-r–r-- 1 root root 2028 Jun 6 08:13 redis.socketed.template.yml

Screen Shot 2020-06-06 at 4.18.55 PM

وقمت بإجراء تغييرات طفيفة على صديقنا القديم app.yml

بما أنني بدأت في هذا الأمر اليوم فقط، فإنني أخطط لإجراء المزيد من الاختبارات قبل نشر التفاصيل.

آمل أن يكون هذا مفيدًا.


تحديث


يعمل كما هو متوقع خارج الحاوية عندما يكون مقبس Unix على حجم مشترك:

طلب السحب (PR) لهذا:

ملاحظة:

للتنفيذ:

  • غيّر قالب Redis في ملف YAML الخاص بالحاوية.
  • أضف سطرًا واحدًا إضافيًا إلى نفس ملف YAML الخاص بالحاوية.
 ## تعيين REDIS_URL واستخدام redis.socketed.template.yml لاستخدام
 ## مخرج نطاق يونكس (unix domain socket) لـ Redis
 REDIS_URL: unix:///shared/tmp/redis.sock

ملاحظات التنفيذ:

  1. إذا كنت قلقًا بشأن أمان قاعدة بيانات Redis على المضيف، فلا داعي لكشف هذا المخرج (socket) في الحجم المشترك.

  2. إذا رغبت في تعيين أذونات المخرج إلى 770 (بدلاً من 777)، غيّر مجموعة المخرج إلى www-data.

هل لاحظ أي شخص آخر أن REDIS_URL لم يعد له أي تأثير؟ أثناء إعادة البناء (rebuild) وأيضًا عند بدء تشغيل الحاوية، على الرغم من تعيين REDIS_URL للاتصال عبر مقبس UNIX، فإنه يحاول الاتصال عبر redis://localhost:6379.

من ناحية، هذا منطقي، نظرًا لأن Discourse لديه إعدادات ويطبق المضيف والمنفذ فقط: discourse/app/models/global_setting.rb at main · discourse/discourse · GitHub
سيكون هناك معلمة path لتحديد مسار مقبس UNIX، والذي سيتجاوز المضيف والمنفذ. وسيكون هناك معلمة url، لتحديد عنوان URL كامل مثل unix:///shared/redis_data/redis.sock.

توضح مكتبة Redis أن متغير البيئة REDIS_URL سيتجاوز أي إعدادات أخرى، وحتى إصدار معين من مكتبة Discourse/Redis، نجح هذا: redis-rb/lib/redis.rb at master · redis/redis-rb · GitHub

تعطل استخدام مقبس UNIX عندما انتقلت Discourse إلى الإصدار 5 من مكتبات Redis: DEV: Upgrade the Redis gem to v5.4 · discourse/discourse@2ed31fe · GitHub
لذا، أعتقد أن REDIS_URL تعطل في مكتبة Redis (لم يعد يتجاوز الخيارات الأخرى)، وليس بواسطة Discourse (حيث لم يتغير الكود المتعلق به)؟

من المحتمل أن يكون هذا الالتزام قد تسبب في تعطله: Use redis-client as transport · redis/redis-rb@08a2100 · GitHub
سأقوم بالإبلاغ عنه في مستودع مكتبة Redis، أو هل يعرف شخص ما أن هذه مشكلة في كيفية تطبيق Discourse لها؟

يتم تمرير متغير البيئة بشكل صحيح بالمناسبة. تركته في مكانه وقمت فقط بتكوين Redis لعدم الاستماع على مقبس UNIX، وبينما يبدأ Unicorn بشكل جيد، يفشل Sidekiq، ويفتقد مقبس UNIX بدلاً من ذلك :sweat_smile::

Error in demon processes heartbeat check: No such file or directory - connect(2) for /shared/redis_data/redis.sock
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/redis-client-0.24.0/lib/redis_client/ruby_connection.rb:116:in `initialize'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/redis-client-0.24.0/lib/redis_client/ruby_connection.rb:116:in `new'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/redis-client-0.24.0/lib/redis_client/ruby_connection.rb:116:in `connect'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/redis-client-0.24.0/lib/redis_client/ruby_connection.rb:51:in `initialize'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/redis-client-0.24.0/lib/redis_client.rb:759:in `new'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/redis-client-0.24.0/lib/redis_client.rb:759:in `block in connect'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/redis-client-0.24.0/lib/redis_client/middlewares.rb:12:in `connect'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/redis-client-0.24.0/lib/redis_client.rb:758:in `connect'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/redis-client-0.24.0/lib/redis_client.rb:745:in `raw_connection'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/redis-client-0.24.0/lib/redis_client.rb:705:in `ensure_connected'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/redis-client-0.24.0/lib/redis_client.rb:285:in `call'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/redis_client_adapter.rb:36:in `block (2 levels) in <module:CompatMethods>'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/api.rb:912:in `block in cleanup'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/config.rb:175:in `block in redis'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/connection_pool-2.5.3/lib/connection_pool.rb:110:in `block (2 levels) in with'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/connection_pool-2.5.3/lib/connection_pool.rb:109:in `handle_interrupt'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/connection_pool-2.5.3/lib/connection_pool.rb:109:in `block in with'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/connection_pool-2.5.3/lib/connection_pool.rb:106:in `handle_interrupt'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/connection_pool-2.5.3/lib/connection_pool.rb:106:in `with'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/config.rb:172:in `redis'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq.rb:74:in `redis'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/api.rb:912:in `cleanup'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/api.rb:903:in `initialize'
/var/www/discourse/lib/demon/sidekiq.rb:25:in `new'
/var/www/discourse/lib/demon/sidekiq.rb:25:in `heartbeat_check'
config/unicorn.conf.rb:131:in `block (2 levels) in reload'

يتناسب هذا مع الصورة التي تفيد بأن REDIS_URL لا يزال يعمل، ولكن فقط إذا لم يتم تعريف إعدادات الاتصال بطريقة أخرى. بالنظر إلى تتبع الخطأ، لا يطبق global_setting.rb المضيف والمنفذ لـ Sidekiq، كما يفعل لـ Unicorn.