Docker 运维入门实战:多副本下,状态和数据该放哪
关键词:无状态、状态外置、会话、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 走一遍最简集群,看看跨机器之后,服务名发现和调度是怎么接管的。
- 点赞
- 收藏
- 关注作者
评论(0)