Kubernetes - 工作负载:Pods

Table of Contents

https://kubernetes.io/docs/concepts/workloads/pods/

Pods 是 Kubernetes 中你可以创建和管理的最小的可部署计算单元。

Pod 是一组一个或者多的容器,他们之间共享存储和网络资源,以及如何运行容器的规范(Spec)。Pods 中的内容始终在同一个位置和同时被调度,在共享的上下文中运行。 Pod 为特性应用程序的『逻辑主机』建模:它包含一个或者多个紧密耦合的应用程序容器。在非云环境中,在同一个物理或者虚拟机器上运行的应用程序类似于在云环境下相同的逻辑主机。

除了应用程序之外,Pod 还可以包含 Pod 启动过程中运行的 init containers。如果集群提供此功能,还可以注入 临时容器 来调试。

1. 什么是 Pods?

注意: 尽管 Kubernetes 不仅支持 Docker,还支持更多的容器运行时,但是 Docker 是最常见的运行时,Docker 中的一些术语可以更好的描述 Pods。

Pod 的共享上下文是一组 Linux namespaces,cgroups 和其它隔离方面的集合 - Docker 容器也隔离了相同的东西。在 Pod 的上下文中,各个应用程序 可能还会使用其它子隔离。

就 Docker 的概念而言,一个 Pod 类似于一组具有共享 namespaces 和共享文件系统卷的 Docker 容器。

2. 使用 Pods

通常情况下,你不需要直接或者单独创建 Pods。而是使用工作负载资源(类似 Deployment,Job 等)间接的创建它们。 如果你的 Pod 需要跟踪状态,考虑使用 StatefulSet 资源。

Kubernetes 中的 Pods 有两种主要的使用方式:

  • Pod 运行单个容器。 『一个 Pod 一个容器』的模式是 Kubernetes 中最常见场景;这种情况下,你可以认为 Pod 是单个容器的一层包装; Kubernetes 管理 Pods 而不是直接管理容器;
  • Pod 运行多个需要相互之间协作的容器。 一个 Pod 可以封装一个应用程序,该应用程序由紧密关联的且需要共享资源的多个位于同一个地方的容器组成。 这些在同一个地点的容器构成了一个单一的内聚服务单元 - 比如,一个容器在公共存储卷中提供存储数据,同时使用单独的 sidecar 容器刷新或者更新这些文件。 Pod 包装了这些容器,存储资源和临时的网络标识一起作为一个单元;

    注意: 在一个 Pod 中管理多个容器是比较高级的使用方法。仅应该在容器紧耦合的情况下使用。

每个 Pod 旨在运行应用程序的单个实例。如果想要水平扩容(运行更多的实例在提供更多的整体资源),你应该使用多个 Pods,一个表示一个实例。 在 Kubernetes 中,这通常称为 replication 。Replicated Pod 通常由工作负载资源和它的控制器来管理。

有关 Kubernetes 如何使用工作负载资源和它们的控制器的更多信息查看下面的 Pods 和控制器,实现应用程序的扩容和自动修复。

2.1. Pod 如何管理多个容器

Pod 设计旨在支持多个协作进程(作为容器),协作进程组合一个紧密联系的服务单元。Pod 中的容器会自动位于同一集群中的同一物理机和虚拟机上,并在 同一个物理机和虚拟机上共同调度。容器可以共享资源和依赖关系,彼此通信,并协调何时以及如何终止它们。

比如,你可能有一个充当共享卷中的 Web 服务器的容器,以及一个单独的『sidecar』容器,该容器从远程源中更新这些文件,如下所示:

pod.svg

Figure 1: Pod

一些 Pods 有 init 容器和应用容器。init 容器在应用容器运行之前运行完成。

Pod 在本地提供了两种共享资源:网络存储

3. 与 Pods 一起工作

你很少会直接在 Kubernetes 中单独创建 Pods。这是因为 Pod 被设计为相对短暂的一次性实体。当 Pod 被创建时(你直接创建或者通过控制器创建), 新的 Pod 会在集群中被调度运行。Pod 会一直保留在该节点上,直到 Pod 执行完毕,删除 Pod 对象,或者由于资源紧缺将 Pod 驱逐,又或者节点发生故障。

注意: 在 Pod 中重启容器不应该和重启 Pod 混淆。Pod 不是进程,而是一个容器运行的环境。Pod 一直存在直到被删除。

当你创建 Pod 对象的清单(manifest),确保名字是有效的 DNS 子域名 格式。

3.1. Pods 和控制器(controllers)

你可以使用工作负载资源创建和管理你的 Pods。资源的控制器处理复制和推出(rollout)和 Pod 失败情况下自愈。比如说,节点挂了,控制器会注意到节点上的 Pods 已经停止工作了,之后会创建一个新的 Pod 替换。调度器会把新 Pod 放置到一个健康的节点上。

以下是管理一个或者多个 Pod 工作负载的示例:

  • Deployment
  • StatefulSet
  • DaemonSet

3.2. Pod 模板

工作负载的资源控制器通过 pod template 来创建 Pods,然后代表你来管理这些 Pods。

PodTemplates 是创建 Pod 的规范(Spec),并且包含在工作负载资源中,比如 DeploymentJobsDaemonSets

每个控制器通过 PodTemplate 字段来创建实际的 Pods。 PodTemplate 定义工作负载资源的期望状态。

下面是一个简单 Job 的 template 清单,会启动一个容器。该容器中会打印一条消息,然后暂停。

apiVersion: batch/v1
kind: Job
metadata:
  name: hello
spec:
  template:
    # This is the pod template
    spec:
      containers:
      - name: hello
        image: busybox
        command: ['sh', '-c', 'echo "Hello, Kubernetes!" && sleep 3600']
      restartPolicy: OnFailure
    # The pod template ends here

修改 Pod 模板或者切换到新的 Pod 模板不会对已经存在的 Pod 直接影响。但如果你修改工作负载资源的 Pod 模板,则该资源需要重新创建新的 Pods 替换掉老的 Pods。

比如,StatefulSet 控制器保证正在运行的 Pods 与当前每一个 StatefulSet 的 pod 模板对象相匹配。如果修改 StatefulSet 的 pod 模板, StatefulSet 会基于新的模板创建新的 Pods。最终,所有旧的 Pods 被新的 Pods 取代,才算更新完成。

每个工作负载资源都实现自己的规则来处理 Pod 模板的更改。如果你想要了解更多的 StatefulSet 说明,查看 StatefulSet 基本教程中的 更新策略

在节点上,kubelet 不直接观察(observe)或者管理有关 Pod 模板和更新的任何信息;这些细节被抽象掉了。关注简化了系统语义的抽象和分离, 并可以在不更改现有代码的情况下扩展集群行为。

4. Pod 的更新和替换

如上一节所述,当工作负载资源的 Pod 模板被修改之后,控制器基于新模板创建新的 Pods 而不是更新或者修补(patching)已有的 Pod。

Kubernetes 不会阻止你直接管理 Pods。可以对直接运行的 Pod 更新某些字段。但是,Pod 的更新操作,类似 patchreplace 有一些限制:

  • Pod 的大部分元数据是不可变的。比如,你不能修改 namespace name uid 或者 creationTimestamp 字段; generation 字段是唯一的。 它仅接收会增加字段当前值的更新;
  • 如果 metadata.deletionTimestamp 设置了,不能将新条目添加到 metadata.finalizers 列表;
  • Pod 更新可能不会修改除以下以外的其他字段 spec.containers[*].images, spec.initContainers[*].image, spec.activeDeadlineSeconds 或者 spec.tolerations 。对于 spec.tolerations ,你只能添加新条目;
  • 当更新 spec.activeDeadlineSeconds 字段的时候,允许两种类型的更新:
    1. 将未分配字段设置为正数;
    2. 将字段从正数更新为较小的非负数。

5. 资源共享和通信

Pod 支持在内部容器之间数据共享和通信。

5.1. 存储在 Pods 中

Pod 可以指定一组共享存储卷。在 Pod 中的所有容器可以访问共享卷,允许这些容器来共享数据。卷还允许 Pod 中持久化数据保留下来,以防其中有一个容器重新启动。 有关 Kubernetes 如何实现共享存储并将其提供给 Pods 的更多信息,参见 存储

5.2. Pod 网络

每个 Pod 会被分配一个唯一的 IP 地址。Pod 中的容器共享网络命名空间,包括 IP 地址和网络端口。在 Pod 内部(也只有在内部),容器相互之间可以通过 localhost 来访问。当 Pod 中的容器与 Pod 外部的实体进行通信时,他们必须协调如何使用共享的网络资源(比如端口)。在 Pod 内部,容器共享一个 IP 地址和端口空间,并且可以直接通过 localhost 找到彼此。Pod 中的容器还可以通过标准的进程间通信(比如 SystemV 信号量和 POSIX 共享内存)。 在不同 Pods 之间的容器有不同的 IP 地址,如果没有 特殊配置,不可以使用 IPC 通信(可以使用 IP 网络进行通信)。

在 Pod 中的容器看到的系统主机名与 Pod 配置的 name 相同,更多查看 网络 小结。

6. 容器的特权模式

在 Pod 中的任何容器都可以开启特权模式,在容器规范(spec)的安全上下文中使用 privileged 标记。这对于想要使用操作系统管理功能(比如操作网络堆栈 或访问硬件设备)的容器很有用。特权容器内的进程获得的特权几乎与容器外的进程相同。

注意: 你的容器运行时必须支持特权容器的概念才能使把这个设置关联起来。

7. 静态 Pods

静态 Pods 由指定节点上的 kubelet 守护进程直接管理,无需 API server 对其进行观察(observing)。尽管大多数 Pod 由控制平面(比如,Deployment) 管理,但是对于静态 Pods,kubelet 会直接监督每个静态 Pod(失败之后重启)。

静态 Pods 始终绑定到特定节点的一个 kubelet 上。静态 Pod 的主要用途是运行自托管的控制平面:换句话说,使用 kubelet 来监督各个 控制平面组件

kubelet 自动尝试在 Kubernetes API server 上为每个静态 Pod 创建一个镜像 Pod。这意味着节点服务器上运行的 Pod 在 API server 上可见,但无法从节点 进行控制。

8. 下一步

理解为什么 Kubernetes 在其他资源(比如 StatefulSets 或者 Deployments)包装了 Pod API 的上下文,可以阅读现有技术的信息,包括:

First created: 2021-04-21 17:13:12
Last updated: 2022-12-11 Sun 12:49
Power by Emacs 29.0.91 (Org mode 9.6.6)