Docker 运维入门实战:多副本下,状态和数据该放哪

举报
yd_274623194 发表于 2026/11/03 09:21:29 2026/11/03
【摘要】 关键词:无状态、状态外置、会话、Redis、共享存储、多副本上一篇把 api 扩到了 3 个副本,网关轮着把请求分给它们,流量分摊开了。但结尾留了一个坑没展开:副本一多,如果每个副本都在自己本地记东西,一个请求这次落在副本 A、下次落在副本 B,数据就对不上了。今天就把这个坑填上:多副本时,状态、会话和数据到底该放哪。先把问题说具体。单副本时一切都存在本机:用户登录后,会话记在进程内存里;用...

关键词:无状态、状态外置、会话、Redis、共享存储、多副本

上一篇把 api 扩到了 3 个副本,网关轮着把请求分给它们,流量分摊开了。但结尾留了一个坑没展开:副本一多,如果每个副本都在自己本地记东西,一个请求这次落在副本 A、下次落在副本 B,数据就对不上了。今天就把这个坑填上:多副本时,状态、会话和数据到底该放哪。

先把问题说具体。单副本时一切都存在本机:用户登录后,会话记在进程内存里;用户上传的文件,存在容器本地目录里;临时计数、验证码,也都在本地。这些在单副本时都"能用",但副本一多就全露馅。

一、问题长什么样:三个对不上的现场

  • 登录态丢了:用户在副本 A 登录成功,会话存在 A 的内存里;下一个请求被轮询到副本 B,B 没这份会话,用户被当成没登录,又被踢回登录页。刷新几次,时好时坏,最难排查的就是这种。
  • 上传的文件找不到:文件传到了副本 A 的本地目录,下载请求落到副本 B,B 的目录里没有这个文件,返回 404。
  • 计数对不上:每个副本各自在内存里数"今天第几个请求",三个副本三个数,谁也对不上谁。

三个现场的共同点是:状态被存在了副本本地,而请求却在副本之间流动。解决办法也就一句话:让副本本身不存状态,把状态挪到所有副本都能访问的同一个地方。

二、先分清:哪些是状态,哪些不是

动手之前先分家。一份服务里通常有三类东西:

  • 代码和配置:程序本身、配置文件、环境变量。这类东西每个副本一份、内容相同,天生适合跟着副本走,改代码走的还是前面讲的 CI/CD 流水线。
  • 会话和临时数据:登录会话、验证码、限流计数。特点是量不大、变得快、丢了用户要重新操作。
  • 业务数据和文件:数据库里的订单、用户,用户上传的图片、附件。特点是量会长大、丢了就是事故。

分清之后原则就清楚了:第一类留在副本里,第二、三类全部外置。副本只负责算,不负责记;要记的东西,统一记到副本外面那个唯一的地方去。

三、会话外置:放进 Redis,谁接到请求都能认人

会话是最常见的第一步。做法是给系统加一个 Redis 服务,专门存会话:

services:
  api:
    # 原有配置不变,副本数还是 3
  redis:
    image: redis:7
    volumes:
      - redis-data:/data

登录成功后,服务端不再把会话记在自己内存里,而是生成一个会话 ID,把会话内容写进 Redis,只把会话 ID 通过 Cookie 发给浏览器。之后任何副本收到请求,拿着会话 ID 去 Redis 里查,查到就认人。这样请求落到哪个副本都一样,用户再也不会被随机踢下线。

临时计数、验证码同理:原来放内存的计数器改成在 Redis 里加一减一,三个副本数的是同一个数。注意 Redis 本身只跑一份,它是"记东西的地方",不是被复制的对象;它的数据目录照样要用数据卷持久化,这一点和前面数据卷那篇讲的是一回事。

四、文件外置:别存副本本地,存到大家够得着的地方

上传文件的原则同样是"只存一份,存到副本外面":

  • 练习和单机阶段:把上传目录做成所有副本共享的同一个数据卷,三个副本挂载同一个卷,写到哪、读到哪都是同一批文件。
  • 认真做的时候:用对象存储。文件不落在任何一台机器上,副本只负责把文件传上去、把访问地址记进数据库。副本换机器、扩缩容,文件都不受影响。

判断标准很简单:如果一份文件只有"写它的那个副本"能读到,它就放错地方了。

五、数据外置:数据库独立成一份,副本只负责连

业务数据同理,而且更严格:数据库只跑一份(比如一个 MySQL 或 PostgreSQL 容器,数据在它自己的数据卷里),3 个 api 副本都连它。副本里不存任何业务数据,只是"拿着请求去数据库查和写"的手。

这样副本就变成了纯粹的计算单元:随时可以加一个、删一个、换一个,数据纹丝不动。这就是常说的"无状态服务"——不是服务没有状态,而是状态不在服务本地,全在它身后的 Redis、文件存储和数据库里。

六、三个新手最容易踩的坑

1. 把存状态的东西也复制三份
看到副本有用,就把 Redis、数据库也起 3 个,结果三份数据各记各的,比原来更乱。存状态的组件要的是"一份且可靠",不是"多份且热闹"(它们自己的高可用是另一个话题,先别混进来)。

2. 只挪了一半
会话挪进了 Redis,但上传文件还存在本地;或者数据进了数据库,验证码还在内存里。排查时"大部分时候正常、偶尔出错"最折磨人,因为剩下的那一半只在请求换副本时才发作。挪就一次挪干净:会话、计数、文件、数据,逐项过一遍清单。

3. 把缓存当成了数据源
Redis 里顺手也放了缓存,有人就开始把重要数据只写缓存不落库。缓存可以丢、可以过期,真正的数据必须落在数据库或文件存储里。分工要清楚:Redis 管会话和临时数据,数据库管正经业务数据。

结语

上一篇解决的是"副本多了,请求发给谁";今天解决的是"副本多了,东西记在哪"。答案收成一句:副本只算不记,状态集中外置——会话和临时数据进 Redis,文件进共享存储或对象存储,业务数据进独立的数据库。副本无状态了,才能随加随减。下一步副本还都在一台机器上,下一篇把它们真正分散到两台机器:用 Docker Swarm 走一遍最简集群,看看跨机器之后,服务名发现和调度是怎么接管的。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。