39岁,我学会了在深夜的哑铃撞击声中寻找逻辑。封控第21天,凌晨一点半,客厅地板上铺着瑜伽垫,我做完最后一组哑铃划船,汗水滴在MacBook Pro的触控板旁边。屏幕上跑着docker-compose logs -f,看着nginx、flask、redis的容器日志像瀑布一样刷过去,心里那股因为物理空间被锁死而导致的焦躁,突然被一种更底层的秩序感压下去了。
这三年从带团队到重新做回独狼,最大的领悟就是“可控”。团队不可控,客户需求不可控,甚至明天的核酸时间都不可控。但代码和部署流程,只要逻辑自洽,就是绝对可控的。2020年那会儿接了个体育场馆的小程序项目,为了赶进度,三个后端在不同电脑上配环境就花了整整两天,一个依赖版本不对全组卡死。那时候就知道容器化是出路,但没时间也没决心去重构历史债务。现在好了,物理上哪儿也去不了,时间大把,债该还了。
这次的目标很简单:把我手头这个为健身教练做线上排课的工具,从一台阿里云轻量应用服务器,做到能随时横向扩展到三台不同地域的VPS上,并且整个过程不能超过十分钟。听起来像运维的活,但产品经理如果不懂交付的最后一百米,所有的PRD都是纸上谈兵。第一步是Dockerfile,以前总图省事用python:3.9-slim当基础镜像,跑起来才发现400多MB,传输和启动都是负担。这次换了alpine版本,把pip install和项目文件拷贝分开层,利用缓存,最后镜像压到120MB。这就像健身,你得知道哪些是核心肌群必须加载,哪些脂肪和冗余动作可以砍掉。
编排才是精髓。docker-compose.yml里,我把web服务、celery worker、celery beat、redis、postgres全拆成独立service,用自定义网络连通。最头疼的是数据库的数据持久化和跨主机通信。一开始用volume挂载,发现迁移时volume也得跟着走,麻烦。后来改成在compose文件里声明一个专门的dbdata volume,然后在另一台机器上,先用scp把数据库备份传过去,再启动时自动还原。虽然牺牲了点实时性,但对于排课这种低频更新业务,够用了。
真正的考验在横向扩展。我写了个deploy.sh脚本,核心就三块:rsync同步项目目录(排除掉venv和__pycache__),scp传输环境变量文件和docker-compose.yml,然后ssh过去执行docker-compose up -d。听起来简单,但细节吃人。比如不同VPS的防火墙规则,有的默认关掉了2375端口,得先开;比如ssh密钥登录的配置,一台台处理;再比如docker-compose down的时候,如果不用-v参数,旧volume会残留,导致新容器启动失败。这些坑,文档里不会写,都是一个一个深夜试出来的。
哑铃撞击地板的声音是有节奏的,就像容器启动时那些状态检查(healthcheck)的间隔。一组动作做12次,中间休息45秒,和容器从starting到healthy的探测周期,在本质上都是对系统稳定性的压力测试。当我在这台机器上执行docker-compose up,看着所有容器状态变绿,然后立刻在另一台机器上重复这个过程,三分钟后两个完全隔离的环境跑着同一套业务——那种感觉,比当年第一次跑通微信支付回调还要爽。这不是技术炫技,这是一种在失控世界里重新夺回掌控权的物理证据。
产品经理的焦虑永远不会消失,只是转移了。以前焦虑流量和转化,现在焦虑交付效率和系统弹性。但至少,当我把这套部署流程固化下来之后,下次再遇到突发需求或者流量波动,我可以在喝一杯水的功夫里,让整个服务在另一片大陆上站起来。这大概就是39岁还能在互联网里混下去的底气:你得有一个在任何环境下都能快速构建秩序的工具箱。而我的工具箱里,现在除了Axure和Python,又多了一对哑铃和一套Docker命令。














