Container Discourse com UnixSocket para Redis?

Estou curioso se alguém está usando unixsocket /var/discourse/shared/... para o Redis em seus containers Standalone. Parece que o redis-rb também suporta sockets de domínio com:

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

Aparentemente, conectar via Unix Domain Socket é cerca de 20% mais rápido do que usar sockets TCP, de acordo com este artigo no Medium

Além disso, usar Unix Domain Sockets facilitaria a execução de múltiplos containers standalone sem precisar separar seus containers de web e dados… Caso contrário, acho que você acabará com conflitos se o Redis estiver escutando em 127.0.0.1…

Estou tentando configurar dois containers totalmente standalone em uma única máquina agora, e como ambos são para sites muito pequenos, prefiro a flexibilidade de mantê-los em containers standalone… Infelizmente, o Redis escutando em 127.0.0.1 (e presumivelmente o Postgres também) causará conflitos…

1 curtida

Olá @ryanerwin

Parece que você não está entendendo completamente containers e Docker; então deixe-me ajudar.

Por padrão, o Redis roda no container independente na porta 6379:

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

74

Agora, saia do container e verifique o netstat para o Redis:

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

Então, você pode ver que o Redis está ouvindo em localhost dentro do container; e localhost dentro do container não foi (e não é) exposto para fora do container.

Portanto, se você tiver muitos containers Discourse independentes em execução, não haverá conflitos de Redis entre os containers, pois o Redis não foi exposto para fora de cada container.

É por isso que é chamado de “container”… :slight_smile:

Cada socket dentro do container deve ser explicitamente exposto para estar disponível fora do container.

Espero que isso ajude de alguma pequena forma.


Note que sockets de domínio Unix são muito legais… Abordei apenas seu comentário sobre os supostos conflitos de Redis entre containers independentes e não abordei o tópico de sockets de domínio Unix.

5 curtidas

Foi assim que imaginei que funcionaria com o Discourse rodando dentro do Docker também. No entanto, ao executá-lo, durante o processo de inicialização, 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.

E eu havia encontrado este tópico sobre “install fails because of other redis container”, mas o problema real era espaço em disco insuficiente… Reorganizei algumas coisas e agora funciona perfeitamente com múltiplos contêineres independentes.

2 curtidas

Caro @ryanerwin,

É ótimo saber que você identificou o problema subjacente e agora entende que o Redis roda “dentro do container” e não é exposto (do ponto de vista de E/S de socket) para fora do container (como configurado por padrão).

Quanto ao uso de sockets de domínio Unix com o Redis, acho que é uma excelente ideia. Ainda não configurei isso, nem li nenhum artigo sobre como fazê-lo para o Discourse, então incentivo você a continuar explorando essa opção.

Se eu tiver tempo, farei mais pesquisas sobre isso e tentarei configurar o Redis para usar um socket de domínio Unix no Discourse em um servidor de staging. Enquanto isso, se você conseguir resolver isso e compartilhar seus resultados, será muito apreciado. Tenho certeza de que muitas outras pessoas também estão interessadas nesse tópico interessante sobre o Redis.

Obrigado.

3 curtidas

Olá @ryanerwin,

Espero que fique feliz em saber que estou rodando o Discourse com Redis usando um socket Unix em um container standalone, exatamente como você perguntou.

Veja a parte inferior desta captura de tela do Sidekiq:

Além disso, aqui estão mais algumas capturas de tela da construção do aplicativo:

Meus próximos passos são:

  1. Mover o socket Unix para o volume compartilhado para que ele possa ser acessado fora do container.
  2. Retestar com variáveis de ambiente (ENV vars) mínimas para obter as alterações “essenciais” e colocar tudo para funcionar.

Basicamente, criei este novo template:

-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

e fiz pequenas alterações no velho amigo app.yml

Como só comecei isso hoje, planejo testar um pouco mais antes de publicar os detalhes.

Espero que isso seja útil.


Atualização


Funciona conforme o esperado fora do container quando o socket Unix está em um volume compartilhado:

4 curtidas

PR para isso:

Nota:

Para implementar:

  • Altere o modelo do Redis no arquivo yml do container
  • Adicione uma linha adicional ao mesmo arquivo yml do container
 ## Defina a REDIS_URL e use o redis.socketed.template.yml para usar 
 ## um socket de domínio Unix para o Redis
 REDIS_URL: unix:///shared/tmp/redis.sock

Notas de Implementação:

  1. Se estiver preocupado com a segurança do banco de dados Redis no host, não há necessidade de expor esse socket Unix no volume compartilhado.

  2. Se desejar definir as permissões do socket Unix como 770 (em vez de 777), altere o grupo do socket Unix para www-data.

4 curtidas

Alguém mais percebeu que REDIS_URL não tem mais efeito? Durante a (re)construção e também na inicialização do contêiner, apesar de REDIS_URL estar configurado para conectar via socket UNIX, ele tenta conectar via redis://localhost:6379.

Por um lado, faz sentido, já que o Discourse tem configurações para e aplica apenas host e porta: discourse/app/models/global_setting.rb at main · discourse/discourse · GitHub
Haveria um parâmetro path para definir um caminho de socket UNIX, que substituiria host e porta. E haveria um parâmetro url, para definir uma URL completa como unix:///shared/redis_data/redis.sock.

O gem Redis documenta que a variável de ambiente REDIS_URL substituiria quaisquer outras configurações, e até uma certa versão do Discourse/Redis gem isso funcionou: redis-rb/lib/redis.rb at master · redis/redis-rb · GitHub

O uso do socket UNIX quebrou quando o Discourse mudou para a versão 5 do gem Redis: DEV: Upgrade the Redis gem to v5.4 · discourse/discourse@2ed31fe · GitHub
Então, imagino que REDIS_URL quebrou no gem Redis (não substituindo mais outras opções), e não pelo Discourse (onde o código relacionado não mudou)?

Provavelmente este commit quebrou: Use redis-client as transport · redis/redis-rb@08a2100 · GitHub
Eu reportaria no repositório do gem Redis, ou alguém sabe se é um problema com a forma como o Discourse o implementa?

A variável de ambiente, aliás, é passada corretamente. Eu a deixei no lugar e apenas configurei o Redis para não escutar no socket UNIX, e enquanto o unicorn inicia bem, o sidekiq falha, sentindo falta do socket UNIX em vez disso :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'

Isso se encaixa no quadro de que REDIS_URL ainda funciona, mas apenas se as configurações de conexão não forem definidas de outra forma. Olhando o rastreamento de erro, global_setting.rb não aplica host e porta para o sidekiq, como faz para o unicorn.