带有 UnixSocket 的 Redis 的 Discourse 容器?

想问问是否有人在自己的独立容器中使用 unixsocket /var/discourse/shared/... 来连接 Redis?看起来 redis-rb 也支持通过域套接字连接,用法如下:

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

根据 这篇 Medium 文章 的说法,通过 Unix 域套接字连接比使用 TCP 套接字快约 20%……

此外,使用 Unix 域套接字会让在同一主机上运行多个独立容器变得更加容易,而无需将 Web 容器与数据容器分离。否则,你可能会遇到 Redis 监听 127.0.0.1 导致的冲突……

我目前正尝试在一台主机上部署两个完全独立的容器,由于它们都用于非常小的站点,我更希望保持它们在独立容器中运行的灵活性……但不幸的是,如果 Redis(可能还有 Postgres)监听 127.0.0.1,将会产生冲突。

1 个赞

你好 @ryanerwin

看来你对容器和 Docker 的理解还不够深入,让我来帮你解答一下。

Redis 默认在独立容器中运行,监听端口为 6379:

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

74

现在退出容器并检查 Redis 的 netstat 状态:

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

你可以看到,Redis 在容器内部监听的是 localhost;而容器内的 localhost 并未(也从未)被暴露到容器外部。

因此,如果你运行多个独立的 Discourse 容器,容器之间不会出现 Redis 冲突,因为 Redis 并未被暴露到每个容器之外。

这就是为什么它被称为“容器”…… :slight_smile:

容器内的每个套接字都必须显式暴露,才能在容器外部可用。

希望这对大家有一点帮助。


注意,Unix 域套接字非常酷……我只是针对你关于独立容器之间感知到的 Redis 冲突的评论进行了回应,并未涉及 Unix 域套接字这个话题。

5 个赞

我原本以为在 Docker 中运行 Discourse 也会是这样,但实际运行时,在引导过程中我看到了:

INFO -- : > cd /var/www/discourse && git reset --hard
# oO0OoO0OoO0Oo Redis 正在启动 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 容器”的帖子,但真正的问题其实是磁盘空间不足……重新整理了一些内容后,多个独立容器确实可以正常运行。

2 个赞

亲爱的 @ryanerwin

很高兴得知您已找到根本问题,并理解 Redis 是“在容器内”运行的,从套接字 I/O 的角度来看,它并未暴露在容器外部(按开箱即用的默认配置)。

关于使用 Unix 域套接字与 Redis 配合,我认为这是个很好的想法。我尚未进行过此类设置,也未查阅过任何关于如何在 Discourse 中配置此功能的文档,因此我鼓励您继续探索这一方案。

如果时间允许,我将进一步研究,并尝试在测试服务器上配置 Discourse 使用 Unix 域套接字连接 Redis。在此期间,如果您能成功实现并分享结果,我们将不胜感激。我相信许多其他人也对这个有趣的 Redis 话题感兴趣。

谢谢。

3 个赞

你好 @ryanerwin

很高兴告诉你,我已经在独立容器中成功运行了 Discourse,并使用了 Unix 套接字连接 Redis,正如你所询问的那样。

请查看下方来自 Sidekiq 的截图底部:

此外,这里还有一些构建应用时的其他截图:

我接下来的计划是:

  1. 将 Unix 套接字移动到共享卷,以便容器外部也能访问。
  2. 使用最少的环境变量重新测试,以获取“最简”配置,使系统正常运行。

基本上,我已经创建了这个新模板:

-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 套接字位于共享卷上时,在容器外部也能按预期工作:

4 个赞

相关 PR:

注意:

实施步骤:

  • 修改容器 yml 文件中的 redis 模板
  • 在同一容器 yml 文件中添加一行
 ## 设置 REDIS_URL 并使用 redis.socketed.template.yml 以使用
 ## Redis 的 Unix 域套接字
 REDIS_URL: unix:///shared/tmp/redis.sock

实施说明:

  1. 如果担心主机上 Redis 数据库的安全性,无需在共享卷中暴露此 Unix 套接字。

  2. 如果希望将 Unix 套接字的权限设置为 770(而非 777),请将 Unix 套接字的组更改为 www-data。

4 个赞

有没有其他人注意到 REDIS_URL 不再起作用了?在(重新)构建和容器启动时,尽管 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 gem 文档指出 REDIS_URL 环境变量将覆盖任何其他设置,并且直到某个 Discourse/Redis gem 版本之前,这都是有效的:redis-rb/lib/redis.rb at master · redis/redis-rb · GitHub

当 Discourse 迁移到 Redis gem 版本 5 时,UNIX 套接字使用中断了:DEV: Upgrade the Redis gem to v5.4 · discourse/discourse@2ed31fe · GitHub
所以我猜 REDIS_URL 是在 Redis gem 中中断的(不再覆盖其他选项),而不是 Discourse(相关的代码没有改变)?

很可能是这个提交中断了它:Use redis-client as transport · redis/redis-rb@08a2100 · GitHub
我会在 Redis gem 仓库报告这个问题,或者有人知道这是 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 没有像 unicorn 那样为主机和端口应用 sidekiq。