三年前,我在一家创业公司负责部署一个简单的Ruby应用。当时Docker正如日中天,技术博客都在吹“一次构建,到处运行”。我用了三天时间写Dockerfile,把Ruby版本、系统依赖、时区、字体库全都塞进镜像。结果第一次上生产,镜像构建成功,但容器启动时,时区对了,字体却崩了——因为某个底层库在alpine镜像里没有。那一刻我意识到,Docker没有解决环境问题,它只是把问题抬高了一层,变成了你自己的问题。
后来我逐渐发现,Docker最大的错误不是技术本身,而是它让所有人产生了一种幻觉:只要用容器,软件交付就变简单了。但实际上,当你开始处理镜像仓库权限、多阶段构建缓存失效、docker-compose与k8s配置不一致、容器内进程僵尸化、日志驱动丢失、网络插件冲突时,你会怀念一个能用systemctl restart的进程。Docker把系统管理员的经验彻底抹去,然后塞给你一堆新的、更复杂的抽象——而且是每半年一变的那种。
现在人人都谈“云原生”,但云原生不是Docker发明的,Docker只是恰好搭上了那班车。真正可怕的是,整个行业已经把Docker当成了默认的“物理机替代者”,甚至没人思考容器和虚拟机的边界在哪里。虚拟机好歹有一个独立的内核,崩溃是干净利落的;而容器共享宿主机内核,一个容器里echo 1 > /proc/sys/kernel/panic可能直接放倒整台机器。我用过的分布式系统里,容器化之后最常出现的故障不是业务逻辑错,而是“节点暂时不可用”,因为宿主机内核被某个邻居搞挂了。
更荒唐的是,Docker的生态在自我吞噬。Compose被Swarm抛弃,Swarm被Kubernetes碾压,Kubernetes又不断重造Docker的轮子(像containerd、CRI-O),而Docker Desktop对商业用户收费后,大量开发者转向Podman和Rancher。我认为这不是坏消息,反而是一种解放——Docker这个曾经的“标准”正在瓦解,你可以选择轻量级的isulad,或者干脆回到裸机跑二进制,只要你能清晰地解释你的依赖。
我今天不是劝你们放弃Docker,而是劝你们别把容器当成信仰。容器是一个工具,它有适用范围,也有它腐烂的角落。如果你要运行一个长期稳定的状态服务,虚拟机的隔离性和故障边界显然更可控;如果你只是想在CI里快速跑几个测试,那Docker依然性感。但请记住,技术栈不是赎罪券,容器化不是终局。每一次“一次构建,到处运行”的承诺,背后都藏着三张待办的运维工单。