2026年了,小项目还用Docker Compose?聊聊我的真实选型经历

📅 发布时间:2026/8/1 21:55:58
2026年了,小项目还用Docker Compose?聊聊我的真实选型经历
很多团队在选型上花了太多时间或者根本没想清楚就上了 k8s。我见过一个 4 人的创业团队产品还没跑通 PMF先花两周搭了一套 EKS 集群配了 cert-manager、external-dns、ArgoCD、Prometheus 全家桶。结果三个月后他们最大的运维开销不是业务而是维护这套标准答案控制平面升级卡了一天CoreDNS 解析偶发超时查了三天cert-manager 的证书续期在凌晨静默失败导致官网 HTTPS 挂了两小时。也见过另一个团队服务已经稳定扛着 8000 QPS还在用 docker-compose 手动 restart每次发布要人盯着数据库主从切换全靠手敲命令一次误删 volume 直接丢了半天的订单。两个都是错的但错得不一样前者是提前透支了不该付的复杂度后者是到了该升级的时候还停在舒适区。两种极端都源于同一个问题没有用数字和场景去定义我到底需要什么。这篇文章不谈情怀只谈我自己踩过的坑和算过的账。一、先说清楚本质区别很多人把 Compose 和 k8s 当作同一个东西的两个版本这是选型错误最常见的根源。它们根本不是一类工具连解决的问题域都不同。Docker Compose 是一个单机编排器。它的世界模型是一台机器上面跑一组互相连得通的容器。它的核心抽象是 service一组同构容器、网络一个 bridge、volume一块持久化目录。它假设你只有一台宿主机所有副本都在这台机器上水平复制。没有跨节点调度没有自愈没有分布式存储。它做的事情本质上是一句话docker run的批处理版本——按依赖顺序把容器拉起来连到同一个网桥挂上卷。Kubernetes 是一个分布式系统平台。它的世界模型是一个由 N 台机器组成的资源池上面跑着被抽象成工作负载的东西。它的核心抽象是 Pod调度最小单元、Deployment声明式期望状态、Service稳定的网络端点、etcd事实唯一来源。k8s 的控制器会持续 reconcile你声明我要 3 个副本它就去保证任何时候都有 3 个活着挂了一个立刻在集群里任意一台节点上补一个。中间的调度、绑定、健康检查、重启全部由控制循环自动完成你不用管那个副本到底落在哪台机器上。这里有一个容易被忽略的关键差异声明式 vs 命令式。Compose 的docker compose up是一次性的动作它读配置、跑命令、然后就撒手了。容器之后挂了没有后台进程负责把它拉回来除非你配了restart: always但那只是 Docker daemon 级别的简单重启不涉及跨节点、不涉及扩容。k8s 的 API 是声明式的你提交的是期望状态一个常驻的 controller 循环不断把实际状态往期望状态收敛。这才是它强悍的地方也是它复杂的地方——你得维护一套永远在后台运行的分布式状态机而这套状态机本身就是要被运维的对象。一句话总结区别Compose 解决的是我怎么在一台机器上把一堆容器方便地跑起来。k8s 解决的是我怎么在一个机器集群上让一堆分布式应用自己维持住该有的样子。这两个问题难度差了一个数量级。Compose 的复杂度是 O(应用数)k8s 的复杂度是 O(集群规模 × 应用数 × 抽象层数)。选错了要么你用牛刀杀鸡k8s 管 3 个容器控制平面比业务还重要么你拿螺丝刀当锤子Compose 硬扛高可用单机挂了全站挂。二、场景化选型用数字说话下面是我在实际项目里用的判断门槛。不是精确科学但比感觉需要靠谱得多。先看几个最关键的维度。团队规模1–3 人含你本人默认 Compose。你没精力做 k8s 的运维也用不满它的能力。4–8 人分水岭。如果其中没有一个人能全职或半全职负责基础设施仍然建议 Compose 一台还不错的机器。这个阶段最危险的不是技术是没人背锅。8 人以上且有专门的基础设施 / 平台角色认真考虑 k8s否则人工发布会成为吞吐瓶颈。流量与可用性峰值 QPS 2000且允许机器重启期间服务短暂不可用分钟级Compose 足够。单机挂掉的概率本来就比你的代码 bug 低一个数量级。需要 99.9% 以上可用性、且要求跨可用区容灾必须 k8s或云厂商的等效托管方案。Compose 在单机模型下物理机故障 全站故障这是架构层面的硬伤。有突发流量如活动、抢购、晚高峰需要分钟级弹性扩容k8s 的 HPA 是刚需。Compose 的replicas是静态的扩缩容要人改配置重启。部署频率一天几次手动发布、能接受短暂停机窗口Compose。每天十几次、要求零停机滚动发布、需要灰度 / 金丝雀 / 快速回滚k8s 的滚动更新、Argo Rollouts、Flagger 是成熟的。人工盯发布在高频下会出事。服务数量≤ 10 个服务、单机资源够Compose。网桥内 service name 互访心智成本几乎为零。30 个微服务、需要服务发现、配置中心、多租户隔离k8s 的命名空间、Service、ConfigMap、NetworkPolicy 能省你大量手写胶水代码。服务多了之后Compose 那张平铺的网桥会很难管。有状态负载这点很多人忽略单个数据库实例Postgres/MySQL/RedisCompose 往往是更优解哪怕你的业务已经不小。StatefulSet PVC StorageClass 在 k8s 里管一个单主的数据库复杂度远高于直接在机器上跑一个容器挂卷。k8s 擅长的是无状态服务编排不是替你管数据库。数据库的 RDS / 云托管版通常比塞进 k8s 更稳。需要多副本有状态如 Kafka、Elasticsearch、TiDBk8s 的 Operator 模式如 Strimzi、ECK才是真正发挥价值的地方Compose 完全不够用。一个快速自测表维度选 Compose选 k8s团队 8 人且无专职运维≥ 8 人有平台团队峰值 QPS 2000 5000 或波动剧烈可用性可接受分钟级中断要求 99.9% 跨 AZ部署手动、低频高频、需零停机服务数≤ 10 30有状态单库直接挂卷多副本有状态集群如果六项里有三项以上落在 k8s 列你大概率该认真评估迁移了。如果大部分在 Compose 列硬上 k8s 就是给自己找罪受。三、Compose 的真实优势它比 k8s 更好的那些场景说 Compose 适合小项目这是废话。我要说的是在很多正经场景里Compose 不是将就而是更优解。下面每一条都是我在生产里反复验证过的。第一它几乎零运维。没有 API Server、etcd、scheduler、kubelet 需要你盯着。一个docker compose up -d就能跑docker compose logs -f就能看全部日志docker compose ps一眼看全状态。k8s 里你要先搞清楚 kubelet 为什么 NotReady、etcd 磁盘慢导致 leader 频繁选举、CoreDNS 解析失败怎么排查、某节点被 taint 了 Pod 为啥调度不上去。这些对于 3 个人的团队是纯消耗不产生任何业务价值。第二调试心智负担低一个量级。容器挂了docker exec -it进去看网络不通docker network inspect一眼看穿数据要备份直接对 volume 目录tar打包。在 k8s 里同样的事要过 Pod → Endpoint → Service → kube-proxyiptables/ipvs→ CNI 好几层新手很容易卡在一句Connection refused上找不到是哪层丢的包。我带过的新人平均要在 k8s 网络排错上卡一周才入门。第三网络模型简单到没有惊喜。Compose 用默认的 bridge 网络所有 service 在同一个用户态网桥上靠内嵌的 DNS基于容器名 / service 名解析互通没有 overlay、没有 CNI 插件、没有 kube-proxy 重写 iptables。你知道流量走的每一跳。k8s 的 Service 是虚拟 IP iptables/ipvs 规则overlay 网络还有封装开销VXLAN 之类跨节点通信的 MTU、MTU mismatch、CNI 插件 bug都是真实的坑。第四资源开销可控且确定。一套最小可用的高可用 k8s3 个 master 2 个 worker至少需要 5 台机器、10 vCPU、20GB 内存其中控制平面就要吃掉 1–2 vCPU 和几 GB 内存做什么都不干的纯管理开销。Compose 只需要一台恰好够用的 VM资源的每一分都花在业务上。对预算敏感的小团队这差价直接是每月几百到上千块。第五确定性没有后台控制器替你做主。Compose 是所见即所得你写 2 个副本它就在这台机器上起 2 个你没配健康检查它就不检查你没配 limit它就让容器想吃多少吃多少。没有 operator 突然把你的配置改了没有 mutating webhook 静默注入 sidecar。这种可预测性对内部工具、定时任务、批处理作业反而是一种优点——你确切知道它此刻在干什么。第六它是 k8s 的完美开发镜像。很多团队忽略的一点Compose 最适合的不是生产而是本地和 CI。你完全可以在生产跑 k8s在本地和流水线用同一个 Compose 文件拉起一整套依赖db、redis、mq保证环境一致性。Compose 和 k8s 不是非此即彼可以各管一段本地 / CI 用 Compose生产用 k8s。这也是 Google 很多团队的做法。具体场景举例这些地方 Compose 比 k8s 更合适不是因为小而是因为不需要分布式内部 BI 报表系统、运营后台QPS 个位数几个人用定时跑的 ETL / 数据同步脚本、批处理作业给客户演示用的 Demo 环境、售前 POC个人 SaaS 的早期版本验证期形态每月都在变CI 里的集成测试依赖栈一次性拉起、跑完销毁单数据库实例直接挂卷比塞进 StatefulSet 省心四、k8s 对小团队的真实成本小团队上 k8s 之前最常低估的是下面几项。我按最容易被低估排序。学习曲线。k8s 不是装好就能用。你得理解 Pod 生命周期、三类探针liveness/readiness/startup的差别和误配后果、调度亲和性与污点、资源 request/limit这个没配好节点要么撑死要么被饿死limit 没配还可能被 OOMKill 连环炸、Service 与 Ingress 的区别、ConfigMap 与 Secret、RBAC、PV/PVC 与 StorageClass、HPA、NetworkPolicy。这还只是核心概念不算 Helm、CRD、Operator、服务网格这些进阶。一个新人在生产环境不捅娄子地用好 k8s保守估计要 1–3 个月沉浸。运维负担Day 2 才是开始。控制平面要升级k8s 每 3 个月一个版本支持窗口约 14 个月云厂商会强制你跟版本etcd 要定期备份且要会恢复etcd 对磁盘延迟极敏感慢盘会导致 leader 选举抖动甚至集群脑裂节点要打安全补丁和滚动重启Ingress Controller、cert-manager、CNI 插件要维护。云托管的托管版EKS/GKE/AKS帮你管控制平面但节点和上面的工作负载还是你的。一旦集群出问题比如某个节点 NotReady 导致调度失败你要有能力在半夜定位。隐性人力成本。有 k8s 的团队要么养一个平台工程师要么让后端兼任。后者的问题是他本来该写业务的现在一半精力在救火、在写 Helm chart、在理准入策略。对小团队这其实是最大的隐性税——你以为省了运维外包的钱实际是把最贵的人力绑在了基础设施上。金钱成本。以 AWS EKS 为例控制平面每月 73 美元起加上至少 2–3 台 worker 节点按需或 Spot、负载均衡器、NAT 网关、出网流量一个最小 k8s 生产环境每月轻松 200–500 美元起还没算人的成本。Compose 在一台 4C8G 的 VM约 30–50 美元 / 月上就能跑得很好。把一个早期项目硬塞进 k8s等于每月固定烧掉一台开发机的预算。过度工程风险。k8s 生态太丰富很容易为了用而用。我见过团队给 5 个服务上了 Istio 做服务网格结果 80% 的流量都在集群内部mTLS 和流量治理带来的复杂度远超收益也见过给 3 个容器配了 ArgoCD GitOps结果每次改个环境变量要走 PR → 审核 → 同步发布效率反而下降。技术选型要跟着问题走不是跟着社区热度走。安全模型的隐性复杂度。k8s 的 RBAC 强大但容易配错Pod 默认能以宿主机网络 / 权限跑得额外上 Pod Security Standards、准入控制器、镜像漏洞扫描。Compose 里这些基本不是问题因为攻击面就一台机器网络也只在内部。五、迁移路径已经在 Compose 上怎么评估是否该迁移我的建议是先问自己三个问题只有都答是才值得迁移。是否真的需要多节点单机资源已经打满或需要跨 AZ 容灾是否真的需要 k8s 独有的能力自动扩缩容、零停机发布、多租户隔离团队里是否有人愿意长期承担 k8s 的运维三个里有两个答否就别迁先把 Compose 用好。迁移的代价不是写几个 yaml而是你从此要养一套分布式系统。如果决定要迁平滑路径如下。第一步用 kompose 自动转换但别信它的产物。kompose 可以把 docker-compose.yml 直接转成 k8s 的 Deployment/Service 清单作为起点。但有一堆它处理不好的地方你必须有心理准备depends_on在 k8s 里没有原生等价物——它直接丢掉了你得用探针和 init 容器自己补启动顺序。它不会生成 readiness/liveness 探针也不会生成 resource request/limit更不会生成 HPA 和 Ingress通常退化成 NodePort 或 LoadBalancer。Secret 里的敏感值会被明文写进生成的清单。也就是说kompose 的输出只是骨架离生产还差 probes limits Ingress secrets 管理这一整套。第二步用轻量 k8s 降低门槛。生产不一定非要 EKS 那种全量发行版。k3s 是单个二进制、内存占用极低的 k8s 发行版默认用 sqlite可选 etcd一台 2C4G 的机器就能跑控制平面加 worker非常适合从 Compose 过渡期。或者直接用云厂商托管版把控制平面运维甩出去你只管节点和工作负载。第三步用 Helm 管理应用。把转换后的清单打包成 Helm chart版本化、参数化发布用helm upgrade回滚用helm rollback。这一步把一堆散 yaml变成一个可管理的应用包。注意 Helm 的模板能力很强也很容易写成意大利面复杂的if/range嵌套后面会很难维护保持 chart 扁平。第四步补齐生产三件套否则别上线。至少要有就绪 / 存活探针否则流量会打进还没起好的 Pod或挂了的 Pod 不被摘流资源 request/limit否则节点会被某一个服务拖垮或触发连环 OOMKillIngress TLS否则裸 Service 没法对外证书还得另管日志 / 监控Prometheus Grafana Loki Alertmanager 是最小集否则线上黑盒。第五步重想有状态数据。这是迁移最容易翻车的地方。数据库别急着搬进 k8s——优先用云托管 RDS或至少在 k8s 外用独立机器跑、用 PVC 持久化并定期备份。Secret 管理用 sealed-secrets / external-secrets / Vault别把密码 commit 进 Git。替代方案Nomad。如果你想要 k8s 的调度能力但又嫌它重HashiCorp Nomad 值得认真看。单个二进制、学习曲线平缓得多、对异构工作负载容器、裸二进制、Java jar、批处理更友好调度器和状态存储都很轻。缺点是国内生态和中文资料少招聘也难。OpenShift则适合强合规、强企业管控的场景内置 SCC、镜像扫描、流水线但更重、更贵、更封闭小团队基本不考虑。六、代码示例同一个应用两种写法下面用同一个典型 Web 应用Nginx 后端 API Postgres Redis对比两种写法的体量差异。代码本身最有说服力。Docker Compose 版本一个文件搞定version:3.9services:nginx:image:nginx:1.27ports:-80:80depends_on:-apivolumes:-./nginx.conf:/etc/nginx/nginx.conf:roapi:build:./apienvironment:DB_HOST:postgresREDIS_HOST:redisDB_PASSWORD:${DB_PASSWORD}depends_on:postgres:condition:service_healthyredis:condition:service_starteddeploy:replicas:2restart_policy:condition:on-failurepostgres:image:postgres:16environment:POSTGRES_PASSWORD:${DB_PASSWORD}volumes:-pgdata:/var/lib/postgresql/datahealthcheck:test:[CMD-SHELL,pg_isready -U postgres]interval:10stimeout:5sretries:5redis:image:redis:7volumes:-redisdata:/datavolumes:pgdata:redisdata:一条命令docker compose up -d全部跑起来包括健康检查、依赖顺序、2 个 api 副本、数据持久化。整个声明约 40 行。等效的 k8s 版本需要至少拆成多个清单这里只列核心部分实际还要 Ingress、Secret、PVC、资源限制、PodDisruptionBudget 等# api-deployment.yamlapiVersion:apps/v1kind:Deploymentmetadata:name:apispec:replicas:2selector:matchLabels:app:apitemplate:metadata:labels:app:apispec:containers:-name:apiimage:registry.example.com/api:1.0.0env:-name:DB_HOSTvalue:postgres-name:REDIS_HOSTvalue:redis-name:DB_PASSWORDvalueFrom:secretKeyRef:name:db-secretkey:passwordports:-containerPort:8080readinessProbe:httpGet:path:/healthport:8080initialDelaySeconds:10periodSeconds:5livenessProbe:httpGet:path:/healthport:8080initialDelaySeconds:30periodSeconds:10resources:requests:cpu:250mmemory:256Milimits:cpu:500mmemory:512Mi---# api-service.yamlapiVersion:v1kind:Servicemetadata:name:apispec:selector:app:apiports:-port:80targetPort:8080---# postgres-statefulset.yaml需配合 PVC、headless Service、StorageClass# redis-deployment.yaml# nginx-deployment.yaml nginx-service.yaml# ingress.yaml对外暴露配 TLS# db-secret.yaml敏感值不能明文# 若干 PVC 定义 PodDisruptionBudget注意两点差异k8s 版光等效于 Compose 那一个文件就要拆成 7–8 个清单而且必须显式声明探针和资源限制否则生产跑不稳。Compose 帮你隐藏的假设重启策略、依赖顺序、健康检查在 k8s 里全部要你显式写出来。Compose 的depends_on带condition: service_healthy在 k8s 里没有原生等价物k8s 不保证 Pod 启动顺序你得用 readiness 探针让未就绪的 Pod 不接流量用 init 容器处理强依赖的初始化。这是表达能力强的代价也是它被诟病重的根源。如果你数一下Compose 版约 40 行解决的事k8s 版骨架就要 150 行还只是能跑离生产就绪还差监控、备份、网络策略、扩缩容策略。这多出来的每一行都是你未来要维护、要理解、要排错的东西。七、作者观点什么情况下我一定选 k8s什么情况下我坚决不选把我自己的判断标准摊开讲不绕弯子。我会毫不犹豫上 k8s 的情况团队 ≥ 8 人且有一个人能长期不是临时负责基础设施服务的可用性要求 99.95%必须跨可用区部署单机故障不可接受流量波动剧烈需要 HPA 做秒级到分钟级的弹性伸缩比如电商大促、在线教育晚高峰、突发营销每天发布十几次且要求零停机、需要灰度发布和秒级回滚微服务超过 30 个需要统一的服务发现、配置管理、多环境隔离有真正的多副本有状态集群Kafka / ES / TiDB需要用 Operator 托管。我会坚决不上 k8s 的情况团队 ≤ 3 人或者没有专职运维谁都背不动集群的锅单机资源一台 8C16G 的 VM就能扛住全部流量峰值 QPS 2000业务是内部工具、定时任务、批量处理、Demo / PoC、个人项目早期预算紧张每月不愿意为控制平面和节点多花 200 美元以上产品还在验证期技术栈和部署形态每个月都在变此时灵活比规范重要。最后落到一句具体的判断也是这篇文章的结论对于 1–3 人、单机可承载、发布低频、可用性要求不极端、且主要是无状态或单库的早期项目我选 Docker Compose把省下来的时间和钱全投到产品本身对于 8 人以上、需要跨节点高可用、弹性伸缩和零停机发布的成熟业务我选 Kubernetes并用托管版把控制平面的苦活外包出去同时把数据库交给云托管 RDS 而不是塞进集群。中间那段 4–8 人的灰区答案只有一个看你们组里有没有人真心愿意长期背这个运维的锅——有就上没有就先别动先把 Compose 用到极致。补充两个最常见的误判都是我或身边人真实交过学费的。误判一「k8s 上云托管版就省心了不用管。」错。托管版只接管控制平面节点、工作负载、网络策略、存储、证书、扩缩容策略全还是你的。EKS 不会因为你用了它就自动给你配好探针和资源限制Pod 该 OOM 还是 OOM。托管只是把最底层那点苦活外包了中层的复杂度一文不少。误判二「Compose 跑久了迟早要迁 k8s不如一开始就上。」这等于为还没发生的规模提前付运维税。大多数项目的真实瓶颈从来不是编排工具而是需求不清晰、架构不合理、人不够。在 2000 QPS 以下、3 人以内的阶段Compose 不会成为你的天花板你的产品和团队才是。等真的撞到 Compose 的能力边界单机打满、需要跨 AZ、需要弹性那时候再迁成本和风险都可控而且你已经清楚自己到底要 k8s 的哪部分能力不会为了「标准架构」而迁。技术选型没有标准答案但一定有更适合你此刻数字区间的那一个。先想清楚自己处在哪个数字区间再决定要不要在集群里折腾。Compose 不是「低配版 k8s」k8s 也不是「高级版 Compose」——它们是解决两类不同问题、在各自领地都足够好的工具。承认这一点你就已经避开了绝大多数选型坑。