Discourse-Container mit UnixSocket für Redis?

Ich bin neugierig, ob jemand in seinem Standalone-Container unixsocket /var/discourse/shared/... für Redis verwendet. Es scheint, dass redis-rb auch Domain-Sockets mit folgendem Code unterstützt:

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

Laut diesem Artikel auf Medium ist die Verbindung über einen Unix Domain Socket etwa 20 % schneller als die Verwendung von TCP-Sockets.

Außerdem würde die Verwendung von Unix Domain Sockets es erleichtern, mehrere Standalone-Container auszuführen, ohne Web- und Datencontainer zu trennen. Andernfalls denke ich, dass es zu Konflikten kommt, wenn Redis auf 127.0.0.1 lauscht.

Ich versuche derzeit, zwei vollständig unabhängige Container auf einem Host einzurichten. Da beide für sehr kleine Websites gedacht sind, würde ich die Flexibilität bevorzugen, sie in Standalone-Containern zu halten. Leider führt das Lauschen von Redis auf 127.0.0.1 (und vermutlich auch von Postgres) zu Konflikten.

1 „Gefällt mir“

Hallo @ryanerwin

Es scheint, als verstünden Sie Container und Docker noch nicht ganz; lassen Sie mich Ihnen daher helfen.

Redis läuft standardmäßig im eigenständigen Container auf Port 6379:

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

74

Beenden wir nun den Container und prüfen mit netstat, ob Redis läuft:

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

Wie Sie sehen können, lauscht Redis innerhalb des Containers auf localhost; und localhost innerhalb des Containers wurde (wird) nicht nach außen exponiert.

Daher gibt es bei vielen laufenden eigenständigen Discourse-Containern keine Redis-Konflikte zwischen den Containern, da Redis nicht nach außen exponiert wurde.

Deswegen nennt man es auch einen „Container“… :slight_smile:

Jeder Socket innerhalb des Containers muss explizit exponiert werden, um außerhalb des Containers verfügbar zu sein.

Ich hoffe, das hilft Ihnen ein kleines bisschen weiter.


Beachten Sie, dass Unix Domain Sockets sehr cool sind… Ich habe mich nur auf Ihren Kommentar zu den wahrgenommenen Redis-Konflikten zwischen eigenständigen Containern bezogen und das Thema Unix Domain Sockets nicht weiter behandelt.

5 „Gefällt mir“

So habe ich mir das auch vorgestellt, als Discourse in Docker läuft. Doch als ich es tatsächlich ausführte, sah ich während des Bootstrap-Prozesses Folgendes:

INFO -- : > cd /var/www/discourse && git reset --hard
# oO0OoO0OoO0Oo Redis startet 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.

Ich hatte diesen Thread über „install fails because of other redis container“ gefunden, doch das Problem lag tatsächlich an zu wenig Speicherplatz… Ich habe einige Dinge umorganisiert, und es funktioniert jetzt einwandfrei mit mehreren eigenständigen Containern.

2 „Gefällt mir“

Lieber @ryanerwin,

Es ist großartig zu lesen, dass Sie das zugrunde liegende Problem gefunden haben und nun verstehen, dass Redis „im Container“ läuft und (aus Sicht der Socket-E/A) nicht außerhalb des Containers (so wie standardmäßig eingerichtet) exponiert ist.

Was die Verwendung von Unix-Domain-Sockets mit Redis betrifft, halte ich das für eine hervorragende Idee. Ich habe dies noch nicht eingerichtet und habe auch noch keine Anleitung gelesen, wie man dies für Discourse einrichtet. Daher ermutige ich Sie, diese Option weiter zu erkunden.

Falls ich Zeit habe, werde ich dies weiter recherchieren und versuchen, Redis auf einem Staging-Server für Discourse so einzurichten, dass es einen Unix-Domain-Socket verwendet. In der Zwischenzeit wäre es sehr geschätzt, wenn Sie dies selbst herausfinden und Ihre Ergebnisse veröffentlichen könnten. Ich bin sicher, dass viele andere ebenfalls an diesem interessanten Redis-Thema interessiert sind.

Vielen Dank.

3 „Gefällt mir“

Hallo @ryanerwin,

ich hoffe, du wirst erfreut zu hören, dass ich Discourse mit Redis über einen Unix-Socket in einem Standalone-Container zum Laufen gebracht habe, genau wie du gefragt hast.

Schau dir den unteren Teil dieses Screenshots von Sidekiq an:

Außerdem hier noch ein paar weitere Screenshots vom Aufbau der App:

Meine nächsten Schritte sind:

  1. Den Unix-Socket in das gemeinsame Volume verschieben, damit er von außerhalb des Containers erreichbar ist.
  2. Erneutes Testen mit minimalen ENV-Variablen, um die „nötigsten
4 „Gefällt mir“

PR dazu:

Hinweis:

Zur Implementierung:

  • Ändern Sie die Redis-Vorlage in der container.yml-Datei
  • Fügen Sie derselben container.yml-Datei eine zusätzliche Zeile hinzu
 ## Setzen Sie die REDIS_URL und verwenden Sie redis.socketed.template.yml, um 
 ## einen Unix-Domain-Socket für Redis zu nutzen
 REDIS_URL: unix:///shared/tmp/redis.sock

Implementierungshinweise:

  1. Wenn Sie Bedenken hinsichtlich der Sicherheit der Redis-Datenbank auf dem Host haben, müssen Sie diesen Unix-Socket im freigegebenen Volume nicht exponieren.

  2. Wenn Sie die Berechtigungen des Unix-Sockets auf 770 (statt 777) ändern möchten, ändern Sie die Gruppe des Unix-Sockets auf www-data.

4 „Gefällt mir“

Ist sonst noch jemandem aufgefallen, dass REDIS_URL keine Auswirkung mehr hat? Sowohl beim (Neu-)Build als auch beim Start des Containers versucht er, über redis://localhost:6379 eine Verbindung herzustellen, obwohl REDIS_URL so eingestellt ist, dass er über einen UNIX-Socket verbunden wird.

Einerseits ist das sinnvoll, da Discourse Konfigurationen für Host und Port hat und nur diese anwendet: discourse/app/models/global_setting.rb at main · discourse/discourse · GitHub
Dort gäbe es einen path-Parameter zur Definition eines UNIX-Socket-Pfades, der Host und Port überschreiben würde. Und es gäbe einen url-Parameter zur Definition einer vollständigen URL wie unix:///shared/redis_data/redis.sock.

Das Redis-Gem dokumentiert, dass die Umgebungsvariable REDIS_URL alle anderen Einstellungen überschreiben würde, und bis zu einer bestimmten Discourse/Redis-Gem-Version funktionierte dies: redis-rb/lib/redis.rb at master · redis/redis-rb · GitHub

Die Verwendung von UNIX-Sockets brach, als Discourse zur Redis-Gem-Version 5 wechselte: DEV: Upgrade the Redis gem to v5.4 · discourse/discourse@2ed31fe · GitHub
Ich vermute also, dass REDIS_URL im Redis-Gem (das andere Optionen nicht mehr überschreibt) und nicht bei Discourse (wo der zugehörige Code nicht geändert wurde) kaputtgegangen ist?

Wahrscheinlich hat dieser Commit es kaputt gemacht: Use redis-client as transport · redis/redis-rb@08a2100 · GitHub
Ich würde es im Redis-Gem-Repository melden, oder weiß jemand, dass es ein Problem mit der Implementierung von Discourse ist?

Die Umgebungsvariable wird übrigens korrekt übergeben. Ich habe sie beibehalten und nur Redis so konfiguriert, dass es nicht auf dem UNIX-Socket lauscht, und während Unicorn schön startet, schlägt Sidekiq fehl und vermisst den UNIX-Socket:

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'

Es passt ins Bild, dass REDIS_URL immer noch funktioniert, aber nur, wenn keine anderen Verbindungseinstellungen definiert sind. Wenn man sich die Fehlerspur ansieht, wendet global_setting.rb Host und Port nicht für Sidekiq an, wie es das für Unicorn tut.