☰
K8s容器编排入门:从Pod、Deployment到Service的实战指南
2026/10/22 3:57:06 网站建设 项目流程

这几年只要是搞后端、搞运维、甚至跟前端稍微沾点边的,没人绕得开“k8s”三个字母。要不是之前面试被问到,我可能到现在还停留在听说过、没用过的阶段。我的理解是,k8s 就是 Kubernetes 的简写,k 和 s 之间有 8 个字母。这东西说白了是一套容器编排系统,负责帮你管理一大堆跑在容器里的应用。这篇文章就是写给跟我当初一样、想快速建立 k8s 心智模型的同学看的:不管你是开发、运维还是测试,只要能搞懂它解决什么问题、核心概念怎么串,再看懂几份配置文件,就能在公司里把应用部署这件事聊得明明白白。

我尽量少讲玄乎的概念,多讲“为什么要这样设计”和“我实际操作时踩过的坑”。文章涉及的部分命令和配置文件,你完全可以照着敲一遍,跑不跑得通其实不重要,重要的是理解每一步在干嘛。

1. 为什么大家都在聊 k8s:先搞清楚它解决什么问题

1.1 容器时代带来的“新麻烦”

很多人一开始会问:我有 Docker 不就够了吗,为什么还要搞个 k8s?

没错,单机情况下 Docker 确实很好用。你写个 Dockerfile,把依赖装好,docker build出镜像,docker run启动容器,一个应用就完事了。但如果到了几十台服务器、上百个服务的规模,事情就没这么简单了。

举个实际场景:你有一个支付服务,很火,现在一台服务器撑不住了。你按传统思路再开一台,复制一份部署,前面架个负载均衡,两个实例分担压力。听起来不复杂,可一旦服务扩容到十几个,加上版本迭代、凌晨发布、机器宕机、网络分区,你很快就会面临几个灵魂拷问:

  • 某个服务挂了一个实例,谁负责把它拉起来?
  • 流量高峰来了,怎么在几分钟内给某个服务扩容 10 个副本?
  • 新版本出了,怎么做到不中断服务地滚动替换实例?
  • 环境变量、配置项、数据库密码,怎么统一管理,不散落在各种启动脚本里?

这些问题,就是容器编排工具要解决的。而 k8s 正是在这一堆问题里长出来的标准答案。它不是某家公司闭门造车的产物,而是经过大规模线上验证过的成熟方案。可以想象成集装箱码头管理系统:Docker 给了你标准集装箱(容器),k8s 则是码头上那套负责调度吊车、安排船期、管理堆场的自动化系统。

1.2 Kubernetes 的核心能力

k8s 的能力可以总结成一句话:声明式地管理容器化应用,让系统持续处于你期望的状态。

所谓“声明式”,跟“命令式”是相对的。命令式是你一步步告诉系统“怎么做”,比如先启动容器再装 Nginx;声明式是你直接告诉系统“要什么”,比如“给我 3 个 Nginx 容器”,剩下的怎么达到归系统管。

这句话拆开来讲,包含几层含义:

  • 自动调度:新来一个容器,系统会自动找一台负载合适的机器去放。
  • 健康检查与自愈:容器里的进程挂了,系统会自动重启;机器宕机,会把上面的容器重新调度到别的机器。
  • 弹性伸缩:可以设定“CPU 使用率超过 70% 就加副本”,系统会自己执行。
  • 滚动发布:新版镜像发布时,逐步替换旧实例,保持服务不中断。
  • 服务发现与负载均衡:容器都是随时会换 IP 的,系统提供一套内置的稳定访问入口。

1.3 入门 k8s 的正确姿势

说完了它是什么,我想聊聊怎么学。k8s 的内容范围非常大,很多人刚开始就是从 YAML 文件一头扎进去,结果被 Pod、 Deployment、 Service、 Ingress、 ConfigMap、 Secret、 PVC 这些概念砸得头晕。

我的建议是分三步走:

  1. 先理解架构,搞明白控制节点和工作节点分别在干嘛。
  2. 再理解工作负载,就是应用是怎么被描述和运行的。
  3. 最后理解流量和存储,应用对外怎么暴露,数据放哪里。

不要一上来就被各种 Operator、 Helm、 Service Mesh 的英文缩写吓唬住,那些都是生态工具。k8s 本身的核心思想其实很朴素:Master 管事,Node 干活,跑一堆有名字的进程,再配一堆描述用的 YAML,就这些。

2. k8s 里的核心概念其实不多:先抓住这几头

2.1 控制平面与工作节点:大脑和手脚

k8s 集群一般分两种角色:控制平面(Control Plane)和工作节点(Node)。控制平面是大脑,负责做决策;工作节点是手脚,负责真正跑容器。

控制平面有几个关键组件,各司其职:

  • API Server:整个集群的唯一入口,你用的 kubectl 命令最终都是打到它这里。它负责鉴权、校验、转发请求。
  • etcd:集群状态的数据库。所有配置和实际运行状态都存在这里,本质是一个分布式键值存储。
  • Scheduler:调度器。当事先定义好的“应用期望状态”被提交后,它负责挑一台最合适的节点去创建容器。
  • Controller Manager:一堆控制器的集合。它像巡警一样,不断巡检实际状态与期望状态是否一致,不一致就想法子修正。

工作节点上也有三个常驻组件:

  • kubelet:负责接收控制平面的指令,并在本机上创建或销毁容器,同时定期上报节点状态。
  • kube-proxy:负责维护节点上的网络规则,实现服务访问的转发和负载均衡。
  • 容器运行时:真正执行容器的组件,比如 containerd、cri-o 这类。

我第一次近距离观察集群组件状态时,感觉整个系统就像一家分工明确的公司管理层加执行层,各组件间的沟通全靠 API Server 中转,大家不直接私下乱聊,这让整个系统非常规整,也特别容易排查问题。

2.2 第一个要认识的对象:Pod

很多人刚学 k8s,会先遇到一个概念:Pod。理解 Pod 是理解整个 k8s 的关键一步。

Pod 是 k8s 中最小、最基本的调度单位。它不是直接调度容器,而是调度 Pod——一个 Pod 里可以装一个或多个容器。

你可以把 Pod 想象成一个“应用实例的方盒子”:盒子里的容器共享同一个网络命名空间和存储卷。里面所有容器都通过 localhost 就能互相访问,文件系统也可以共享同一个磁盘目录。

一个 Pod 里放多个容器,常见场景是“主力进程加辅助进程”。比如有一个服务需要定期同步日志到某个外部存储,那主容器跑业务,辅助容器跑日志同步工具,两个容器共享一份挂载卷,就能配合工作。

但实际上,绝大多数情况下一个 Pod 里面只放一个主容器。前期学 k8s 的时候,可以直接把 Pod 理解为“一个运行中的容器实例的包装”,并不会出大错。只是需要认识到,Pod 是会被随时创建和销毁的,它的 IP 不固定,所以不能直接用 Pod IP 作为外部访问目标。

2.3 Deployment:你真正要写的清单

有了 Pod 的概念,下一个核心对象就是 Deployment。它是你最常打交道的工作负载控制器。

Deployment 的作用是描述“我想要的一组 Pod”长什么样:用哪个镜像、多少副本数、什么端口、什么环境变量。它负责维护这组 Pod 的生命周期,保证正在运行的副本数始终跟期望副本数一致。

举个例子:你指定副本数为 3,系统会创建 3 个 Pod。其中一个出故障被 kubelet 上报,控制器就会立即重新创建,让总数回到 3。这就是“自愈能力”。

更难得的是 Deployment 还内置了滚动更新策略。发新版时,它不会一下子把所有旧 Pod 删掉再全部重建,而是严格控制节奏:先起一个新的,等它 ready 再删掉一个旧的。整个过程中,服务一直有实例在跑,不会出现全部宕掉的窗口期。

我第一次做滚动发布操作时,确实是屏住呼吸看状态的。一条命令执行完,几秒内 pod 列表慢慢刷刷刷地变化,服务全程没有断过,整个体验非常舒服。

2.4 Service:让流量稳定进出的门牌

既然 Pod 的 IP 是“用完就丢”的临时 IP,那总不能每次发布完都把新的 Pod IP 告诉调用方吧。这就是 Service 存在的意义。

Service 提供了给一组 Pod 提供统一、固定的访问入口。它有自己的虚拟 IP(ClusterIP),并且通过 selector 标签选择要代理的 Pod 集合。Pod 数量变化、IP 变化,Service 都不受影响。

Service 的访问方式有好几种类型,我只说常用的三种:

类型用途可访问范围
ClusterIP集群内部访问,默认类型集群内
NodePort在每台节点上开一个端口转发到 Service集群外部通过节点 IP+端口访问
LoadBalancer云厂商提供的负载均衡器公网

这里有个很绕但也特别重要的点:Service 本身只做“转发”,不做客户端感知的负载均衡配置。它通过 selector 把请求分派给一组 Pod,由 kube-proxy 在数据面完成转发工作。

2.5 Namespace 和 ConfigMap/Secret:资源隔离与配置管理

再补充两个常用对象,也是实际部署中一定会遇到的。

Namespace是资源隔离逻辑边界。它把集群从逻辑上切成不同的空间,互相隔离。比如系统划分为 test 和 prod 两个空间,test 里的资源在 prod 中不可见,也防止大家命名冲突。

我不建议一开始就在所有地方用自定义 Namespace,但需要一个清晰的认知:kubectl 命令默认操作的是 default 命名空间,如果资源在别的命名空间,必须通过-n参数指定。

ConfigMap负责存非敏感配置:比如应用读取的环境变量、配置文件。Secret负责存敏感信息:数据库密码、API 密钥。两者可以挂载成环境变量,也可以挂载成文件。使用它们的价值是让镜像本身不用存放任何环境相关的配置,同一个镜像在不同环境通过注入不同配置,就能跑出不同的行为。容器本身能做得更“纯净”,配置与镜像解耦。

3. 亲手跑一个应用:从清单到发布一次走通

3.1 场景设计与目标

概念讲再多,都不如实际操作一遍来得彻底。这个章节,我设计一个最简单也最经典的场景:部署一个 Nginx 静态网页服务,要求 3 个副本,通过 NodePort 暴露给集群外部访问。

这个场景覆盖了 k8s 最核心的主干流程:写好 YAML 描述、提交给集群、Pod 创建运行、Service 暴露流量。做一遍,你就对 k8s 的整体操作有感觉了。

为了真机演示,本地需要有一个可用的 k8s 集群环境。我实际用的是轻量级本地集群方案,比如 minikube 这类工具,它能在一台机器上快速拉一个单节点集群,适合学习和调试。启动非常简单,一个命令就可以,启动完成后它会自动配好 kubectl 连接信息。

3.2 编写第一个 Deployment 清单

现在进入正题。第一步,创建一个描述 Deployment 的文件,命名为web-demo.yaml。

核心内容如下:

apiVersion: apps/v1 kind: Deployment metadata: name: web-demo labels: app: web-demo spec: replicas: 3 selector: matchLabels: app: web-demo template: metadata: labels: app: web-demo spec: containers: - name: web image: nginx:1.25 ports: - containerPort: 80

这个文件有四个关键部分,我一点一点拆开说。

  • apiVersion:文档版本号。Deployment 当前的稳定版本在apps/v1,网上老教程里写的extensions/v1beta1已经过期了,不建议照抄。
  • kind:资源类型,这里就是Deployment。
  • metadata:命名信息,包括名称和标签。
  • spec:期望状态的描述,是核心部分。

spec 里的 selector 的matchLabels对应的是“这个 Deployment 管哪些 Pod”。它必须跟 template 元数据中的 labels 吻合,否则控制器会认为模板产生的 Pod 不在它的管辖范围内。

replicas: 3表示期望 3 个副本。containerPort: 80只起到声明作用,方便查看和后续关联 Service。镜像字段nginx:1.25我建议尽量写具体版本,避免直接写latest这种标签,否则之后很难追溯哪个版本正在运行。

3.3 提交配置并观察运行状态

写完清单后,执行应用命令:

kubectl apply -f web-demo.yaml

看到回显deployment.apps/web-demo created就提交成功了。接下来马上查看 Pod 状态:

kubectl get pods

一次很关键的细节是:刚提交完马上看,Pod 可能还处在ContainerCreating状态,这是正常的,容器需要拉镜像、启动进程。等十几秒再执行,如果看到类似下面这样 3 个 Pod 都在运行,就说明部署成功了:

NAME READY STATUS RESTARTS AGE web-demo-7d8b9f5b6c-abcde 1/1 Running 0 52s web-demo-7d8b9f5b6c-fghij 1/1 Running 0 52s web-demo-7d8b9f5b6c-klmno 1/1 Running 0 52s

Pod 名字后面的那串随机字符串不是乱码,它由两部分组成:Deployment 生成的 ReplicaSet 编号 + Pod 唯一ID。以前我刚开始看,以为是乱码,后来才知道前一段是 Pod 模板版本的哈希。

如果某个 Pod 一直卡在ContainerCreating,可以用下面命令查详细信息:

kubectl describe pod <pod名称>

describe 输出的尾部 Events 区域非常关键,你能看到到底是拉镜像失败、还是挂载卷失败、还是容器启动报错。启动报错一般会显示 ExitCode 和日志摘要。

3.4 创建 Service 暴露服务

Deployment 创建好了,但集群内部和外部现在都还访问不到这些 Nginx Pod。需要再写一份 Service 清单把它暴露出来。

创建web-svc.yaml文件:

apiVersion: v1 kind: Service metadata: name: web-demo-svc spec: selector: app: web-demo type: NodePort ports: - port: 80 targetPort: 80 nodePort: 30080

这里有几个字段需要说明:

  • port:Service 对外暴露的端口。集群内其他服务访问web-demo-svc:80就能访问到这批 Pod。
  • targetPort:请求最终转发到 Pod 上的哪个端口。跟容器里的监听端口一致,这里是 80。
  • nodePort:节点上开的端口,范围默认是 30000-32767。手动指定一个 30080,方便外部记忆。

如果不想指定 nodePort,可以只写port和targetPort,系统会自动分配一个空闲的 3 万段端口。手动指定在有安全组或防火墙规则的场景更好用。

提交服务并查询:

kubectl apply -f web-svc.yaml kubectl get svc

如果有一行web-demo-svc显示NodePort 10.xx.xx.xx 80:30080/TCP,就说明服务创建成功。此时打开浏览器,输入所在机器的 IP 加冒号加 30080 端口,比如http://127.0.0.1:30080,能看到 Nginx 的欢迎页,就说明整个链路是完全通透的。

这里分享一个我踩过的坑:如果你在自己电脑上访问不到,优先检查是不是因为防火墙挡了端口,或者是集群本身的网络策略。很多时候问题根本不在 k8s 里面,而在网络层。

3.5 体验滚动发布与回滚

服务跑起来了,再试试发布流程,这也是 k8s 最吸引人的能力之一。

把镜像版本从nginx:1.25换成nginx:1.26,执行:

kubectl set image deployment/web-demo web=nginx:1.26

这条命令会触发生成新的 ReplicaSet,然后开始滚动替换。执行之后,建议立即打开另一个终端,循环执行:

kubectl get pods -w

会看到旧版本的 Pod 在不断终止,新版本 Pod 在不断创建,整个过程中始终保持有几个 Pod 是 Running 的。

如果不小心发布了一个坏版本,回滚很简单:

kubectl rollout undo deployment/web-demo

它会回退到上一个稳定版本。可以理解为 K8s 给所有 Deployment 记录了一本“发布相册”,可以随时回到上一张“照片”。

我想特别强调:发布操作虽然简单,但生产环境建议尽量使用“先部署一批、验证再全量”策略。不要一次性把新版本推给全部副本,可以用 k8s 的发布策略参数控制并发数和暂停点,先让一小部分流量走新版本,确认稳定后再继续。

4. 常见问题与排查技巧实录

4.1 Pod 一直 Pending

这是新手的第一个高频问题。kubectl get pods看到 Pod 一直在Pending,说明调度器还没找到合适的节点来运行这个 Pod。

排查方式是查看详细信息:

kubectl describe pod <pod名称>

常见原因有几种:

  • 节点资源不足:CPU 或内存的 requests 已经超了节点剩余量。解决方法是增加节点,或者降低实例的资源请求值。
  • 存在污点和容忍度问题:节点打了污点,Pod 未设置对应容忍度,调度器会跳过这个节点。
  • PVC 无法绑定:如果配置了持久化卷声明,但集群里没有可用的持久化卷接上,Pod 也会卡 Pending。

4.2 镜像一直拉不下来

Pod 状态是ImagePullBackOff或ErrImagePull。

这个报错的字面意思是“镜像回退”,本质是拉取失败多次后的退避策略。看 describe 信息,Events 里会直接显示失败原因。

原因也不复杂,无非两类:

  • 镜像不存在或版本号写错:命令去镜像仓库手动拉一遍就知道。
  • 仓库需要认证但没配置:私有仓库必须通过imagePullSecrets指定凭证。

如果你用的是本地私有镜像仓库,还有个常见坑:仓库没配 HTTPS,kubelet 默认拒绝连接。需要在节点上配置 insecure registry,或者添加信任证书。这个问题描述信息里面直接会给你一个类似http: server gave HTTP response to HTTPS client的报错,定位很快。

4.3 容器反复重启

Pod 看起来是 Running,但 READY 列显示 0/1,或者 RESTARTS 数字一直在涨。这就说明新进程启动后,又立刻崩了,然后再被拉起来。

有个排查技巧。首先看它为什么崩溃:

kubectl logs <pod名称>

如果日志没有有用信息,加上前一个容器的日志:

kubectl logs <pod名称> --previous

这里要补充一个容易忽略的点:启动失败不一定全是进程问题,也有可能是 livenessProbe 存活探针配错了。如果探针指定的端口或者路径不对,kubelet 会认为容器不健康,持续执行重启操作。

遇到这种问题,先想想你的 Pod 是正常退出的,还是被探针杀掉的,别急着改代码。观察一段时间,如果你没有配探针,那就直接看启动日志,多半很快就有眉目。

4.4 Service 访问不通

Service 创建好了,但访问始终不通。这类问题我用一个固定套路快速排查。

先确认后端有 Pod 在运行,且处于 Running:

kubectl get pods -l app=web-demo

再确认 Service 能选择到这些 Pod。用下面命令看 endpoints 是否绑定上:

kubectl get endpoints

如果 endpoints 列表为空,说明 Service 的 selector 写的标签跟 Pod 的标签不匹配。仔细核对 selector 和 Pod template 里的 labels 是否一致。

如果 endpoints 有值,那问题可能出在网络层,依次查这几项:

  • NodePort 是否跟 config 里写的一致
  • 节点防火墙是否放行了节点端口
  • kube-proxy 相关服务是否健康

这条链路排查过一次之后,就基本具备在网络复杂、多依赖环境下定位问题的能力了。记住:不要一上来就去翻完 k8s 的源代码,按顺序排就行:Pod 健康状态 → Endpoint 是否是绑定上 → Service 配置 → 网络策略。

4.5 常用调试命令速查

把日常排查用得最多的命令整理成一张速查表,建议保存:

命令作用
kubectl get pods -A查看所有命名空间的 Pod
kubectl describe pod <name>查看 Pod 的详细事件
kubectl logs -f <name>实时查看容器日志
kubectl exec -it <name> -- bash进入容器内部调试
kubectl get svc --all-namespaces查看所有服务
kubectl get events --sort-by=.metadata.creationTimestamp按时间排序查看集群事件
kubectl rollout status deployment/<name>查看发布进度

加-o wide参数,能够多输出几列详细信息,包括 Pod 所在节点、IP、Service 的 ClusterIP 和端口等,值得多用。能用命令解决的事,尽量不进控制台界面去点点点。

5. 团队落地 k8s 的几点经验

5.1 从最小可用开始:不要一上来就引入全套生态

在团队里推广 k8s,最容易犯的错误是一上来就上整套方案:Helm、Ingress、Istio、Prometheus、Kustomize 全上,文件拆得又深又碎。最后团队没人能看明白整套配置,一出现问题无人敢动。

我带过的经验是:先跑通一个最朴素的 Deployment + Service,再逐步加东西。最小可用的一整套配置总共不超过 40 行 YAML,所有人都能看懂,出了问题也定位得了。

等到项目多了,需要共用配置模板了,再考虑引入 Helm 这类工具。一定要让团队先形成“这套部署我能说清楚”的共识,再追求自动化。

5.2 镜像版本管理不做好,后面全是灾难

这个我认为是最值得强调的经验。刚上手时,大家图省事,全部用latest标签。等出了问题想回滚,一看生产环境跑的是latest,哪个镜像版本都对应不上,根本没法回滚。

正确做法是:

  • 每次构建生成唯一 Tag,建议直接用流水线的构建号或 commit Hash。
  • 部署清单里的镜像版本固定写死,不写动态标签。
  • 回滚之前确认当前线上版本跟清单记录的版本一致。

另外要注意镜像仓库的存储清理。小集群跑半年,不清理旧镜像,仓库会慢慢膨胀,导致拉取性能下降。

5.3 资源请求一定要设置

很多初学者往 k8s 塞工作负载,不给 requests 和 limits。这是把集群当做一个共享大水池在用,谁也说不准某个服务是不是偷偷吃光了节点 CPU。

resource requests 和 limits 的本质价值在于:

  • requests帮助调度器做容量判断,保证 Pod 能拿到的最小资源。
  • limits限制 Pod 能使用的资源上限,防止“内存泄漏拖垮邻居”。

建议测试环境至少设置 requests,生产环境 requests 和 limits 都设置。不要设置得过紧,否则会频繁触发 CPU 限流和 OOM 重启。

5.4 日志和监控要趁早

我可以直说,没有监控就开始上 k8s,等于摸黑下山。因为容器是可替换的,一旦 Pod 重建,日志就留在被销毁的容器里了。如果没有收集,排障就是一片空白。

建议部署 k8s 之初就把监控和日志收集纳入规划,三个基础指标至少得有:

  • 资源类:节点 CPU、内存使用率,Pod 资源饱和度
  • 运行类:Pod 重启次数、Pending 数量、错误事件
  • 网络类:Service 的请求量、延迟、错误率

不一定要上很重的平台,先把核心指标收上来。然后给关键告警通道跑通,比如工作负载频繁重启时它会通知到你,这样很多问题能在用户感知之前被处理。

5.5 权限管理:别让所有人都拥有超管权限

权限这块经常被小团队忽略。在小范围内部时,团队人少,所有人拉进一个拥有集群管理权限的组,图个方便。人多了以后,误删、误改就成了潜在的风险。

k8s 原生的 RBAC 机制支持按命名空间隔离,可以做到只看不动、只操作某些固定资源。至少做到测试、生产环境,让核心运维人员才持有完整权限,其他开发人员只读或者仅能在自己对应的命名空间内执行操作。

6. 我自己踩的坑和后续想做的事

文章到这里,原理、实操、排障基本上都覆盖了。最后聊聊我个人的感受。

我先说一个当初特别容易迷惑的现象:跟着网上教程敲命令时,别人的命令一晃眼可能就有长得不太一样的段位,尤其版本差异会造成不同的字段写法,比如apps/v1beta1、extensions/v1beta1都已经成历史。现在除非你在维护老系统,否则尽量使用稳定版本的 API 去写,别把老语法当成标准。

再有就是,如果你是从测试或运维入手的,别去纠结 Service Mesh 那些花里胡哨的东西。先把“Pod 如何创建、如何被 Service 选中、如何对外提供访问”这条主线跑通,再学发布策略和资源管理。这跟学开车一样,先把直线走稳,再去玩什么弯道超车。

后面如果有时间,这篇文章的实操部分还可以继续扩展,比如用 Ingress 替代 NodePort 做更优雅的域名访问、用 PVC 挂载持久化存储目录、用 HPA(水平自动扩缩容)体验一下秒级扩容。这些内容在现在这套基础之上,大约只需要增加几个概念,就能串起来了。等我真的把这些摸透了、踩新坑了,再来继续分享。

希望这篇扫盲文章能帮少部分还没入过 k8s 的朋友,至少看到 YAML 文件和心理上感觉不再像看天书。工具好不好,只有上手才知道。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询