¿Contenedor de Discourse con UnixSocket para Redis?

Me pregunto si alguien está usando unixsocket /var/discourse/shared/... para Redis en su contenedor independiente. Parece que redis-rb también admite sockets de dominio con:

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

Según este artículo en Medium, conectarse mediante un socket de dominio Unix es aproximadamente un 20 % más rápido que usar sockets TCP…

Además, usar sockets de dominio Unix facilitaría ejecutar múltiples contenedores independientes sin necesidad de separar los contenedores de web y datos… De lo contrario, creo que terminarás con conflictos porque Redis esté escuchando en 127.0.0.1…

Estoy intentando configurar dos contenedores completamente independientes en un mismo host ahora mismo, y como ambos son para sitios muy pequeños, preferiría la flexibilidad de mantenerlos en contenedores independientes… Lamentablemente, Redis escuchando en 127.0.0.1 (y presumiblemente Postgres también) causará conflictos…

1 me gusta

Hola @ryanerwin

Parece que no entiendes del todo cómo funcionan los contenedores y Docker; así que déjame ayudarte.

Redis se ejecuta por defecto en el contenedor independiente en el puerto 6379:

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

74

Ahora, salgamos del contenedor y verifiquemos con netstat si Redis está escuchando:

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

Como puedes ver, Redis está escuchando en localhost dentro del contenedor; y localhost dentro del contenedor no está (no estaba) expuesto fuera de él.

Por lo tanto, si tienes muchos contenedores independientes de Discourse ejecutándose, no habrá conflictos de Redis entre ellos, ya que Redis no ha sido expuesto fuera de cada contenedor.

Por eso se llama “contenedor”… :slight_smile:

Cada socket dentro del contenedor debe exponerse explícitamente para estar disponible fuera de él.

Espero que esto ayude de alguna manera.


Ten en cuenta que los sockets de dominio Unix son muy interesantes… Solo abordé tu comentario sobre los supuestos conflictos de Redis entre contenedores independientes y no traté el tema de los sockets de dominio Unix.

5 Me gusta

Así es como pensé que funcionaría con Discourse ejecutándose dentro de Docker también; sin embargo, cuando lo ejecuté realmente, durante el proceso de inicialización vi:

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

Y había encontrado este hilo sobre “la instalación falla debido a otro contenedor de Redis”, sin embargo, el problema real era el espacio en disco insuficiente… Reorganicé algunas cosas y ahora funciona perfectamente con múltiples contenedores independientes.

2 Me gusta

Estimado @ryanerwin,

Es genial leer que has identificado el problema subyacente y ahora entiendes que Redis se ejecuta “dentro del contenedor” y no está expuesto (desde una perspectiva de E/S de socket) fuera del contenedor (tal como viene configurado por defecto).

En cuanto al uso de sockets de dominio Unix con Redis, creo que es una excelente idea. Todavía no lo he configurado, ni he leído ninguna guía sobre cómo hacerlo para Discourse, por lo que te animo a que sigas explorando esta opción.

Si tengo tiempo, investigaré más a fondo e intentaré configurar Redis para que utilice un socket de dominio Unix en Discourse en un servidor de pruebas. Mientras tanto, si puedes resolverlo y compartir tus resultados, te lo agradecería mucho. Estoy seguro de que a muchos otros también les interesa este interesante tema de Redis.

Gracias.

3 Me gusta

Hola @ryanerwin,

Espero que te alegrarás al saber que tengo Discourse funcionando con Redis mediante un socket Unix en un contenedor independiente, tal como preguntaste.

Mira la parte inferior de esta captura de pantalla de Sidekiq:

Además, aquí hay más capturas de pantalla del proceso de construcción de la aplicación:

Mis próximos pasos son:

  1. Mover el socket Unix al volumen compartido para que pueda ser accedido fuera del contenedor.
  2. Volver a probar con variables de entorno mínimas para lograr los cambios “esenciales” necesarios para que esto funcione.

Básicamente, he creado esta nueva plantilla:

-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

y he realizado pequeños cambios en el viejo amigo app.yml

Como solo empecé esto hoy, planeo probar un poco más antes de publicar los detalles.

Espero que esto sea útil.


Actualización


Funciona como se esperaba fuera del contenedor cuando el socket Unix está en un volumen compartido:

4 Me gusta

PR para esto:

Nota:

Para implementar:

  • Cambiar la plantilla de Redis en el archivo YAML del contenedor
  • Agregar una línea adicional al mismo archivo YAML del contenedor
 ## Establecer REDIS_URL y usar redis.socketed.template.yml para utilizar
 ## un socket de dominio Unix para Redis
 REDIS_URL: unix:///shared/tmp/redis.sock

Notas de implementación:

  1. Si te preocupa la seguridad de la base de datos Redis en el host, no es necesario exponer este socket Unix en el volumen compartido.

  2. Si deseas establecer los permisos del socket Unix en 770 (en lugar de 777), cambia el grupo del socket Unix a www-data.

4 Me gusta

¿Alguien más ha notado que REDIS_URL ya no tiene ningún efecto? Durante la (re)compilación y también al iniciar el contenedor, a pesar de que REDIS_URL está configurado para conectarse a través de un socket UNIX, intenta conectarse a través de redis://localhost:6379.

Por un lado, tiene sentido, ya que Discourse tiene configuraciones para host y puerto y solo las aplica: discourse/app/models/global_setting.rb at main · discourse/discourse · GitHub
Habría un parámetro path para definir una ruta de socket UNIX, que anularía el host y el puerto. Y habría un parámetro url, para definir una URL completa como unix:///shared/redis_data/redis.sock.

La gema de Redis documenta que la variable de entorno REDIS_URL anularía cualquier otra configuración, y hasta cierta versión de la gema Discourse/Redis esto funcionaba: redis-rb/lib/redis.rb at master · redis/redis-rb · GitHub

El uso del socket UNIX se rompió cuando Discourse pasó a la versión 5 de las gemas de Redis: DEV: Upgrade the Redis gem to v5.4 · discourse/discourse@2ed31fe · GitHub
Así que supongo que REDIS_URL se rompió en la gema de Redis (ya no anula otras opciones), y no por Discourse (donde el código relacionado no cambió).

Probablemente este commit lo rompió: Use redis-client as transport · redis/redis-rb@08a2100 · GitHub
¿Lo informaría en el repositorio de la gema de Redis, o alguien sabe que es un problema con la forma en que Discourse lo implementa?

La variable de entorno, por cierto, se pasa correctamente. La dejé en su lugar y solo configuré Redis para que no escuchara en el socket UNIX, y mientras unicorn se inicia sin problemas, sidekiq falla, echando de menos el socket UNIX en su lugar :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'

Encaja en el cuadro que REDIS_URL todavía funciona, pero solo si la configuración de conexión no está definida de otra manera. Mirando el rastro del error, global_setting.rb no aplica el host y el puerto para sidekiq, como lo hace para unicorn.