在Kubernetes中,一个重要的概念就是Pod(豆荚),Kubernetes并不是直接管理容器的,他的最小管理单元叫Pod。
在docker的应用中,我们把一个应用程序封装在一个镜像中,之后启动这个镜像并映射一个宿主机端口号,就能访问这个应用。既然单个容器可以部署这个应用,为什么Kubernetes不直接管理容器,还需要设计一个Pod呢?
一:什么是Pod
Pod是一个或多个容器的组合。这些容器共享存储、网络和命名空间,以及运行规范。在Pod中,所有容器都被统一安排和调度,并运行在共享的上下文中。对于具体应用而言,Pod是它们的逻辑主机,Pod包含业务相关的多个应用容器。所以,Pod是一组具有共享命名空间、IP地址和端口的容器的集合。
备注:
共享上下文是一种基于线程的内存位置
1:从使用的角度来看
在实际的使用时,单个容器是无法单独来支撑我们的应用的,往往需要很多微服务才能组成个系统,并且还会存在A服务依赖B服务,B服务需要和C服务共用某个目录。另外,在使用裸容器时,很难实现对容器内进行健康检查以及横向扩容等操作,而Pod可以轻松解决这些问题。
2:从Kubernetes的角度来看
Docker只是容器Runtime(运行时)的一种们还有很多容器Runtime,比如Rkt、CRI-0等,而Kubernetes作为目前最流行的容器编排工具,需要支持各个Runtime并且不依赖于底层Runtime的实现技术,于是就抽象出了Pod这个概念,用于管理多个紧密相连的符合CRI标准的容器。
Pod可以简单的理解为一组、一个或多个容器,每个Pod还包含一个Pause容器,Pause容器是Pod的父容器,主要负责户进程的回收管理。同时,通过Pause容器可以使同一个Pod里面的不同容器共享存储、网络、PID、IPC等,容器之间可以使用LocalhostPort的方式相互访问,可以使用volume实现数据共享。根据Docker的构造,Pod可以被创建为一组具有共享命名空卷、IP地址和端口的容器
3.Pod有两个必须知道的特点:
网络:每一个Pod都会被指派一个唯一的Ip地址,在Pod中的每一个容器共享网络命名空间,包括Ip地址和网络端口。在同一个Pod中的容器可以同locahost进行互相通信。当Pod中的容器需要与Pod外的实体进行通信时,则需要通过端口等共享的网络资源
存储:Pod能够被指定共享存储卷的集合,在Pod中所有的容器能够访问共享存储卷,允许这些容器共享数据。存储卷也允许在一个Pod持久化数据,以防止其中的容器需要被重启
二.Pod的管理
(1) kubectl命令创建pod
ku run pod名 --image=镜像名 --labels="app=nginx"
(2)查看pod状态
kubectl get pod
//列出 Kubernetes 集群中指定命名空间内的所有 Pod
kubectl get pod -n 命令空间
(3)显示Pod的更多信息
kubectl get pod pod名称 -o wide
备注:-o wide
选项会提供更详细的输出,包括节点名称、IP 地址、容器状态等。
(4)查看pod日志
ku logs nginx(pod名)
(5)以yaml格式显示Pod详细信息
ku get pod nginx -o yaml
(6)显示资源的详细描述信息
ku describe pod nginx(pod名)
备注:
- kubectl get:常用于查看同一资源或多个资源对象,可以使用-o参数自定义输出格式
- kubectl describe:侧重于描述指定资源的各方面的详细信息,不仅会返回节点信息,还会返回在其上运行的pod的摘要,节点事件等信息。
(7)在Pod的容器中执行命令
ku exec nginx -c nginx -- date
备注:-c:指定Pod中容器的名字
(8)登录到Pod中的容器中
ku exec -it nginx -c nginx -- bash
(9)在线编辑运行中的资源对象
kudectl edit pod nginx
(10)将pod的端口映射到宿主机
ku port-forward --address 0.0.0.0 pod/nginx 8080:80
Forwarding from 0.0.0.0:8080 -> 80
备注:
kubectl
: Kubernetes 命令行工具。port-forward
: 端口转发命令,用于将本地端口转发到 Pod。--address 0.0.0.0
: 使得端口绑定到本地机器的所有网络接口上,从而允许外部访问。pod/nginx
: 指定要转发端口的 Pod 名称,这里是nginx
。如果你的 Pod 名称不同,请替换为实际名称。8080:80
: 将本地端口 8080 映射到 Pod 上的 80 端口。
这条命令会在前台运行,此时就可以在其他客户端用该k8s主机的IP地址和8080的端口号进行访问了,Ctrl+c停止,但停止后就没有这个映射了。
(11) 在宿主机和Pod的容器之间拷贝文件
将宿主机文件拷贝到Pod的容器
kubectl cp 宿主机文件位置 pod名称:文件存放位置
kubectl cp 宿主机文件位置 pod名称:文件存放位置 -c 容器名称
备注:
nginx
是 pod 名称。- 如果 pod 内有多个容器,默认情况下,文件会被复制到 pod 中的第一个容器。如果需要指定容器,使用
-c
选项。
三Pod探针
1.探针的实现方式
在生产环境中,进程正常启动并不代表应用能正常处理请求,所以合理的设计应用的健康检查尤其重要。在使用裸机或裸容器部署时,一般很难对应用做很完善的健康检查,而Pod提供的探针可以很方便的用来检测容器的应用是否正常。目前探针有3种检测方式,可以根据不同的场景选择合适的健康检查方式。
pod探针的实现方式
|-----------------|----------------------------------------|
| 实现方式 | 说明 |
| ExecAction | 在容器内执行一个指定的命令,如果命令返回值为0,则认为容器健康 |
| TCPSocketAction | 通过TCP连接检查容器指定的端口,如果端口开放,则认为容器健康 |
| HTTPGetAction | 对指定的URL进行Get请求,如果状态码在200-400之间,则认为容器健康 |
而且,这些检查方式可以被周期性的执行,每次检查容器后可能得到状态如下
2.探针的状态
pod探针检查容器后可能得到的状态
|-------------|----------------|
| 状态 | 说明 |
| success(成功) | 容器通过检查 |
| Failure(失败) | 容器检查失败 |
| Unknown(未知) | 诊断失败,因此不采取任何措施 |
3.探针类型
pod探针有三类,分别是:livenessProbe(存活探针),readinessProbe(就绪探针),startupProbe(启动探针)
- livenessProbe(存活探针):判断容器是否正常运行,如果失败则杀掉容器(不是pod),再根据重启策略决定是否重启容器
- readinessProbe(就绪探针):判断容器是否能够进入ready状态,探针失败则进入noready状态,并从service的endpoints中剔除次容器
- startupProbe(启动探针):判断容器内的应用是否启动成功,在success状态前,其它探针都处于无效状态
四.Pod镜像拉取策略和重启策略
在发布应用或者更改控制器配置时,会触发Pod的滚动更新,此时针对容器的镜像有不同的拉取方式。如表所示。
拉取策略
|---------------|--------------------------------------------|
| 操作方式 | 说明 |
| Always | 总是拉取,无论镜像是否存在,总是拉取 |
| Never | 无论是否存在都不会拉取 |
| IfNotPressent | 镜像不存在时拉取镜像,是k8s默认的策略, 但是如果tag为latest,则总是拉取 |
ku run nginx02 --image=nginx:1.7.9 --labels="app=nginx" --image-pull-policy=Never
重启策略
|-----------|----------------------|
| 操作方式 | 说明 |
| Always | 默认策略,容器失效时,自动重启该容器 |
| OnFailure | 容器以不为0的状态码终止,自动重启该容器 |
| Never | 无论何种状态,都不会重启 |
ku run nginx --image=nginx:1.7.9 --labels="app=nginx" --restart=OnFailure
五.创建一个简单的Pod
1.编写一个简单的Pod
vim nginx-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
name: nginx
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
2.编写Pod的配置文件frontend-localredis-pod.yaml
vim frontend-localredis-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: redis-php
labels:
name: redis-php
spec:
containers:
- name: frontend
image: kubeguide/guestbook-php-frontend:localredis
imagePullPolicy: IfNotPresent
livenessProbe:
tcpSocket:
port: 80
initialDelaySeconds: 1
periodSeconds: 3
timeoutSeconds: 1
ports:
- containerPort: 80
- name: redis
image: kubeguide/redis-master
imagePullPolicy: IfNotPresent
ports:
- containerPort: 6379
restartPolicy: OnFailure
备注:
3.pod文件语法
(1)Pod文件的一级属性
在 Kubernetes 中,Pod 的 YAML 文件包含几个主要的一级属性:
-
apiVersion
:指定使用的 API 版本(如v1
)。 -
kind
:定义资源的类型,这里是Pod
。 -
metadata
:包含有关 Pod 的元数据,如名称、标签、注释等。 -
spec
:定义 Pod 的规格,包括容器配置、卷、重启策略等。 -
status
(系统自动填充):表示 Pod 当前的状态,如阶段和容器状态。
(2)spec(规格)属性
spec
属性在 Kubernetes Pod 的 YAML 文件中定义了 Pod 的配置和行为。主要包括以下几个方面:
-
containers
:列出 Pod 中的容器及其设置,例如镜像、端口和环境变量。 -
volumes
:定义 Pod 使用的存储卷,例如临时存储或持久化存储。 -
restartPolicy
:设置容器的重启策略,选项包括Always
、OnFailure
和Never
。 -
nodeSelector
:指定节点选择器,用于将 Pod 调度到特定节点。 -
affinity
:定义节点和 Pod 的亲和性规则,用于更复杂的调度需求。 -
tolerations
:允许 Pod 在带有污点的节点上运行。 -
serviceAccountName
:指定 Pod 使用的服务帐户,以便赋予相应的权限。
这些配置共同决定了 Pod 的行为和调度方式。
(3)通过kubectl explain 命令来查看每种资源的可配置项
kubectl explain pod/deployment/service/pod.Metadata/pod.spec.containers
(4)运行kubectl create命令创建pod
Kubectl create -f frontend-localredis-pod.yaml
(5)查看pod的详细信息
kubectl describe pod pod名称
(6)暴露端口
kubectl expose pod pod名称 --port=8080(外部访问端口) --target-port=80(pod内部端口) --type=NodePort --name=nginx-php
备注:
-
pod pod名称
:指定你要暴露的资源类型和名称。在这个例子中,pod
表示你要暴露的是一个 Pod,pod名称
是 Pod 的名称。 -
--port=8080
:定义 Service 将暴露在集群外部的端口号。在这个例子中,Service 会在端口8080
上监听。 -
--target-port=80
:定义流量被转发到 Pod 上的哪个端口。也就是说,当请求进入 Service 的端口8080
时,它会被转发到 Pod 的端口80
。 -
--type=NodePort
:定义 Service 的类型。NodePort
表示 Service 会在每个节点的某个端口上暴露,这样可以通过访问任意节点的这个端口来访问 Service。 -
--name=nginx-php
:定义创建的 Service 的名称。在这个例子中,Service 将被命名为nginx-php
。
(7)删除pod
kubectl delete -f frontend-localredis-pod.yaml
六.静态pod
将yaml文件放到宿主机的/etc/kubernetes/manifests/下,过一会会发现其中所定义的pod为开启状态,主要用于设置开机自启,关闭将yaml结尾的文件移除或者删除即可。
七.pod启动阶段
1.pod的启动过程包含的步骤
在 Kubernetes 中,Pod 的启动过程是一个复杂且精细的过程,涉及多个阶段。下面是 Pod 从创建到运行的主要启动阶段:
-
Pod 创建:
- 用户或控制器(如 Deployment、StatefulSet 等)提交一个创建 Pod 的请求。
- Kubernetes API 服务器接收到请求,并在集群的控制平面中创建一个 Pod 对象。
-
调度(Scheduling):
- 调度器(Scheduler)会选择一个适合运行该 Pod 的节点。选择依据包括资源需求(CPU、内存)、节点标签、亲和性规则等。
- 调度器将 Pod 分配到选定的节点,并更新 Pod 对象的
spec.nodeName
字段。
-
节点接收并创建容器:
- 节点上的 kubelet 发现有新的 Pod 分配给它,并开始在该节点上创建 Pod。
- Kubelet 拉取容器镜像(如果镜像未缓存),然后创建容器。
-
容器启动:
- 容器按照定义的命令启动。启动过程包括创建容器的文件系统、初始化网络设置、启动进程等。
-
Pod 初始化阶段:
- Init Containers(初始化容器): 如果 Pod 定义中包含 Init Containers,这些容器会在应用容器(应用容器是 Pod 的主要容器)启动之前运行。Init Containers 用于执行一些初始化操作,如设置配置、准备数据等。所有 Init Containers 必须成功完成,Pod 才能进入正常的运行阶段。
- Pod 生命周期钩子 : 可能还会执行某些生命周期钩子,如
postStart
钩子(在容器启动后立即调用)。
-
容器运行:
- 应用容器开始运行并提供服务。
- Kubernetes 监控容器的健康状况,确保容器保持运行状态。容器状态的变化(如失败、重启等)会被记录并上报到 Kubernetes 控制平面。
-
健康检查:
- Liveness Probe: 用于检查容器是否还在运行。如果失败,Kubernetes 可能重启容器。
- Readiness Probe: 用于检查容器是否已经准备好接受流量。如果失败,Pod 不会被标记为"就绪",从而不会接收流量。
-
Pod 运行:
- 当容器成功启动并通过健康检查,Pod 进入"Running"状态,开始接受流量和处理请求。
-
服务暴露(如果配置了):
- 如果 Pod 与 Service 关联,那么 Service 将开始将流量路由到 Pod 上的容器。
-
容器和 Pod 的生命周期管理:
- 在运行过程中,Kubernetes 会持续监控 Pod 和容器的状态,并根据需求进行管理,例如调度、扩展、更新和故障恢复。
总的来说,Pod 的启动过程涉及从定义、调度、容器创建到运行的一系列步骤,每个阶段都对确保 Pod 能够正确和高效地提供服务至关重要。
2.phase的可能状态(pod的生命周期)
在 Kubernetes 中,Pod 的生命周期阶段通过其 phase
字段进行表示。phase
是一个字符串字段,表明 Pod 当前所处的状态。以下是 Pod 可能的 phase
状态及其含义:
-
Pending:
- 定义:Pod 被创建,但尚未完全调度到某个节点上或者还在等待所有的容器被创建和启动。
- 常见原因:可能是由于节点资源不足、镜像拉取中、卷挂载等问题。
-
Running:
- 定义:Pod 已经被调度到一个节点上,所有容器正在运行或至少一个容器正在运行。
- 常见情况:Pod 中的所有 Init Containers 都已经成功完成,应用容器已经开始运行,并且正在执行其任务。
-
Succeeded:
- 定义:Pod 中的所有容器已经成功完成其任务并退出(容器的退出状态码为 0),并且不会再重启。
- 常见情况:通常出现在批处理作业或短暂任务的 Pod。
-
Failed:
- 定义:Pod 中的一个或多个容器已经终止并且退出状态码非 0,或者 Pod 中的所有容器都失败并且不会再重启。
- 常见情况:容器运行出错、异常退出等。
-
Unknown:
- 定义:Kubernetes 控制平面无法确定 Pod 的状态,通常是由于与节点的通信出现问题。
- 常见情况:网络故障、节点故障等导致无法获取 Pod 的状态。
八.故障排除步骤
查看看Pod事件
kubectl describe TYPE AME_PREF
查看Pod日志(Failed状态下)
kubectl logs <POD_NAME> [-C Container_NAME]
进入Pod(状态为running,但是服务没有提供)
kubectl exec -it pod名称 bash
查看集群信息
kubectl get nodes
发现集群状态正常
kubectl cluster-info
查看kubelet日志发现
journalctl -xefu kubelet