一、什么是 Pod
在Kubernetes中,Pod是调度和运行的最小单元,它包含一个或多个容器,并共享网络和存储资源。
Pod 是 kubernetes 集群中最小的部署和管理的基本单元,协同寻址,协同调度。
Pod 是一个或多个容器的集合,是一个或一组服务(进程)的抽象集合。
Pod 中可以共享网络和存储(可以简单理解为一个逻辑上的虚拟机,但并不是虚拟机)。
Docker 是目前 Pod 最常用的容器环境,但仍支持其他容器环境。
我们可以看到,当我们启动一个 Pod 以后,每个 Pod 内都会有一个 Pause 的容器
每个 Pod 里运行着一个特殊的被称之为 Pause 的容器,其他容器则为业务容器,这些业务容器共享 Pause 容器的网络栈和 Volume 挂载卷,因此他们之间通信和数据交换更为高效,在设计时我们可以充分利用这一特性将一组密切相关的服务进程放入同一个 Pod 中。同一个 Pod 里的容器之间仅需通过 localhost 就能互相通信。
二、Pod 的网络
- 每个Pod被分配了唯一的IP地址,该Pod内的所有容器共享一个网络空间,包括IP和端口。
- 同个Pod不同容器之间通过localhost通信,Pod内端口不能冲突。
- 不同Pod之间的通信则通过IP+端口的形式来访问到Pod内的具体服务(容器)。
三、Pod的生命周期
Pod的生命周期是指Pod从创建到终止的整个过程。
以下是Pod生命周期的主要阶段:
-
Pending( Pending):在这个阶段,Pod已经被Kubernetes系统接受,但一个或多个容器图像尚未创建。这意味着调度已经完成,但容器尚未启动。这个阶段可能持续一段时间,特别是如果需要从远程仓库拉取镜像时。
-
Running(运行中):当Pod中的所有容器都已成功启动,并且至少有一个容器仍在运行时,Pod进入Running状态。在这个状态下,Pod是活跃的,并且可以处理请求。
-
Succeeded(已完成):当Pod中的所有容器都已成功完成执行,并且不会再次启动时,Pod进入Succeeded状态。这通常发生在批处理作业或一次性任务完成后。
-
Failed(失败):如果Pod中的所有容器都已退出,但至少有一个容器因为非零退出状态而失败,则Pod进入Failed状态。这表示任务未能成功完成。
-
Unknown(未知):当Kubernetes无法获取Pod的状态时,例如由于与节点的通信故障,Pod进入Unknown状态。
除了这些主要状态之外,Pod还有一系列的条件(conditions),这些条件提供了更详细的生命周期信息。例如:
- Initialized:所有初始化容器都已经成功执行。
- Ready:Pod已准备就绪,可以响应请求。
- ContainersReady:所有容器都已经成功启动。
- PodScheduled:Pod已经调度到某个节点上。
四、常见错误
- ImagePullBackOff: 当 Pod 尝试拉取一个镜像但是失败时会出现这个报错。可能原因有镜像仓库无法连接、镜像不存在或者权限不足等。
- 确保镜像能够在集群中访问
- 检查镜像仓库是否可连接
- 确保镜像名称和标签正确
- 检查镜像拉取的凭证是否正确
- CrashLoopBackOff: 当容器不断启动和崩溃时会出现这个报错。可能原因是应用程序内部错误、配置问题或者资源不足等。
- 检查容器日志以找出导致容器崩溃的问题
- 检查应用程序配置和资源使用情况
- 确保容器运行时参数正确
- ErrImagePull: 当 Pod 尝试拉取镜像但是失败时会出现这个报错。通常是镜像无效、镜像仓库无法连接或者权限问题等。
- 检查镜像是否存在
- 检查镜像仓库是否可连接
- 检查拉取镜像的权限
- CreateContainerConfigError: 当 Pod 创建容器配置失败时会出现这个报错。可能是容器配置文件错误、命令错误或者环境变量设置问题等。
- 检查容器配置文件是否正确
- 确保容器命令和参数正确
- 检查环境变量的设置是否正确
- PodInitializing: 表示 Pod 正在初始化。这通常是因为容器启动需要时间,可以耐心等待。
- 等待 Pod 初始化完成
- Pending: Pod 处于待定状态,可能是因为节点资源不足或者调度问题等。可以使用 kubectl describe pod 命令查看具体原因。
- 检查节点资源是否充足
- 检查调度器是否有足够的资源可用来调度 Pod
- Error: Pod 处于错误状态,可能是由于容器无法运行、网络问题或者权限问题等。可以使用 kubectl logs 命令查看容器日志获取更多信息。
- 检查容器是否能够正常运行
- 检查网络是否正常
- 检查权限是否正确配置
以上是一些常见的 Kubernetes Pod 报错,可以根据报错信息的不同进行相应的排查和处理。
五、创建 Pod
Pod 实际上是容器的集合,在 kubernetes 中对运行容器的要求为 “容器的主程序需要一直在前台运行,而不是后台运行“ 当多个应用之间是紧耦合的关系时,可以将多个应用一起放在一个Pod中,同个Pod中的多个容器之间互相访问可以通过localhost来通信。
相关命令:
操作 | 命令 |
---|---|
创建 | kubectl create -f 文件名.yaml 或者 kubectl run pod名称 参数 |
查询运行状态 | kubectl get pods -n 空间名称,如果不指定则默认显示default空间内的 pod |
查询详情 | kebectl describe pod Pod名称 -n 空间名称,如果不指定则默认显示default空间内的 pod |
删除 | kubectl delete pod Pod名称 / kubectl delete pod --all |
更新 | kubectl replace 文件名.yaml |
创建pod的两种方式:
基于命令的方式:
1.简单直观快捷,上手快。
2.适合临时测试或实验。
基于配置文件的方式:
1.配置文件详细描述了服务的所有需求,即应用最终要达到的状态。
2.配置文件提供了创建资源的模板,能够重复部署。
3.可以像管理代码一样管理部署。
4.适合正式的、跨环境的、规模化部署。
5.这种方式要求熟悉配置文件的语法,有一定难度。
在 kubernetes 中,一般使用 yaml 格式的文件来创建符合我们预期期望的 pod
# 使kubectl命令直接生成yaml文件
kubectl create deployment nginx --image=nginx --dry-run=client -o yaml > nginx-deployment.yaml
# 导出某个pod的信息并生成yaml文件
kubectl get pod <pod名称> -o yaml > pod.yaml
# 导出所有pods的yaml信息
kubectl get pods --all-namespaces -o yaml > pods.yaml
基本语法为:
- 缩进时不允许使用Tab键,只允许使用空格
- 缩进的空格数目不重要,只要相同层级的元素左侧对齐即可
- #标识注释,从这个字符一直到行尾,都会被解释器忽略
这样的 yaml 文件我们一般称为资源清单,如下表所示:
必选字段:
apiVersion: v1 #必选,版本号,例如v1
kind: Pod #必选,资源类别
metadata: #必选,元数据
name: string #必选,Pod名称
namespace: string #所属于的命名空间,默认创建在default空间
labels: #自定义标签
- name: string #自定义标签名称
spec: #必选,Pod中容器的详细定义
containers: #必选,Pod中容器列表
- name: string #必选,容器名称
image: string #必选,容器的镜像名称
示例文件:
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app: web
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
完整资源清单详情示例:
apiVersion: v1 #必选,版本号,例如v1
kind: Pod #必选,Pod
metadata: #必选,元数据
name: string #必选,Pod名称
namespace: string #创建资源所属于的命名空间,不写的话默认创建在default空间
labels: #自定义标签
- name: string #自定义标签名字
annotations: #自定义注释列表
- name: string
spec: #必选,Pod中容器的详细定义
containers: #必选,Pod中容器列表
- name: string #必选,容器名称
image: string #必选,容器的镜像名称
imagePullPolicy: [Always | Never | IfNotPresent] #获取镜像的策略 Alawys表示下载镜像 IfnotPresent表示优先使用本地镜像,否则下载镜像,Nerver表示仅使用本地镜像
command: [string] #容器的启动命令列表,如不指定,使用打包时使用的启动命令
args: [string] #容器的启动命令参数列表
workingDir: string #容器的工作目录
volumeMounts: #挂载到容器内部的存储卷配置
- name: string #引用pod定义的共享存储卷的名称,需用volumes[]部分定义的的卷名
mountPath: string #存储卷在容器内mount的绝对路径,应少于512字符
readOnly: boolean #是否为只读模式
ports: #端口号名称
containerPort: int #容器需要监听的端口号
hostPort: int #容器所在主机需要监听的端口号,默认与Container相同
protocol: string #端口协议,支持TCP和UDP,默认TCP
env: #容器运行前需设置的环境变量列表
- name: string #环境变量名称
value: string #环境变量的值
resources: #资源限制和请求的设置
limits: #资源限制的设置
cpu: string #Cpu的限制,单位为core数,将用于docker run --cpu-shares参数
memory: string #内存限制,单位可以为Mib/Gib,将用于docker run --memory参数
requests: #资源请求的设置
cpu: string #Cpu请求,容器启动的初始可用数量
memory: string #内存清楚,容器启动的初始可用数量
livenessProbe: #对Pod内个容器健康检查的设置,当探测无响应几次后将自动重启该容器,检查方法有exec、httpGet和tcpSocket,对一个容器只需设置其中一种方法即可
exec: #对Pod容器内检查方式设置为exec方式
command: [string] #exec方式需要制定的命令或脚本
httpGet: #对Pod内个容器健康检查方法设置为HttpGet,需要制定Path、port
path: string
port: number
host: string
scheme: string
HttpHeaders:
- name: string
value: string
tcpSocket: #对Pod内个容器健康检查方式设置为tcpSocket方式
port: number
initialDelaySeconds: 0 #容器启动完成后首次探测的时间,单位为秒
timeoutSeconds: 0 #对容器健康检查探测等待响应的超时时间,单位秒,默认1秒
periodSeconds: 0 #对容器监控检查的定期探测时间设置,单位秒,默认10秒一次
successThreshold: 0
failureThreshold: 0
securityContext:
privileged:false
restartPolicy: [Always | Never | OnFailure] #Pod的重启策略,Always表示一旦不管以何种方式终止运行,kubelet都将重启,OnFailure表示只有Pod以非0退出码退出才重启,Nerver表示不再重启该Pod
nodeSelector: obeject #设置NodeSelector表示将该Pod调度到包含这个label的node上,以key:value的格式指定
imagePullSecrets: #Pull镜像时使用的secret名称,以key:secretkey格式指定
- name: string
hostNetwork:false #是否使用主机网络模式,默认为false,如果设置为true,表示使用宿主机网络
volumes: #在该pod上定义共享存储卷列表
- name: string #共享存储卷名称 (volumes类型有很多种)
emptyDir: {} #类型为emtyDir的存储卷,与Pod同生命周期的一个临时目录。为空值
hostPath: string #类型为hostPath的存储卷,表示挂载Pod所在宿主机的目录
path: string #Pod所在宿主机的目录,将被用于同期中mount的目录
secret: #类型为secret的存储卷,挂载集群与定义的secre对象到容器内部
scretname: string
items:
- key: string
path: string
configMap: #类型为configMap的存储卷,挂载预定义的configMap对象到容器内部
name: string
items:
- key: string
path: string
命令实战
# 创建pod,我们用kubectl run -h,来查看命令帮助,是不是豁然开朗
# kubectl run -h
Create and run a particular image in a pod.
Examples:
# Start a nginx pod.
kubectl run nginx --image=docker.io/library/nginx:1.21.6
......
# 我们就用示例给出的第一个示例,来创建一个nginx的pod
# kubectl run nginx --image=docker.io/library/nginx:1.21.6
pod/nginx created
# 等待镜像下载完成后,pod就会正常running了(这里介绍两个实用参数 -w代表持久监听当前namespace下的指定资源的变化;-o wide代表列出更为详细的信息,比如这里pod运行的node节点显示)
# 注: READY下面的含义是后面数字1代表这个pod里面期望的容器数量,前面的数字1代表服务正常运行就绪的容器数量
# kubectl get pod -o wide -w
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 1/1 Running 0 100s 172.20.139.68 10.0.1.203 <none> <none>
字段说明:
字段 | 含义 |
---|---|
NAME | 代表资源名称。 |
READY | 代表资源是否就位,0/1,代表有Pod内有一个容器,这个容器还没有运行。 |
STATUS | 代表容器运行的状态。 |
RESTARTS | 代表Pod重启次数,及容器被创建的次数。 |
AGE | 代表Pod资源运行的时间。 |
IP | 代表Pod的IP地址。 |
NODE | 代表Pod被调度到哪个节点。 |
NOMINATED NODE | 被被提名节点,作为候选节点的特定节 |
READINESS GATES | 就绪门,用于控制何时可以将节点视为就绪或不就绪的机制。 |
# 我们来请求下这个pod的IP
# curl 172.20.139.68
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
# 我们进到这个pod服务内,修改下页面信息看看,这里会学到exec子命令,-it代表保持tty连接,不会一连上pod就断开了
# kubectl -it exec nginx -- bash
# echo 'hello, world!' > /usr/share/nginx/html/index.html
# exit
# curl 172.20.139.68
hello, world!
# 我们来详细分析的这个pod启动的整个流程,这里会用到kubectl的子命令 describe,它是用来描述后面所接资源的详细信息的,划重点,这个命令对于我们生产中排查K8s的问题尤其重要
# kubectl describe pod nginx
Name: nginx
Namespace: default
Priority: 0
Service Account: default
Node: 10.0.1.203/10.0.1.203
Start Time: Tue, 24 Oct 2023 21:43:38 +0800
Labels: run=nginx
Annotations: <none>
Status: Running
IP: 172.20.139.68
IPs:
IP: 172.20.139.68
Containers:
nginx:
Container ID: containerd://184b225e3e83650a4700959536e0e2d444ffe416b4fd5cce8c9c6ee209d14cb9
Image: docker.io/library/nginx:1.21.6
Image ID: docker.io/library/nginx@sha256:2bcabc23b45489fb0885d69a06ba1d648aeda973fae7bb981bafbb884165e514
Port: <none>
Host Port: <none>
State: Running
Started: Tue, 24 Oct 2023 21:43:39 +0800
Ready: True
Restart Count: 0
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-2gmsh (ro)
Conditions:
Type Status
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes:
kube-api-access-2gmsh:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 3607
ConfigMapName: kube-root-ca.crt
ConfigMapOptional: <nil>
DownwardAPI: true
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 3m35s default-scheduler Successfully assigned default/nginx to 10.0.1.203
Normal Pulled 3m35s kubelet Container image "docker.io/library/nginx:1.21.6" already present on machine
Normal Created 3m35s kubelet Created container nginx
Normal Started 3m35s kubelet Started container nginx
# 重点分析下最后面的Events事件链
# 1. kubectl 发送部署pod的请求到 API Server
# 2. API Server 通知 Controller Manager 创建一个 pod 资源
# 3. Scheduler 执行调度任务,Events的第一条打印信息就明确显示了这个pod被调度到192.168.1.102这个node节点上运行,接着开始拉取相应容器镜像,拉取完成后开始创建nginx服务,至到最后服务创建完成,在有时候服务报错的时候,这里也会显示相应详细的报错信息
# 我们删除掉这个nginx的pod
# kubectl delete pod nginx
pod "nginx" deleted
# kubectl get pod
# 现在已经看不到这个pod了,假设这里是我们运行的一个服务,而恰好运行这个pod的这台node当机了,那么这个服务就没了,它不会自动飘移到其他node上去,也就发挥不了K8s最重要的保持期待服务运行数量的服务特性了。
六、Pod资源限制
Pod资源限制允许您指定Pod中容器可以使用的CPU和内存资源的上限。这有助于确保每个容器不会过度消耗资源,从而影响其他容器的性能。您可以通过在Pod规格中设置资源限制来控制容器的资源使用情况。
例子:
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
containers:
- name: my-container
image: my-image
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
在上面的示例中,容器“my-container”的内存请求为64MiB,CPU请求为250毫核(0.25核),内存限制为128MiB,CPU限制为500毫核(0.5核)。容器在Pod运行期间不会超过这些限制。
七、作业
# 试着创建一个redis服务的pod,并且使用exec进入这个pod,通过客户端命令redis-cli连接到redis-server ,插入一个key a ,value 为666,最后删除这个redis的pod
root@redis:/data# redis-cli
127.0.0.1:6379> get a
(nil)
127.0.0.1:6379> set a 666
OK
127.0.0.1:6379> get a
"666"