理解 Kubernetes Pod 与 Node 之间的核心区别
掌握 Kubernetes 架构的基础知识,明确 Pod 和 Node 的角色。本指南解释了 Node 是提供资源的底层计算机器,而 Pod 是托管应用程序容器的最小可部署单元。了解这些组件如何通过调度器交互、资源请求的关键考虑因素以及确保应用程序稳定性的实用故障排除步骤。
理解 Kubernetes Pod 与 Node 之间的核心区别
如果你是 Kubernetes 新手,理解 Pod 与 Node 的区别是掌握系统其余部分的第一步。Node 是机器。Pod 是 Kubernetes 放置在该机器上的工作负载。容器在 Pod 内运行,Pod 在 Node 上运行。
这听起来很简单,但它解释了许多日常故障排除场景。如果 Pod 处于 Pending 状态,Kubernetes 可能没有找到合适的 Node。如果 Node 处于 NotReady 状态,其上的每个 Pod 都面临风险。如果你的应用需要高可用性,仅运行更多容器是不够的;你需要将副本分布到不同的 Node 上。
Kubernetes 集群架构概述
Kubernetes 集群由一组机器(物理或虚拟)组成,协同工作。这些机器大致分为控制平面(管理集群状态的大脑)和工作节点(运行实际工作负载的肌肉)。Pod 和 Node 在此结构中交互。
- Node: 提供 CPU、内存、磁盘和网络的物理或虚拟机器。
- Pod: Kubernetes 调度的最小可部署单元。它托管一个或多个容器。
理解这个层次结构——Node 托管 Pod,Pod 托管容器——是掌握 Kubernetes 的起点。
Kubernetes Node:计算能力的基础
Kubernetes Node(有时称为工作机器)是提供必要计算资源(CPU、RAM 和网络)来运行应用程序的机器。集群必须至少有一个 Node,但生产环境通常使用多个 Node 以实现冗余和可扩展性。
Node 的关键职责
每个 Node 运行必要的组件,使其能够与控制平面通信并托管应用程序工作负载:
- Kubelet: 在每个 Node 上运行的代理,负责与控制平面通信。它与容器运行时协作,确保 PodSpec 中描述的容器在其 Node 上运行且健康。
- 容器运行时: 负责拉取镜像和运行容器的软件,现代集群中通常使用 containerd 或 CRI-O。
- Kube-proxy: 维护 Node 上的网络规则,实现与 Pod 的内部和外部通信。
实际示例:Node 表示
当你检查集群中的 Node 时,你看到的是 Kubernetes 正在使用的底层基础设施:
kubectl get nodes
NAME STATUS ROLES AGE VERSION
worker-node-01 Ready <none> 2d1h v1.27.4
worker-node-02 Ready <none> 2d1h v1.27.4
关键要点: Node 是执行发生的机器层。
Kubernetes Pod:最小的可部署单元
Pod 是 Kubernetes 中的原子部署单元。它本身不是容器,而是围绕一个或多个容器的包装器,这些容器保证位于同一 Node 上并共享资源。
为什么使用 Pod 而不是直接使用容器?
Kubernetes 管理 Pod 而不是单个容器,原因如下:
- 共享上下文: 单个 Pod 内的所有容器共享相同的网络命名空间(IP 地址和端口范围),可以通过
localhost轻松通信。 - 共享存储: 同一 Pod 中的容器可以访问相同的挂载存储卷。
- 生命周期管理: Kubernetes 将 Pod 视为单个实体。如果 Pod 内的任何容器失败,Kubernetes 会处理整个 Pod 结构的重启或重新创建。
Pod 的结构
大多数情况下,Pod 包含一个主应用程序容器。然而,它们经常用于边车模式,其中辅助容器协助主容器(例如,日志代理、服务网格代理)。
示例 Pod 定义(简化 YAML)
以下 YAML 定义了一个包含单个 Nginx 容器的 Pod:
apiVersion: v1
kind: Pod
metadata:
name: nginx-pod
spec:
containers:
- name: nginx-container
image: nginx:latest
ports:
- containerPort: 80
关键要点: Pod 是应用程序容器的逻辑主机,是被调度到 Node 上的单元。
核心关系:调度与放置
Pod 和 Node 之间的基本交互由 Kubernetes 调度器控制,它位于控制平面中。
Pod 如何落在 Node 上
- Pod 创建: 用户向 API 服务器提交 Pod 的 YAML 定义(或更高级别的对象,如 Deployment,它会创建 Pod)。
- 调度决策: 调度器根据资源请求、约束和可用容量,识别运行该 Pod 的最佳可用 Node。
- 绑定: 一旦选择了 Node,Pod 就会绑定到该特定 Node。
- 执行: 分配 Node 上的 Kubelet 注意到新的 Pod 分配,拉取必要的镜像并启动容器。
关键点: 一旦 Pod 被调度到 Node 上,它会一直停留在该 Node 上,直到被终止、永久崩溃或 Node 失败。Kubernetes 通常不会在 Node 之间迁移正在运行的 Pod。
| 特性 | Kubernetes Node | Kubernetes Pod |
|---|---|---|
| 角色 | 提供物理/虚拟计算资源。 | 运行一个或多个应用程序容器。 |
| 范围 | 集群基础设施级别。 | 应用程序工作负载级别。 |
| 调度单元 | 从调度器接收 Pod。 | 被调度到 Node 上的单元。 |
| 组件 | Kubelet、容器运行时、Kube-proxy。 | 应用程序容器、共享卷、共享 IP。 |
| 数量 | 每个集群通常几个到多个。 | 根据工作负载,可以是数百或数千个。 |
实际部署示例
想象一个具有以下 Deployment 的 Web 应用程序:
apiVersion: apps/v1
kind: Deployment
metadata:
name: storefront
spec:
replicas: 3
selector:
matchLabels:
app: storefront
template:
metadata:
labels:
app: storefront
spec:
containers:
- name: web
image: example/storefront:v1
ports:
- containerPort: 8080
这不会创建三个 Node。它会创建三个 Pod 副本。调度器然后决定哪些现有 Node 应该运行这些 Pod。在一个小型集群中,你可能会看到:
kubectl get pods -o wide
NAME READY STATUS NODE
storefront-6d8f8c7f9b-2xk9p 1/1 Running worker-node-01
storefront-6d8f8c7f9b-7hxm4 1/1 Running worker-node-02
storefront-6d8f8c7f9b-q4zpt 1/1 Running worker-node-02
有三个 Pod,但只涉及两个 Node。如果 worker-node-02 失败,两个副本会同时消失。Deployment 会尝试替换它们,但需要健康的 Node 容量才能做到。这就是为什么即使你的 Deployment 有多个副本,Node 放置也很重要。
如果你希望 Kubernetes 将副本分散到多个 Node 上,请使用拓扑分布约束或 Pod 反亲和性。对于每个小型内部工具,你不需要这样做,但对于面向客户的服务,你应该考虑这一点,这样单个 Node 故障不会移除大部分容量。
Pod 是可丢弃的;Node 是受管理的容量
健康的 Kubernetes 思维是 Pod 是可替换的。Deployment、ReplicaSet、StatefulSet、Job 和 DaemonSet 会不断创建和替换 Pod。带有随机后缀的 Pod 名称不应被永久依赖。
在运行良好的集群中,Node 也是可替换的,但它们是容量单元。它们需要操作系统补丁、kubelet 健康、足够的磁盘空间、工作的容器运行时和网络连接。当 Node 出现问题时,许多 Pod 会同时受到影响。
这种差异体现在调试方式上:
- 如果一个 Pod 失败,而同一 Node 上的其他 Pod 健康,请从 Pod 的日志、配置、探针、镜像和资源限制开始检查。
- 如果同一 Node 上的许多不相关 Pod 失败,请从 Node 开始检查:磁盘压力、内存压力、kubelet 状态、CNI 健康以及容器运行时日志。
- 如果新 Pod 无法在任何地方启动,请检查集群范围的容量、配额、调度规则和控制平面健康。
常见误解
第一个误解是认为 Pod 等同于容器。大多数 Pod 包含一个主容器,所以这种简化感觉无害。但边车使区别变得重要。服务网格代理、日志收集器或辅助容器可以与应用程序运行在同一 Pod 中。它们共享 Pod 网络命名空间,因此这些容器之间的 localhost 在该 Pod 内有效。
第二个误解是认为 Kubernetes 像实时 VM 迁移一样将正在运行的 Pod 移动到另一个 Node。它通常不会这样做。如果 Node 被耗尽或失败,Kubernetes 会终止或丢失旧 Pod,并在其他地方创建替换 Pod。该替换具有不同的 Pod 身份,并且旧 Pod 的任何本地容器文件系统更改都会丢失,除非它们被写入持久存储。
第三个误解是假设 Service 运行在 Node 上。Service 是 Pod 前面的稳定网络抽象。它通过标签选择 Pod 并将流量路由到它们的 IP。Node 提供这些 Pod 运行的位置,但 Service 本身并不像 Pod 那样“位于”某个工作节点上。
这对日常设计的影响
当你选择副本数量时,你是在选择 Kubernetes 应尝试保持运行的 Pod 副本数量。你并不是在选择将存在多少个 Node。具有十个副本的 Deployment 仍然可能将多个 Pod 放在同一 Node 上,除非你添加分布规则并且集群有足够的 Node 容量。
当你选择 Node 大小时,你是在选择容量池的形状。少数大型 Node 可能效率高,但一个 Node 故障会移除更大比例的运行 Pod。更多小型 Node 可以改善故障隔离,但会增加开销并可能使装箱效率降低。没有通用答案;正确的选择取决于工作负载大小、可用性需求和成本。
有状态工作负载增加了另一个复杂性。StatefulSet 仍然创建 Pod,这些 Pod 仍然在 Node 上运行,但每个 Pod 可能具有稳定的身份和持久卷。如果 Node 死亡,Kubernetes 可以在其他地方重新创建 Pod,但存储层必须支持从新位置附加或访问该卷。
最佳实践和故障排除见解
理解这种架构有助于实际的集群管理:
资源管理
- 资源请求/限制: 始终在 Pod 规范中定义资源
requests和limits。这允许调度器准确地将 Pod 匹配到具有足够容量的 Node,防止资源争用。 - Node 压力: 如果 Node 变得不堪重负(磁盘空间或内存不足),Kubelet 会报告此状况。Kubernetes 可能会驱逐该 Node 上的 Pod 以维持稳定性。
高可用性 (HA)
- 冗余: 为了实现 HA,你必须运行多个 Pod 副本(由 Deployment 或 StatefulSet 管理)。调度器会尝试将这些副本放置到不同的 Node 上,以确保单个 Node 的故障不会导致整个应用程序宕机。
故障排除
当应用程序无法启动时:
- 检查 Pod 状态: 使用
kubectl describe pod <pod-name>。查看 'Events' 部分,了解 Pod 被调度到哪个 Node。 - 检查 Node 状态: 如果 Pod 卡在
Pending状态,问题通常与调度相关(例如,没有 Node 满足所需约束)。如果 Pod 正在运行但失败,请检查其所在 Node 上的 Kubelet 日志。
有用的命令:
kubectl get pods -o wide
kubectl describe pod <pod-name>
kubectl get nodes
kubectl describe node <node-name>
-o wide 输出被低估了。它显示每个 Pod 落在哪个 Node 上,这通常是区分应用程序问题与基础设施问题的线索。
简短总结
Node 回答“这可以在哪里运行?” Pod 回答“什么应该一起运行?” 调度器通过将 Pod 放置到 Node 上来连接这两个概念。一旦你理解了这种关系,Kubernetes 错误就变得更容易解读:挂起的 Pod 通常是放置问题,失败的 Pod 通常是工作负载问题,而不健康的 Node 可能同时导致许多 Pod 问题。