如果你已经在用 Docker 跑起来应用,那接下来一定会撞到这堵墙:容器一多,光靠手工 docker run 显然撑不住挂了谁来拉起来?扩容了怎么让流量切过去?版本更新怎么保证不中断服务?这些问题催生出的答案就是 Kubernetes(社区里都简称 K8s)。它不是一个新奇的容器运行时,本质上是给容器加了一层"编排层",用声明式的方式描述你想要的状态,剩下的交给它去调和现实。这篇文章不堆概念,就按一个新手真正会走过的顺序,把 K8s 的核心对象、自己搭环境的路径、以及那些最容易踩的坑讲清楚。
如果你打算认真跑一套多节点集群,节点机器可以先看看这两家:狗云DOGYUN(高性价比云服务器,多机房可选)、Zenlayer(全球多地区节点,存储与带宽组合多)。K8s 对节点最基本的要求是能跑 containerd 或 dockerd、内核版本不太老、以及节点之间延迟别太高,家用宽带拉的机器做实验可以,真要跑业务还是得挑像样的。

一、先搞清 K8s 到底在管什么
很多人第一次接触 K8s,是被那些名词吓退的:Pod、Deployment、Service、Ingress、Namespace、ConfigMap、Secret……一屏下来根本记不住。其实把这些名词砍掉一半,剩下的就一句话:K8s 管的是"一组副本"和"怎么找到它们"。
最基础的对象是 Pod。它不是容器,是容器的一个"跑位"一个 Pod 里可以放一个或多个共享网络与存储的容器,共享同一个 IP。这套设计现在看起来有点反直觉,但它解决了两个很现实的问题:多个紧密协作的容器(比如一个 Nginx 加一个应用)不该被拆到不同机器上;以及未来 Pod 内容可能不是容器,这个抽象能兜住。
Pod 之上是 Deployment,它才是生产环境里真正天天打交道的对象。你在 Deployment 里写"我要 3 个副本,镜像用 X 版本",K8s 就保证集群里始终有 3 个这样的 Pod 在跑,少了给你补,多了给你删,节点挂了一个就把副本调度到别的节点上去。配置里还有滚动更新策略,改镜像版本时它会先起新的 Pod、就绪之后再把旧的停掉,配合就绪探针基本能做到发版不中断。
Service 解决的是"怎么访问"的问题。Pod 的 IP 是会变的,一旦重建就换地址,前端硬编码 IP 肯定不行。Service 会在集群内部给一组 Pod 挂上一个稳定的服务名和虚拟 IP,还自带一个简单的负载均衡,把请求轮着发给后端 Pod。集群外部要进来的流量,则由 Ingress 或者 NodePort 来接。
至于 Namespace,本质是给资源分组做软隔离的,把测试环境和生产环境分开放,权限、配额、资源限制都能按名字空间分别设置,是集群里必须学会的第一层组织方式。

二、不买服务器也能先玩起来
很多人卡在"我没有三台机器"这一步,其实完全没必要。真要动手,第一步是用 minikube 在自己电脑上拉起一个单节点集群,它会把容器运行时、kubelet、etcd 这些组件一次性在虚拟机里跑起来,一条命令就有了一个可以随便折腾的 K8s 环境。装好之后 kubectl get nodes 能看到那台机器的状态为 Ready,就可以开始写 Deployment 了。虚拟机内存给到 2G 以上会比较舒服,不然 API Server 容易被挤爆。
如果本机装不动,或者想在多台机器上试真实的调度行为,k3s 也是个不错的选择,它是个轻量级的 Kubernetes 发行版,几十兆安装包,一台 Ubuntu 上跑 curl ... | sh - 就起来了,特别适合放在一两台云主机上拼一个玩具集群。
想省事还可以用 KDash 这类图形化控制台,它包装了 K8s 的 API,把节点、Pod、工作负载、事件、资源占用这些信息整理成网页界面,新手阶段用它看状态比自己啃 kubectl get pod -w 舒服太多。当然它只是看和点,真正的生产环境运维还是建议老老实实用命令行和 YAML。
三、配置文件怎么写,先抓三个字段
K8s 的资源都用 YAML 描述,套路高度一致。先看一个最小的 Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo
spec:
replicas: 3
selector:
matchLabels: {app: demo}
template:
spec:
containers:
- name: demo
image: nginx:1.27
看着长,其实只有三块是必填的:replicas 决定副本数,selector 是标签选择器,template 是 Pod 的模板。这三者的关系要理解清楚:Deployment 靠 selector 找到"属于自己"的那些 Pod,如果 selector 里的标签和 template 里的标签对不上,它会认为一个 Pod 都不归自己管,然后疯狂地创建新 Pod,永远不收敛这是新手最常见的死循环。
想让服务被访问,再加一个 Service:
apiVersion: v1
kind: Service
metadata:
name: demo-svc
spec:
type: ClusterIP
selector:
app: demo
ports:
- port: 80
targetPort: 80
这里的 selector 又是一组标签,要和 Deployment 的 template 标签对应上。原理是:Service 并不关心 Pod 是谁创建的,它只按标签去找,把流量发给所有带这个标签的 Pod。所以 K8s 里标签就是一切串联起来的线。
四、Helm 解决"模板复制粘贴"的问题
手写 YAML 跑通一个应用还行,但真上生产,一个应用可能涉及十几个资源对象:Deployment、Service、Ingress、ConfigMap、Secret、HPA……每次发版都复制粘贴一遍,迟早出乱子。Helm 就是干这件事的,它把一整套资源打成一个 Chart,用模板变量代替具体值,安装时通过 values 文件注入环境相关的配置,一条 helm install 命令全部搞定,卸载也是一条命令清干净。
Chart 的目录结构也不复杂,一个 Chart.yaml 描述版本和依赖,一个 values.yaml 存默认配置,模板放在 templates/ 目录下,渲染出来就是普通的 K8s YAML。装之前可以先 helm template 渲染一遍看看结果对不对,比直接 apply 之后才发现问题省事得多。
要注意的是,Helm 不是必需但值得早点学。刚开始一两个应用的时候手写 YAML 反而更直观,等到资源对象超过十个,你会发现手动管理的痛苦已经压不住了。

五、新手最容易踩的几个坑
资源没设 requests 和 limits。这是最普遍的问题。不限制的话,一个 Pod 就能把节点内存吃光,把邻居拖垮,导致节点被标记为 NotReady,Pod 被重新调度,来回折腾。哪怕是小实验,也建议给每个容器写上 requests 和 limits。
没有健康检查探针。容器进程还活着不代表服务能正常响应。liveness 探针判断"要不要重启它",readiness 探针判断"要不要把流量分给它",这两个配好了,滚动更新才真正平滑,否则新 Pod 还没起来流量就切过去了,直接 502。
忘了 Service 的 targetPort。写 80 端口不代表容器真的监听 80,Flask 应用可能跑在 8000,Go 服务可能在 9090,容器端口写错的话 Service 会一直连不上,现象是访问超时而不是明确报错,最难查的一种。
镜像用了 latest 标签。配合滚动更新会出现"这次部署的其实和上次不是同一个镜像"的情况,回滚也回不到预期版本。生产环境老老实实用具体版本号或者 digest。
没配持久化存储。Pod 一重建里面的数据就没了,需要持久化的部分要用 PersistentVolume 加 PersistentVolumeClaim,别指望容器本身能存住东西。
另外还有两个习惯值得养成:所有资源都用 YAML 管理并进 Git,别在集群上直接改,改了下次 apply 就给你覆盖回去了;给关键服务做跨节点反亲和调度,比如加 podAntiAffinity 让同一个应用的副本尽量分散在不同机器上,一台机器挂掉不会把服务全带走。

六、什么时候该上 K8s,什么时候是自找麻烦
最后说点实在的。K8s 的复杂度不低,它带来的是"标准化、可复制、故障自愈",但代价是学习曲线、运维成本和资源开销。如果你只有一两个服务、日流量也不大,用一台云主机加 Docker Compose 拉起来,反而更省心也更省钱,没必要为了追时髦把整个架构搞复杂。
但如果你的情况是"服务超过五个、每天要发好几次版、团队不止一个人、还要求某个服务挂了不能影响别的",那 K8s 那套东西带来的收益就非常明显了发布变简单了,扩容变成改一个数字,副本数自带的容错让你敢把服务放上去而不心虚。
入门路径我建议就三步:先在本地用 minikube 跑通"一个 Deployment 加一个 Service"的完整闭环,把标签选择器这一层彻底搞明白;然后在两三台云主机上用 k3s 拼一个多节点集群,亲手制造一次节点故障,看 Pod 被自动调度到别处;最后再引入 Helm,把已经验证过的那套 YAML 模板化。别一上来就啃官方那本几百页的文档,边做边查,遇到问题再回头看对应章节,效率高得多。


喜欢
顶
难过
囧
围观
无聊



