Pod概念
Pod 是可以在 Kubernetes 中创建和管理的、最小的可部署的计算单元。
Pod是一组(一个或多个)容器;这些容器共享存储、网络、以及怎样运行这些容器的规约。Pod 中的内容总是并置(colocated)的并且一同调度,在共享的上下文中运行。Pod 所建模的是特定于应用的“逻辑主机”,其中包含一个或多个应用容器,这些容器相对紧密地耦合在一起。
核心特性
- 共享上下文:Pod 的共享上下文包括一组 Linux 名字空间、控制组(CGroup)和可能一些其他的隔离方面。在 Pod 的上下文中,各独立的应用可能会进一步实施隔离。
- 逻辑主机:Pod 类似于共享名字空间并共享文件系统卷的一组容器。
- 容器运行时:为了运行 Pod,你需要提前在每个节点安装好容器运行时(如 Containerd 或 Docker)。
两种主要用法
单容器 Pod
最常见的 Kubernetes 使用模式,将 Pod 视为单个容器的包装器。Kubernetes 直接管理 Pod 而不是容器本身。
多容器 Pod
Pod 可以封装由紧密耦合且需要共享资源的多个并置容器组成的应用。这些位于同一位置的容器构成一个内聚的服务单元。
将多个并置、同管的容器组织到一个 Pod 是一种相对高级的使用场景。横向扩展应通过副本机制实现,而非在同一 Pod 中添加多个容器。
Pod 示例
下面是一个 Pod 示例,它由一个运行镜像 nginx:1.14.2
的容器组成。
# simple-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
要创建上面显示的 Pod,运行以下命令:
kubectl apply -f simple-pod.yaml
通过工作负载资源管理 Pod
使用工作负载资源来创建和管理 Pod。Pod 被设计成了相对临时性的、用后即抛的一次性实体。
直接创建 Pod 的问题
- Pod 是临时性的,节点故障会导致 Pod 丢失
- 缺乏扩缩容能力
- 没有自动修复机制
- 无法实现滚动更新
常见的工作负载资源
- Deployment:适用于管理无状态应用,支持滚动更新和回滚。
- StatefulSet:适用于管理有状态应用,提供稳定的网络标识和持久存储。
- DaemonSet:确保所有(或部分)节点上运行一个 Pod 的副本。
- Job:用于运行一次性任务。
- CronJob:用于运行周期性任务。
这些资源的控制器能够处理副本的管理、上线,并在 Pod 失效时提供自愈能力。
Deployment示例
**apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.25.3
ports:
- containerPort: 80**
Pod 模板
工作负载资源的控制器使用 Pod 模板 来创建和管理 Pod。Pod 模板是包含在工作负载对象中的规范,是目标状态的一部分。
apiVersion: batch/v1
kind: Job
metadata:
name: hello
spec:
template:
# 这里是 Pod 模板
spec:
containers:
- name: hello
image: busybox:1.28
command: ['sh', '-c', 'echo "Hello, Kubernetes!" && sleep 3600']
restartPolicy: OnFailure
# 以上为 Pod 模板
重要:修改 Pod 模板不会对已经存在的 Pod 直接起作用。控制器会根据新模板创建新的 Pod 来替换旧的 Pod。
Pod 生命周期
Pod 被调度到节点上后会一直保持运行,直到:
- Pod 中的容器执行完毕。
- Pod 对象被删除。
- Pod 因资源不足而被驱逐。
- 节点失效。
生命周期阶段
- Pending(挂起):Pod 已被 Kubernetes 系统接受,但有一个或多个容器尚未创建或运行。
- Running(运行中):Pod 已绑定到一个节点,所有容器都已创建。至少有一个容器仍在运行,或者正处于启动或重启过程中。
- Succeeded(成功):Pod 中的所有容器都已成功终止,并且不会再重启。
- Failed(失败):Pod 中的所有容器都已终止,并且至少有一个容器是因为失败终止。
- Unknown(未知):通常是由于无法与 Pod 所在节点的 kubelet 通信所致。
重启策略
通过 spec.restartPolicy
字段控制
Always
:容器失败时总是重启(默认)。OnFailure
:容器以非零状态退出时重启。Never
:从不重启。
Init 容器
Pod 可以包含在应用容器启动前运行的 Init 容器。Init 容器总是运行到完成为止,并且每个 Init 容器都必须成功启动,下一个 Init 容器才能运行。如果 Init 容器失败,Kubernetes 会不断地重启 Pod,直到 Init 容器成功为止。
容器探针
-器进程还在运行,并不代表应用是健康的。它可能死锁、可能正在启动(但还未就绪)、可能依赖的服务不可用。探针让 Kubernetes 能够感知应用内部的真实状态,并做出相应决策。
探针类型
livenessProbe
:检测容器是否正在运行。如果失败,kubelet 会杀死容器,然后根据重启策略进行处理。readinessProbe
:检测容器是否已准备好服务请求。如果失败,端点控制器将从与 Pod 匹配的所有 Service 的端点中移除该 Pod 的 IP 地址。startupProbe
:指示容器内的应用是否已启动。如果提供了启动探针,则所有其他探针都会被禁用,直到它成功为止。
探针的三种动作
ExecAction
:在容器内执行指定命令。- 如果命令退出码为 0,则认为诊断成功。
- 配置 :
livenessProbe: exec: command: - cat - /tmp/healthy
TCPSocketAction
:对指定端口上的容器 IP 地址进行 TCP 检查。- 如果端口能连通,则认为诊断成功。
- 配置 :
livenessProbe: tcpSocket: port: 8080
HTTPGetAction
:对指定端口和路径上的容器 IP 地址执行 HTTP Get 请求。- 如果响应的状态码在 200 到 399 之间,则认为诊断成功。
- 配置 :
livenessProbe: httpGet: path: /healthz port: 8080 httpHeaders: - name: Custom-Header value: Awesome
Pod 的资源共享和通信
存储
- 一个 Pod 可以设置一组共享的存储卷(Volume)。Pod 中的所有容器都可以访问该共享卷,从而允许这些容器共享数据。卷还允许 Pod 中的持久数据保留下来,即使其中的容器需要重新启动。
- 容器中的文件系统是临时的,容器重启后文件会丢失。
卷
卷是一个目录,可以被 Pod 中的一个或多个容器访问。卷的生命周期不属于容器,它由 Pod 和外部存储源决定。
类型
- emptyDir
- 生命周期:与 Pod 的生命周期完全一致。Pod 被删除,数据也随之删除。
- 用途:用于 Pod 内容器之间的临时数据共享(例如,一个容器生产数据,另一个容器消费数据)。节点重启后数据也会丢失。
- hostPath:
- 生命周期:将节点上的文件系统目录或文件挂载到 Pod 中。数据持久化在节点上,即使 Pod 被删除,数据依然存在。
- configMap / secret:
- 生命周期:将 Kubernetes 对象(ConfigMap/Secret)中的数据作为文件或环境变量注入到容器中。
- 持久卷 (persistentVolumeClaim - PVC):
- 生产环境的标准做法。
- 生命周期:独立于 Pod。Pod 被删除后,数据依然安全地保存在外部存储系统中(如云硬盘、NFS、Ceph 等)。
- 工作原理:管理员预先创建持久卷 (PersistentVolume, PV):代表一块实际的网络存储。用户创建持久卷声明 (PersistentVolumeClaim, PVC):像一个“申请单”,声明需要多大的存储、需要什么访问模式(如 ReadWriteOnce)。在 Pod 中通过 volumes.persistentVolumeClaim 字段绑定 PVC。
PVC示例
apiVersion: v1
kind: Pod
metadata:
name: my-app-with-db
spec:
containers:
- name: app
image: my-app:latest
volumeMounts:
- name: data-storage # 挂载名为 data-storage 的卷
mountPath: /var/data # 挂载到容器内的路径
volumes:
- name: data-storage # 定义卷的名称
persistentVolumeClaim:
claimName: my-pvc # 指定要使用的PVC名称
网络
- 每个 Pod 都在每个地址族中获得一个唯一的 IP 地址。
- Pod 中的每个容器共享网络名字空间,包括 IP 地址和网络端口。
- Pod 内的容器可以使用
localhost
互相通信。 - 不同 Pod 中的容器具有不同的 IP 地址,需要通过 Pod IP 进行通信。
网络实现细节:
暂停容器 (Pause Container):
- 当创建一个 Pod 时,Kubernetes 会先创建一个名为
pause
的极小容器。它的唯一作用是持有 Pod 的网络命名空间 (Network Namespace)。 - Pod 内的其他容器(用户容器)会通过 Docker 的
--net=container:<id>
参数加入到这个pause
容器的网络命名空间中,从而实现网络共享。
- 当创建一个 Pod 时,Kubernetes 会先创建一个名为
容器间通信:
- 在同一个 Pod 内,容器可以通过
localhost
和端口号直接通信,就像在同一台机器上一样。
- 在同一个 Pod 内,容器可以通过
Pod 间通信:
- Pod-to-Pod:通过对方的 Pod IP 直接通信。这是最基础的通信方式,但 Pod IP 是临时的,Pod 重启后会改变。
- Service:为了解决 Pod IP 不固定的问题,Kubernetes 引入了 Service。Service 是一个稳定的抽象层,它有一个固定的 虚拟 IP (VIP) 和 DNS 名称。Service 通过标签选择器 (Selector) 将流量负载均衡到一组后端 Pod 上。
- ClusterIP:默认类型,在集群内部暴露 Service。
- NodePort:通过每个节点的静态端口暴露 Service。
- LoadBalancer:使用云提供商的负载均衡器对外暴露 Service。
- Ingress:用于管理外部访问集群内服务的 HTTP/HTTPS 路由规则,提供更高级的功能如基于域名和路径的路由、SSL 终止等。它本身不是一种 Service,而是位于 Service 之前的入口控制器。
示例:网络访问流程
外部用户 -> (通过 LoadBalancer/NodePort) -> Service (VIP) -> (通过 iptables/IPVS 规则) -> 目标 Pod (在某个节点上)。