在本地服务器上部署多个应用实例是开发者常见的操作,但有时,一些看似无害的配置可能会引发令人头疼的冲突。我最近就在 Directus 的多实例部署中,遭遇了一场“账号互相顶掉”的奇特事件。这篇文章将分享我的排查过程,希望能为遇到类似问题的你提供一些线索。
现象:账户身份的“乾坤大挪移”
我在同一台服务器上通过 Docker 部署了两个独立的 Directus 实例,分别用于不同的项目。为了区分它们,我给它们设置了不同的容器名、端口、数据库以及数据卷。一切看起来都很正常。
然而,在日常使用中,一个奇怪的现象出现了:当我同时打开两个实例的后台管理界面,并登录各自的管理员账号时,一个实例的登录状态会突然被另一个实例顶替。比如,我在端口 8256
上登录了项目 A 的账号,然后打开端口 8255
的项目 B,并登录。当我再回到项目 A 的页面时,发现我居然也变成了项目 B 的账号!
这感觉就像两个独立的系统在共享一个大脑,身份和会话信息在它们之间随意穿梭。
初步排查:从 Docker 配置入手
我的第一反应是检查 Docker 的配置,这是最容易出错的地方。我仔细对比了两个 docker run
命令,列出了所有环境变量:
- 容器名:
directus-dadi
vs.directus-qianrui
(不同,没问题) - 端口映射:
8256:8055
vs.8255:8055
(不同,没问题) - 数据卷:
/uploads
和/extensions
目录都映射到了不同的本地路径 (不同,没问题) - 数据库: 连接到同一个 PostgreSQL 服务器,但使用了不同的数据库名
wms-dadi
和directsqianrui
(不同,理论上没问题)
这里有一个重点,就是 directus 支持配置 Redis 的 ,但是服务器暂时没有太多的业务需要 Redis 支持,故而没有配置。
在仔细对比后,我发现一个关键的环境变量是相同的:
SECRET
: 两个容器都使用了同一个SECRET
密钥。
我立刻意识到这可能是问题的根源。在 Directus 中,SECRET
用于对用户会话令牌(JWT)进行签名和验证。如果两个实例使用相同的密钥,那么一个实例生成的令牌,另一个实例也能够验证。
我信心满满地修改了其中一个实例的 SECRET
值,并删除了旧容器,重新创建了新的容器。然后,我清理了浏览器缓存,再次进行测试。
但结果令人失望——问题依旧存在。
深入探索:问题的真正根源
这让我陷入了困惑。如果不是 SECRET
的问题,那还有什么可能呢?我开始在 Directus 的官方社区、GitHub 讨论和技术论坛上寻找答案。很快,我发现许多人都曾遇到类似的问题。
最终,我找到了症结所在:
罪魁祸首是浏览器会话 Cookie。
在尝试分配使用 chrome 和 edge 登录两个系统之后,发现互顶的现象没有了。
尽管我的两个 Directus 实例使用了不同的端口号(8255
和 8256
),但它们都运行在同一个 IP 地址或域名下。从浏览器的角度看,http://192.168.1.2:8255
和 http://192.168.1.2:8256
都属于同一个“域”。
根据浏览器的同源策略,document.cookie
的作用域通常由主机名、端口和协议共同决定。但在某些情况下,尤其是在处理会话 Cookie 时,浏览器可能只根据主机名来判断。这就导致了一个现象:当一个实例设置了会话 Cookie 后,另一个实例也会收到并使用这个相同的 Cookie,从而造成会话冲突。
我的 Directus 实例,虽然在服务器上是独立的,但在用户的浏览器中,它们却“共享”了会话信息。这解释了为什么即使我更换了 SECRET
,会话仍然会互相影响。
总结与最佳实践
这次排查经历让我深刻理解到,在多实例部署中,不能只关注服务端的隔离,客户端(浏览器)的隔离同样重要。
如果你也想在同一台服务器上部署多个 Directus 实例,以下是我的经验总结:
- 为每个实例设置唯一的
SECRET
。 这是最基本的安全和隔离措施,能防止会话令牌的跨实例滥用。 - 避免浏览器会话冲突。 这是解决“互相顶替”问题的关键。
- 最简单的方法: 使用不同的浏览器,或为每个实例使用独立的浏览器隐身窗口进行访问。
- 生产环境最佳实践: 配置反向代理,并为每个 Directus 实例分配一个独立的子域名(如
project-a.yourdomain.com
和project-b.yourdomain.com
)。这将从根本上解决浏览器会话的混淆问题。
- 考虑使用 Redis 缓存。 如果有条件,为你的所有 Directus 实例配置一个共享的 Redis 缓存。这不仅能提高性能,还能确保不同实例之间的缓存数据(例如,用户权限、系统配置等)始终保持同步,避免更多潜在的冲突。
通过这次排查,我不仅解决了棘手的会话冲突问题,也对 Docker、浏览器会话以及 Directus 内部机制有了更深入的理解。希望我的经历能帮助你避开这个“坑”,让你的部署之路更加顺畅。