Kubernetes Pod 是 Kubernetes 应用的基本执行单元。可以把它想象成应用程序运行的独特环境,封装了一个或多个应用容器以及共享的存储/网络资源。Kubernetes 有很多封装服务、端点和其他实体的概念,但归根结底一个 Pod 是你的代码运行的地方。
从概念上来说,Pod 可以和 Docker Compose 中的容器进行比较。在与 Docker Compose 相比时,Pod 在 Kubernetes 中扮演的角色与容器在 Docker Compose 中扮演的角色相同,但 Pod 实际上是一种对一个或多个容器的抽象,具有相关的网络和存储配置。Pod 可以包含几个容器,但是最佳实践是有一个运行应用代码的主容器,以及 0 个或多个提供应用额外功能(如日志、监控和网络)的支持容器,这种方式称为 Sidecar,这些支持容器称为 sidecar 容器。
节点是 Kubernetes 中的工作器,Pod 就是在节点上运行。节点可以是虚拟的,例如 AWS EC2 实例,或者物理的计算机服务器。Pod 被分配到节点上,并在这些节点上运行,消耗节点提供给集群的容量。Pod 并非明确绑定到节点,它们是由 Kubernetes 控制平面分配的,如果有必要可以从一个节点移动到另一个节点。
集群本质上是一组提供容量的节点、一组运行应用程序的 Pod 以及其他配置(如服务或入口控制器),所有这些都由 Kubernetes 控制平面管理。从概念上讲,Pod 可以被视为 Kubernetes 运行应用程序的方式。
Pod 是在主容器(运行我们代码的容器)之上的一层抽象,以及零个或多个支持或 sidecar 容器。除了容器之外,Pod 在 Kubernetes 集群中还有一个身份和几个配置。
Pod 在其生命周期中可以经历几个阶段:
Pending:系统已接受该 Pod,但一个或多个容器尚未设置和运行。
Running:该 Pod 已绑定到一个节点,其所有容器都已创建。
Succeeded:Pod 中的所有容器已成功终止并不会重启。该 Pod 不再绑定到一个节点和消耗资源。
Failed:Pod 中至少有一个容器以失败状态终止。该 Pod 不再绑定到一个节点和消耗资源。
Unknown:当由于与 Pod 主机节点的某些通信问题导致状态不明确时。当一个节点无法向 Kubernetes 控制平面报告其状态时,在该节点上运行的 Pod 会进入 Unknown 状态。
一个 Pod 可以封装多个容器,确保它们共享相同的存储和网络命名空间。这使我们可以将应用程序帮助进程与主应用程序耦合在一起,而不必处理它们之间的网络配置。在同一个 Pod 中运行的容器共享本地网络和存储。这通常用于 sidecar 容器,例如日志记录、监控或网络配置。例如,这样配置一个具有多个容器的 Pod:
apiVersion: v1 kind: Pod metadata: name: myapp-pod labels: ? app: myapp spec: containers: - name: myapp-container ? image: busybox ? command: ['sh', '-c', 'echo The app is running! && sleep 3600'] - name: log-container ? ? ? image: busybox ? command: ['sh', '-c', 'tail -f /dev/null']
这将在一个 Pod 中运行两个容器,一个运行应用,另一个记录日志。它们共享网络和文件系统。这就是 Pod 的强大之处。
Kubernetes 是一个复杂的平台,运行生产负载需要比只定义一个包含容器的 Pod 更多的知识。以下是有效管理 Pod 需要理解的一些额外知识点。
直接更新正在运行的 Pod 不是一个好的实践,因为它违反了 Pod 的不变性假设(本质上与容器的不变性相同)。事实上,Kubernetes 在 Pod 层面上执行这种不变性,这意味着它拒绝对 Pod 进行更新。相反,我们应该部署 Pod 的新版本,并平滑地将流量重定向到较新版本。如果这种部署是逐步进行的,每次替换一个 Pod(当为同一应用程序运行多个 Pod 时相关),这称为滚动更新。如果部署一组全新的 Pod,将流量重定向到它们,验证它们是否正常工作,然后才终止旧的 Pod,这称为蓝绿部署。
当创建一个 Deployment,其中包含一组 Pod 时,可以定义更新策略。例如,以下是如何定义滚动更新策略:
apiVersion: apps/v1 kind: Deployment metadata: name: myapp-deployment spec: replicas: 3 strategy: ? type: RollingUpdate ? rollingUpdate: ? ? maxSurge: 1 ? ? maxUnavailable: 0 ? template: ? ...
这将逐步更新 Pod,每次更新一个,同时保持可用 Pod 数量至少为 2。
可以为 Pod 定义 CPU 和内存请求和限制,以防止 Pod 消耗太多节点资源。例如:
resources: requests: ? memory: "64Mi" ? cpu: "250m" limits: ? memory: "128Mi" ? cpu: "500m"
这将请求 64MB 内存和 0.25 CPU 核心,并限制为 128MB 内存和 0.5 CPU 核心。了解这些概念将帮助我们在生产中安全可靠地运行 Pod。
Pod 仅具有短暂的存储,这是通过工作内存实现的。可以使用 Persistent Volume 来创建持久存储,并通过 Persistent Volume Claim 与 Pod 关联。从概念上讲,持久卷可以与节点进行比较:它们使底层资源(在这种情况下是存储,而不是计算)可用于 Kubernetes 集群。Persistent Volume Claim 为 Pod 预留这些可用资源,与它们相关联。这些 Persistent Volume Claim 可以作为卷关联到 Pod 内的容器,并作为容器本地文件系统的一部分进行访问。重要的是,它们可以由 Pod 中的所有容器共享,允许在同一 Pod 中基于文件在容器之间进行通信。日志 sidecar 容器通常使用这种方法来从主容器读取日志,并将其导出到像 AWS CloudWatch Logs 这样的外部日志聚合器。
下面是如何定义一个 Persistent Volume Claim,并将其作为卷关联到 Pod 中的一个容器:
apiVersion: v1 kind: Pod metadata: name: mypod spec: containers: ? - name: mypod ? ? image: busybox ? ? command: ['sh', '-c', 'echo Hello Kubernetes! > /mnt/vol1/hello-file'] ? ? volumeMounts: ? ? ? - mountPath: /mnt/vol1 ? ? ? ? name: vol1 volumes: ? - name: vol1 ? ? persistentVolumeClaim: ? ? ? claimName: my-pvc
这将在 /mnt/vol1 路径挂载 PVC my-pvc,容器可以在其中读写文件。使用持久卷可以确保Pod重新启动时数据不会丢失。
每个 Pod 都在整个集群中分配一个唯一的 IP 地址,可以从集群内部访问该地址。还可以定义服务,它允许通过单个 IP 地址或专用 DNS 名称寻址同类 Pod 组(通常是 Deployment),并在这些 Pod 之间负载均衡流量。还可以定义 Ingress 来通过 Ingress Controller 将服务暴露到集群外部。Pod 之间默认是可以相互通信的,不需要额外的网络配置。但是,有时可能需要进一步隔离 Pod 网络。这可以通过 Kubernetes 网络策略来实现,它允许根据标签选择器控制 Pod 之间的流量。
例如,以下网络策略只允许具有 role=frontend 的 Pod 访问具有 role=backend 的 Pod:
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: backend-policy spec: podSelector: ? matchLabels: ? ? role: backend ingress: - from: ? - podSelector: ? ? ? matchLabels: ? ? ? ? role: frontend
理解 Pod 网络对于在 Kubernetes 中运行基于网络的应用程序非常重要。主流的网络模型包括 Flannel、Calico、Cilium 等。
Kubernetes是一个非常强大的平台,但这种强大也带来了巨大的复杂性。Pod只是一个起点,但理解它们的工作方式对于掌握Kubernetes在Pod之上用于部署生产级负载的抽象和配置是必要的。
理解Pod的基础知识,比如它们的生命周期、如何管理多个容器、存储、资源限制等,是开始使用Kubernetes的关键第一步。一旦你理解了这些基础知识,你就可以构建更复杂的应用程序部署,利用Kubernetes提供的服务发现、负载均衡、滚动更新等高级功能。