1. Compose 跑得好好的,为什么要折腾 k8s
1.1 一个真实的“被迫升级”场景
我最早接触 docker compose 的时候,心里想的是“终于不用在一台服务器上手动敲一串 docker run 了”。那时候公司项目还不大,一台 4核8G 的机器,nginx + php-fpm + mysql + redis 全部用 compose 编排,一条 docker compose up -d 全起来,日志、网络、重启策略都安排得明明白白。说实话,对中小项目来说,docker compose 的体验是真好,它把“多容器协作”这件原本很琐碎的事情,压缩成了一两个命令。
但后来事情变了。某个活动周期流量上来之后,单机部署的瓶颈就暴露了:一台机器扛不住,想要横向扩容,compose 基本帮不上忙。你当然可以在另一台服务器上再起一套 compose,但负载均衡、服务发现、滚动更新、故障自愈这些事全都得自己搞定。此时你会看到团队里开始有人提 k8s,也有人在争论“docker compose 升级到 k8s”是不是过度设计。
这篇文章我不打算写成 k8s 教程,也不打算劝你无脑迁移。我想从一个实际使用者的角度,把这两个东西的底层逻辑、适用边界、迁移路径和踩坑过程讲清楚。无论你是在纠结要不要学 k8s,还是正在准备把一套 compose 项目搬到 k8s,这篇文章应该都能给你一些实在的参考。
1.2 先搞清楚 docker compose 是干什么的
热词里有“docker compose 是干什么的”,这个问题其实问得挺核心。简单来说,docker compose 是一个容器编排工具,但它编排的范围是“单台主机”。你在 docker-compose.yml 里定义几个 service,每个 service 用什么镜像、暴露哪些端口、挂载哪些卷、依赖哪个服务,然后 compose 帮你在同一台机器上把这些容器拉起来,并且管理它们的网络、生命周期和日志。
它解决的核心痛点是“多容器应用的管理”。比如一个 LNMP 项目,有 nginx、php-fpm、mysql、redis 四个容器,手工 docker run 四次会非常痛苦,尤其是网络配置、环境变量、重启策略这些细节,手工敲容易漏。compose 用声明式配置解决这个问题,而且 docker compose up 之后,如果容器挂了,restart: unless-stopped 这类策略也能帮你拉起。对单机开发、测试、小型生产部署来说,这套方案已经足够。
但你要注意,compose 的“编排”是扁平化的,它没有调度器,没有节点概念,没有自动伸缩,没有跨主机的服务发现。它更像一个“单机管家”,而 k8s 则是一个“集群操作系统”。
1.3 k8s 到底解决的是什么问题
提到 k8s,很多人第一反应是“复杂”“要命的 YAML”“运维门槛高”。这些印象不算错,但容易让人忽略它的本质:k8s 是一套分布式系统的编排和调度平台。它把多台服务器抽象成一个资源池,然后通过声明式 API 让你描述“我想要的最终状态”,比如“我要 3 个 nginx 副本”,k8s 负责调度、编排、自愈、伸缩、滚动更新。
为什么会有“k8s经典版”“k8s的lnmp架构实验”这类热搜?因为大多数人的学习路径是从单机 of things 开始的,接触 k8s 之后第一反应是“我要怎么把我熟悉的 compose 项目搬到 k8s 上”。而这正是我接下来要展开的部分。
如果你只有一台机器,跑 k8s 不是不行,但收益很低。一台机器上,compose 能做的,k8s 都能做,但绕远路。反过来也一样:如果你有五六台机器,想统一管理所有中间件和应用,想实现灰度发布、自动扩容、故障转移,这时候 compose 就力不从心了。
2. 两者本质:单机编排与集群调度
2.1 Compose 的模型:本机多容器协作
docker compose 的工作模型建立在 docker 引擎本身之上。每个 service 本质上还是一个容器,compose 做的事情是帮你把这些容器纳入一个共享网络,并按依赖关系决定启动顺序。
举个例子。一个典型的 docker-compose.yml 可能长这样:
version: "3" services: nginx: image: nginx:1.26 ports: - "80:80" volumes: - ./html:/usr/share/nginx/html php: build: ./php expose: - "9000" mysql: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: root123 volumes: - db_data:/var/lib/mysql volumes: db_data:这里 nginx 和 php 之间通过 compose 创建的 bridge 网络通信,mysql 的数据存在命名卷里,nginx 把宿主机的 80 端口映射到容器内。这个模型很简单,所有容器都在同一台机器上,网络、存储、进程都是共享宿主机内核的。
compose 的优点是简单、直观、可读性高。缺点是它没有“跨主机”的抽象。一旦你有两台以上机器,compose 就不知道该怎么把容器分布在不同的节点上,也不知道怎么处理节点故障,更不知道如何做负载均衡。你只能在外面套一层像 nginx、keepalived、consul 这样的“外挂”来弥补。
2.2 Kubernetes 的模型:声明式集群调度
k8s 的模型复杂很多,但核心思想其实不复杂:你告诉它“我要什么”,它不断尝试让现实向期望状态收敛。
- 你想跑一个 nginx,你创建一个 Deployment,里面写 replicas: 3,它会在集群里找三台合适的工作节点各自跑一个副本。
- 你想让外部访问这些副本,你创建一个 Service,它会给你一个稳定的虚拟 IP,并做负载均衡。
- 你想在发布新版本时不停机,你改一下镜像版本,它帮你做滚动更新。
- 某个节点宕机了,它会把上面的 Pod 调度到其他节点重新创建。
这种模型的底层架构有几个核心组件:API Server、etcd、Scheduler、Controller Manager,以及每个节点上的 kubelet 和 kube-proxy。API Server 是所有操作的入口,etcd 存储所有状态,Scheduler 负责把 Pod 分配到合适的节点,Controller Manager 负责各种控制循环。听起来复杂,但你日常使用 k8s 时,直接面对的主要是 kubectl 和 YAML 配置。
这里有一个非常关键的思维转变:从“命令式”到“声明式”。compose 的 docker compose up 是你告诉 docker 去启动东西,k8s 的 kubectl apply -f 是你告诉集群“这个 YAML 描述了我想要的最终状态,请你去实现并持续维护”。compose 也不是没有声明式的东西,但它的“声明”范围比 k8s 小得多。
2.3 概念映射表:把 compose 翻译成 k8s
很多人觉得 k8s 难学,是因为概念太多。但如果从 compose 的视角去看,其实很多概念是可以一一对应的。我整理了一张对照表,方便你从熟悉的概念出发理解 k8s:
| Docker Compose 概念 | Kubernetes 对应概念 | 说明 |
|---|---|---|
| service | Deployment + Service | Deployment 管副本和维护状态,Service 提供稳定访问入口 |
| container | Pod 内的容器 | Pod 是最小调度单元,可包含 1 个或多个容器 |
| network(bridge) | Pod 网络 + Service + Ingress | 默认同 Pod 内 localhost,跨 Pod 用 Service 抽象 |
| volume(命名卷/绑定挂载) | PersistentVolumeClaim(PVC)+ emptyDir | 持久化数据用 PVC,临时数据用 emptyDir |
| environment / env_file | ConfigMap / Secret | 普通配置放 ConfigMap,敏感信息放 Secret |
| depends_on | initContainer + 探针(startup / readiness) | 控制启动顺序的方式完全不同 |
| restart: unless-stopped | Pod 的 restartPolicy + 控制器兜底 | k8s 中推荐用 Deployment 控制器保证自愈 |
| ports | Service(NodePort / LoadBalancer)+ Ingress | 不建议直接在宿主机映射端口 |
| 扩展服务(docker compose up --scale) | HPA(水平自动伸缩) | k8s 支持基于 CPU/内存/自定义指标的自动伸缩 |
这张表是我做迁移时自己总结的,后来发现不少团队也是这么教新人的。你可以把它当成一本地图,而不是死记硬背的概念列表。真正需要理解的是每个映射背后的意图,比如 depends_on 在 compose 里只是控制启动顺序,但在 k8s 里光是顺序不够,还需要健康检查来判断“启动完成后是否可用”。
3. 从 Compose 到 k8s:5 步迁移路径
3.1 第一步:把 compose 项目容器化合规
我见过很多人把 docker-compose.yml 直接扔给 k8s,想找个自动转换工具一步到位。工具确实有,比如 compose-spec 转换成 k8s 的插件,但实际用下来效果一般,因为 compose 和 k8s 的模型差异太大了。我的建议是:先手工做一次“容器化合规检查”,再动手迁移。
具体检查四点:
- 镜像是否已经构建并推送到镜像仓库。compose 里如果用的是 build,k8s 里默认是没有构建能力的,你需要先构建镜像,推到私有仓库或 Docker Hub,然后在 k8s 的 deployment YAML 里用 image 指定完整地址。
- 环境变量是否可配置。compose 的 environment 可以硬编码,但到 k8s 里最好用 ConfigMap 管理,避免频繁改 YAML。
- 启动顺序是否有强依赖。如果某个服务必须等另一个服务完全就绪才能启动,你需要考虑 k8s 的 initContainer 或者探针。注意,initContainer 只能保证“启动前检查”,如果对方是一个外部服务,不如直接让应用支持重试。
- 数据持久化在哪里。compose 里无论是绑定挂载还是命名卷,迁移到 k8s 都要换成 PVC,这涉及存储类的选择,尤其是有状态服务(mysql、redis)时要特别小心。
这个阶段不用写任何 k8s 配置,先把 compose 项目的规范性和可移植性提上来。如果 compose 本身就依赖宿主机文件路径、特定内核模块或者宿主机端口,迁移时大概率会卡住。
3.2 第二步:镜像仓库与版本管理
k8s 调度到哪台机器是随机的,Pod 重建后会在新节点启动,所以镜像必须在集群所有节点都能拉取的位置。自建 Harbor 是最常见的方案,开发环境用 Docker Hub 或者阿里云 ACR 也可以。
这里有个细节常被忽略:镜像 tag 一定要带版本。很多人习惯用 latest,这在单机 compose 上没问题,但在 k8s 里会非常坑。因为 Deployment 滚动更新的判断依据是镜像 tag 或 digest 变化,如果你一直用 latest,那么你重新构建镜像、推送同一个 tag 到仓库之后,kubectl rollout restart 才能触发重新拉取,不然节点上的 kubelet 可能直接用本地的缓存镜像。正确的做法是每次构建都打一个新 tag,或者用 commit SHA 做 tag。
另外一个坑是私有仓库认证。k8s 节点拉取私有镜像需要配置 imagePullSecret,你可以把它挂在 Deployment 的 spec.template.spec.imagePullSecrets 里。很多新手迁移时镜像一直拉取失败,排查半天发现是没配 secret。
3.3 第三步:配置剥离——ConfigMap 与 Secret
compose 里最常见的配置方式有两种:直接在 environment 里写,或者用 env_file 引用文件。到 k8s 里,我强烈建议把配置统一剥离到 ConfigMap 和 Secret 中。
普通配置放 ConfigMap 的例子:
apiVersion: v1 kind: ConfigMap metadata: name: app-config data: APP_ENV: production DB_HOST: mysql-service DB_PORT: "3306"敏感信息放 Secret:
apiVersion: v1 kind: Secret metadata: name: app-secret type: Opaque stringData: DB_PASSWORD: "root123"然后在 Deployment 里引用:
env: - name: APP_ENV valueFrom: configMapKeyRef: name: app-config key: APP_ENV - name: DB_PASSWORD valueFrom: secretKeyRef: name: app-secret key: DB_PASSWORD为什么推荐这么做?因为配置和镜像分离之后,你改配置不需要重新构建镜像,只需要 kubectl apply 更新 ConfigMap,然后滚动重启 Pod 即可。在团队协作时,也不容易把密码提交到 Git。
有个坑要提醒:ConfigMap 更新后,已经运行的 Pod 里的环境变量不会自动变化。你需要修改 Deployment 的 annotations(比如加一个版本号)触发滚动更新,或者直接 kubectl rollout restart deployment 名称。如果你希望配置变化自动生效,可以考虑用 ConfigMap 挂载文件的方式,kubelet 会定期同步,但依然有延迟,业务方需要自己监听文件变化。
3.4 第四步:存储从 volume 到 PVC
如果你在 compose 里用了命名卷,迁移到 k8s 时对应的是 PVC。PVC 的挑战在于存储类的选择,不同云厂商的存储类能力不一样,常见的有:
- 本地存储(local):性能好,但 Pod 漂移后数据不跟着走,适合临时数据。
- 网络存储(NFS、Ceph、云盘):Pod 漂移后依然能挂载,适合有状态服务。
我迁移 LNMP 里的 mysql 时,用的是 NFS 存储类。配置大致是这样:
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: mysql-pvc spec: accessModes: - ReadWriteOnce storageClassName: nfs resources: requests: storage: 30Gi然后在 Deployment 里挂载:
volumes: - name: mysql-data persistentVolumeClaim: claimName: mysql-pvc这里要注意 accessModes 和实际存储类是否匹配。NFS 一般支持 ReadWriteMany,云盘可能只支持 ReadWriteOnce。如果多个副本同时读写同一个 PVC,用 ReadWriteOnce 类型的存储会直接失败。
还有一点:mysql、redis 这类有状态服务,迁移时千万不要直接把 compose 里的数据目录原样挂载过去。如果数据库版本或配置差异较大,轻则启动失败,重则数据文件损坏。稳妥的做法是先在新环境起一个空实例,再把数据导入进去。
3.5 第五步:启动顺序、探针与滚动更新
compose 里的 depends_on 看起来控制了启动顺序,但它实际上只控制容器创建的先后顺序,并不等待依赖服务就绪。比如说 mysql 还没初始化完成,php 可能就先启动了,连接数据库失败后如果应用有重试逻辑还好,没有重试逻辑就直接崩溃。
k8s 里没有 depends_on 这种写法,你需要用两种方式配合:
- initContainer:在主容器启动前执行一些检查或初始化操作。比较常见的做法是在 initContainer 里运行一条命令去检查依赖服务是否就绪,比如用 nc 或 wget 探测端口。
- 探针(Probe):k8s 有三种探针,livenessProbe 判断容器是否存活,readinessProbe 判断是否可以接收流量,startupProbe 保护启动较慢的应用。
一个简单的 readinessProbe 示例:
readinessProbe: httpGet: path: /healthz port: 8080 initialDelaySeconds: 10 periodSeconds: 5配置探针之后,k8s 才会把 Pod 标记为 Ready,然后接入 Service 的负载均衡。这和 compose 的 depends_on 相比,是本质上的升级:compose 只能保证“启动了”,k8s 能保证“可用了”。
滚动更新也值得提前设计好。默认情况下,Deployment 更新镜像时会逐个替换 Pod,如果你的应用只有一个副本,更新时会有短暂中断。如果要求零停机,需要设置 minReadySeconds、maxSurge 和 maxUnavailable。compose v2 也有 rolling 更新,但比较复杂,实际用到的人很少。
4. 典型案例:在 k8s 上把 LNMP 架构跑起来
4.1 LNMP 容器化的注意点
热搜词里出现了“k8s的lnmp架构实验”“k8s部署lnmp”,说明不少人在拿 LNMP 当 k8s 练手项目。LNMP 确实是一个很好的案例,因为它涉及 Web 服务、动态语言、数据库三种不同类型的工作负载。
LNMP 在 k8s 里的部署思路大概是:
- nginx:无状态 Web 层,Deployment 副本数可伸缩,前端静态文件可以放镜像里或挂 PVC。
- php-fpm:无状态应用层,Deployment 副本数可伸缩,nginx 通过 Service 访问 php-fpm。
- mysql:有状态数据库层,用 StatefulSet 或者 Deployment + PVC 部署,通常只跑一个副本,数据持久化到 PVC。
- redis:有状态缓存层,可以用 Deployment + PVC,或者直接上 redis Operator。如果只是单机实验,Deployment 就够了。
nginx 与 php-fpm 的通信在 compose 里很简单,因为它们在同一网络里。在 k8s 里,nginx 容器要访问 php-fpm Pod,需要给 php-fpm 创建一个 Service。比如:
apiVersion: v1 kind: Service metadata: name: php-fpm-service spec: selector: app: php-fpm ports: - port: 9000 targetPort: 9000然后 nginx 配置里的 fastcgi_pass 指向 php-fpm-service:9000。这里有个小坑:compose 里 service 名可以直接作为 DNS 名称,比如 fastcgi_pass php:9000,而 k8s 里 Service 名称同样可以作为 DNS 名称,所以其实写法是类似的,只是要注意命名空间隔离和同名冲突。
4.2 网络规划:Service、Ingress 与多网卡场景
LNMP 对外提供 Web 服务,在 k8s 里通常有两种方案:NodePort 或者 Ingress。如果你只是实验,NodePort 最简单:
apiVersion: v1 kind: Service metadata: name: nginx-service spec: type: NodePort selector: app: nginx ports: - port: 80 targetPort: 80 nodePort: 30080这样集群任何节点的 30080 端口都能访问到 nginx。但生产环境一般用 Ingress Controller,比如 nginx-ingress 或 traefik。Ingress 的好处是可以在七层做路由、TLS、限流等,而且不需要直接暴露 Service 端口。
顺便说一下热搜里提到的“k8s multus 网络 vlan 配置”。Multus 是一个多网络插件,允许 Pod 同时接入多个网络接口,可以在一个虚拟网络上跑管理面流量,另一个物理网络跑数据面流量。日常单集群项目基本用不到,但如果你在做 SDN、边缘计算或者对网络性能有特殊要求的场景,可以了解一下。它配置的核心是 NetworkAttachmentDefinition 资源,需要配合可用的 CNI(如 macvlan、vlan)和默认网络插件(如 Calico、Flannel)协同工作。这个扩展我不建议新手第一时间玩,先把基础 Service/Ingress 网络搞明白更重要。
4.3 LNMP 迁移时踩过的几个坑
我在把一套 compose 版 LNMP 迁到 k8s 的时候,踩过不少值得记录的坑。
第一个坑是 php-fpm 启动后,nginx 报 502。排查后发现是 php-fpm 的 listen 配置。compose 里容器内启动 php-fpm 默认监听 9000 端口,但在某些镜像里,php-fpm 的配置是监听 Unix socket 路径,而不是 TCP。如果是 socket,nginx 的 fastcgi_pass 需要改成 socket 文件路径,并额外挂 socket 目录。用 k8s 时,不同 Pod 之间没法共享 socket 文件,所以必须改成 TCP 监听,并在 php-fpm 的配置里指定 listen = 0.0.0.0:9000。
第二个坑是 mysql 初始化。compose 里可以用 environment 设置 MYSQL_ROOT_PASSWORD,k8s 环境也可以,但要小心 ConfigMap 里的密码包含特殊字符。比如密码里有 $ 或者 \,YAML 解析时会被转义,导致初始化密码和业务配置里的密码不一致。建议用 Secret 的 stringData 字段,避免手动 base64 转码时的格式问题。
第三个坑是镜像拉取超时。集群里如果有节点在中国大陆,直接拉 Docker Hub 镜像经常超时。解决办法是在镜像仓库层面做缓存/代理,或者把镜像同步到云厂商的镜像仓库,然后用完全限定名。这个话题跑题了,不展开,但要提醒你提前处理。
5. 常见问题与排坑实录
5.1 是不是所有 compose 项目都值得迁移到 k8s
这是每次聊到迁移时,最容易被问起的问题。我的看法是:不是。
如果你同时满足以下条件,迁移的收益其实不大:
- 项目规模小,单台服务器足以支撑业务。
- 团队没有专职运维,没有 k8s 使用经验。
- 流量变化平缓,不需要自动扩缩容。
- 数据存储强依赖本地磁盘,且没有现成网络存储。
这种情况下,docker compose 足够稳定,强行上 k8s 只会增加复杂度。迁移到 k8s 是有代价的:YAML 数量变多、学习成本上升、排查问题所需的技能栈变广、集群本身也需要维护(apiserver、etcd、网络插件、证书过期等)。“新版 docker 自带 compose”这个特性本来就是在降低单机用户的编排成本,说明 Docker 官方也在认可 compose 在轻量场景里的价值。
那什么情况下必须考虑 k8s?两条明确信号:一是需要横向扩容,二是需要故障自愈。当你的应用需要“机器挂了自动换一台继续跑”,或者“流量高峰自动增加副本”,compose 是做不了的,这时候上 k8s 就是刚需。
5.2 安装部署与版本选择的常见误区
热搜里有“k8s安装部署”“rancher装k8s”“k8s经典版”这些词,说明很多人卡在装环境这一步。k8s 的安装确实容易劝退新手,尤其是裸金属环境,证书、etcd、网络插件、kubelet 配置,每一步都可能出问题。
我给你的建议是分阶段:
- 纯学习阶段:用 kind(Kubernetes in Docker)或者 k3d,一条命令起一个集群,省去了几乎所有安装环节。适合先熟悉概念和 YAML 写法。
- 单机生产实验:用 k3s,轻量、内存占用小,也是 Rancher 团队出的产品,配 Rancher 的可视化管理界面非常方便。
- 正规多节点生产:用 kubeadm 或者云厂商托管集群(比如 ACK、TKE、EKS)。托管集群省心很多,etcd 和 master 节点都不需要自己维护。
关于版本,我建议优先选择当前稳定版本,不要追“经典版”这种过时概念。k8s 的迭代速度很快,社区一般维护最近三个小版本,老版本会有安全漏洞和兼容性问题。安装工具链版本(如 kubeadm、kubelet、kubectl)要与集群版本匹配,这个在安装文档里写得很清楚,但很多人会栽在这上面。
Rancher 作为一个可视化管理平台,适合团队多人操作,它本身不替代 k8s,而是给你一个 UI 来管理多个集群和部署应用。如果你团队里有人畏惧命令行,Rancher 能显著降低使用门槛。
5.3 高频问题速查表
最后我整理了一个高频问题表,都是我在实际操作和帮同事排障时遇到过的经典问题,直接收藏可查:
| 问题现象 | 常见原因 | 排查与解决 |
|---|---|---|
| Pod 一直 Pending | 节点资源不足 / 调度约束不满足 | kubectl describe pod 查看事件,检查 requests 配置与节点可用资源 |
| ImagePullBackOff | 镜像地址错误 / 私有仓库未认证 | 检查镜像 tag 是否存在,配置 imagePullSecret |
| CrashLoopBackOff | 应用启动失败 / 探针配置过严 | 查看日志 kubectl logs 上一个容器,调整探针参数 |
| 503 Service Unavailable | Service 选择器无匹配 Pod / 后端未 Ready | kubectl get endpoints,检查 selector 是否匹配 |
| Nginx 502 Bad Gateway | 后端服务端口不匹配 / php-fpm listen 配置错误 | 进入 nginx 容器 curl 测试后端地址 |
| ConfigMap 修改后不生效 | 环境变量注入不会自动更新 | 触发滚动更新 restart deployment |
| PVC 一直 Pending | 存储类不存在或不可用 | kubectl get storageclass,检查 storageClassName |
| NodePort 无法访问 | 安全组/防火墙未放行 | 检查节点安全组和防火墙规则,kubectl get svc 确认端口 |
| 跨节点 Pod 无法通信 | 网络插件问题 | 检查 CNI 组件状态,查看 kube-system 命名空间日志 |
| 证书过期导致 kubectl 报错 | 集群证书有效期问题 | 查看 kubeadm 证书期限,定期 renew |
5.4 我的建议:小步慢走,别搞一刀切
迁移这件事,最忌讳的就是把所有服务一次性搬到 k8s。我见过一些团队,周会上定了“下个月全部上 k8s”的目标,结果三个月后还在和 YAML 搏斗,线上事故不断。更稳妥的方式是挑一个无状态服务先迁移,比如把 nginx 前端迁到 k8s,后面依然连 compose 环境的 php-fpm。之所以能这么干,是因为 Service 的抽象天然屏蔽了后端地址的细节,只要你把 compose 服务的地址改成 k8s Service 的 DNS 或者 NodePort 地址即可。等跑顺了,再逐步迁移 php-fpm、redis、mysql。
这样做的另一个好处是团队能渐进式学习。先让一两个主力把 k8s 跑熟,再带着其他人一起踩坑。如果一开始就全员扑上去,大家的挫败感会非常强。
6. 写在最后:两个工具的定位从来不是替代关系
我接触 These two 工具多年,最深的感受是:它们不是同一层的东西,硬要比出高下其实是浪费时间。docker compose 解决的是“一台机器上怎么把多个容器组织起来”,k8s 解决的是“一个集群里怎么把各种应用调度的井井有条”。两者的关系更像自行车和汽车,都能代步,但适用场景和驾驶门槛完全不同。
如果你问我个人会怎么选,我的原则很简单:单机环境、开发测试、小规模生产,优先 compose;多节点、高可用、自动伸缩、复杂发布流程,直接 k8s。如果团队资源和精力有限,compose 完全够用的阶段强行上 k8s,只会给自己找麻烦。反过来,如果业务已经明显在增长,早点接触 k8s 其实是给未来省时间,因为真正的迁移成本会随着业务复杂度和数据量的增长指数上升。
最后分享一个比较反常识的经验:把 compose 项目迁到 k8s 之后,你再回头看 compose,往往能把 compose 写得更好。因为你理解了 ConfigMap 该怎么拆、探针该怎么配、存储边界在哪,这些认知反过来会让你的 compose 项目更加健壮。我在实际迁移完 LNMP 之后,就把原来 compose 里依赖启动顺序的写法全部改成了应用层重试加健康检查,整体稳定性提升非常明显。
工具永远在变,但“把应用状态描述清楚、让系统自己去维护”的这种声明式思维,是这两个工具共同传递给你的最有价值的东西。学会了它,将来不管再出现什么新编排系统,你上手都会快很多。