Kubernetes Pod详解
一、Pod介绍
1.Pod的结构

每个Pod中都可以包含一个或者多个容器,这些容器可以分为两类:
- 用户程序所在的容器,数量可多可少。
- Pause容器,这是每个Pod都会有的一个根容器,他的作用有两个:
. 1.可以以他为依据,评估整个Pod的健康状态。
2.可以在根容器上设置ip地址,其他容器都依赖此ip(Pod IP),以实现Pod内部的网络通信。(这里是Pod内部的通讯,Pod之间的通讯是采用虚拟二层网络技术来实现,我们当前的环境用的是Flannel)
2.Pod的定义
下面是Pod的资源清单(配置yaml)
apiVersion: v1 #必选,版本号,例如v1
kind: Pod #必选,资源类型,例如Pod
metadata: #必选,元数据name: string #必选,Pod名称namespace: string #Pod所属的命名空间,默认为“default”labels: #Pod自定义的标签列表- name: string #标签名和值
spec: #必选,Pod中容器的详细定义containers: #必选,Pod中的容器列表- name: string #必选,容器名字image: string #必选,容器的镜像名称imagePullPolicy: [Always | Never | IfNotPresent] #获取镜像的策略command: [string] #容器启动命令列表,如不指定,使用打包时使用的启动命令args: [string] #容器启动的命令参数列表workingDir: string #容器的工作目录volumeMounts: #挂载到容器内部的存储卷配置- name: string #引用Pod定义的共享存储卷的名称,需用volumes[]部分定义的卷名mountPath: string #存储卷在容器内部mount的绝对路径,应少于512字符readOnly: boolean #是否只读模式ports: #需要暴露的端口库号列表- name: string #端口的名称containerPort: int #容器监听的端口号hostPort: int #容器所在主机需要监听的端口号,默认与Container相同protocol: string #端口协议,支持TCP和UDP,默认TCPenv: #容器运行前需设置的环境列表- name: string #环境变量名称value: string #环境变量的值resource: #资源限制和请求的设置limits: #资源限制的设置cpu: string #CPU的限制,单位为core数,将用于docker run --cpu-shares参数memory: string #内存限制,单位可以为Mib/Gib,将用于docker run --memory参数requests: #资源请求的设置cpu: string #CPU 请求,容器启动的初始可用数量memory: string #内存请求,容器启动的初始可用数量lifecycle: #生命周期钩子postStart: #容器启动后立即执行此钩子,如果执行失败,会根据重启策略进行重启preStop: #容器终止前执行此钩子,无论结果如何,容器都会终止livenessProbe: #对Pod内各容器健康检查的设置,当探测无响应几次后将自动重启该容器exec: #对Pod容器内检查方式设置为exec方式command: [string] #exec方式需要制定的命令或脚本httpGet: #对Pod内各个容器健康检查方式设置为HttpGet,需要制定Path、portpath: stringport: numberhost: stringscheme: stringHttpHeaders:- name: stringvalue: stringtcpSocket: #对pod内各个容器健康检查方式设置为tcpSocket方式port: number initialDelaySeconds: 0 #容器启动完成后首次探测的时间,单位为秒timeoutSeconds: 0 #对容器健康检查探测等待响应的超时时间,单位秒periodSeconds: 0 #对容器监控检查的定期探测时间设置,单位秒,默认1秒successThreshold: 0failureThreshold: 0sucurityContext:privileged: falserestartPolicy: [Always|Never|OnFailure] #对Pod的重启策略nodeName: #设置NodeName表示将该Pod调度到指定名称的node节点上nodeSelector: object #设置NodeSelector表示将该pod调度到包含这个label的Node上imagePullSecurets: #pull镜像时使用的secret名称,以key:secretKey格式指定- name: stringhostNetwork: false #是否使用主机网络模式volumes: #在该pod上定义共享存储卷列表- name: string #共享存储卷名称emptyDir: {} #类型为emptyDir的存储卷,与pod同生命周期的一个临时目录,为空值hostPath: string #类型为hostPath的存储卷,表示挂载pod所在宿主机目录path: string #pod所在宿主机的目录,将被用于同期中mount的目录secret: #类型为secret存储卷,挂载集群与定义的secret对象到容器内部scretname: stringitems:- key: stringpath: stringconfigMap: #类型为configMap的存储卷,挂载预定义的configMap对象到容器内部name: stringitems:- key: stringpath: string
小提示:
可以通过一个命令来查看各种资源的可配置项
#查看某种资源可以配置的一级属性
kubectl explain 资源类型
#查看属性的子属性
kubectl explain 资源类型 属性#常用的资源类型为Pod Service Namespace Deployment
在kubernetes中基本所有资源的一级属性都是一样的,主要包含5个部分:
| 属性 | 类型 | 作用 |
| apiVersion | string | 版本,由kubernetes内部定义,版本号必须可以用kubectl api-versions查询到 |
| kind | string | 类型,由kubernetes内部定义,版本号必须可以用kubectl api-resources 查询到 |
| metadata | Object | 元数据,主要是资源标识和说明,常用的有name、namespace、labels等 |
| spec | Object | 描述,这是配置中最重要的一部分,里面是对各种资源配置的详细描述 |
| status | Object | 状态信息,里面的内容不需要定义,由kubernetes自动生成 |
在上面的属性中,spec是研究的重点,他常见的紫属性有:
| 属性 | 类型 | 作用 |
| containers | []Object | 容器列表看,用于定义容器的详细信息 |
| nodeName | string | 根据nodeName的值将pod调度到指定的Node节点上 |
| nodeSelector | map[] | 根据nodeSelector中定义的信息选择将该Pod调度到包含这些label的Node上 |
| hostNetwork | boolean | 是否使用主机网络模式,默认是false,如果设置为true,表示使用宿主机网络 |
| volumes | []Object | 存储卷,用于定义Pod上面挂载的存储信息 |
| restartPolicy | string | 重启策略,表示Pod在遇到故障的时候的处理策略 |
二、Pod 配置
主要研究pod.spec.containers属性,这也是pod配置中最为关键的一项配置。
[root@master ~]#kubectl explain pod.spec.containers
KIND: Pod
VERSION: v1
RESOURCE: containers <[]Object>
FIELDS:name #容器名称必要配置image #容器需要的镜像imagePullPolicy #镜像拉取策略command <[]string> #容器启动命令args <[]string> #容器启动命令参数env <[]Object> #容器环境变量的配置ports <[]Object> #容器需要暴露的端口号列表resources
1. 基本配置
创建pod-base.yaml文件,内容如下:
apiVersion: v1
kind: Pod
metadata:name: pod-basenamespace: devlabels:user: axiba
spec:containers:- name: nginximage: nginx:1.17.1- name: busyboximage: busybox:1.30
上面定义了一个比较简单的Pod的配置,里面有两个容器:
- nginx:用1.17.1版本的nginx镜像创建
- busybox:用1.30版本的busybox镜像创建

可以使用kubectl describe命令查看events
kubectl describe pod pod-base -n dev

2.镜像拉取策略
编写pod-imagepullpolicy.yaml,内容如下
apiVersion: v1
kind: Pod
metadata:name: pod-imagepullpolicynamespace: devlabels:user: axiba
spec:containers:- name: nginximage: nginx:1.17.1imagePullPolicy: Always - name: busyboximage: busybox:1.30
imagePullPolicy,用于设置镜像拉取策略,kubernetes支持配置三种拉取策略:
- Always:总是从远程仓库拉取镜像
- IfNotPresent:本地有则使用本地镜像,本地没有则从远程仓库拉取镜像
- Never:只使用本地镜像,从不去远程仓库拉取
默认值说明:
1.如果镜像tag为具体版本号,则默认策略时IfNotPresent
2.如果镜像tag为:lastest(最近版本),默认策略是Always
查看pod详情的events可以看出,当拉取策略设置为Always时,有从远程仓库拉取镜像的过程

当拉取策略设置为Never的时候
1.镜像已经存在

2.镜像不存在,这时直接报错

3.容器启动命令
在前面busybox容器一直没有成功运行,那么到底是什么原因呢?
是因为busybox并不是一个程序,而是类似一个工具类集合,kubernetes集群启动管理后,他会自动关闭。解决方法就是让其一直在运行,这就用到了command配置。
创建pod-command.yaml文件,内容如下:
apiVersion: v1
kind: Pod
metadata:name: pod-commandnamespace: devlabels:user: axiba
spec:containers:- name: nginximage: nginx:1.17.1 - name: busyboximage: busybox:1.30command: ['/bin/sh','-c','touch /tmp/hello.txt;while true;do /bin/echo $(date +%T) >> /tmp/hello.txt; sleep 3; done;']
command用于在pod中的容器初始化完毕之后运行的一个命令。

特别说明:kubernetes中的command和args其实是实现覆盖Dockerfile中ENTRYPOINT的功能。
- 如果command和args均没有写,那么用Dockerfile的配置。
- 如果command写了,但args没有写,那么Dockerfile默认的配置会被忽略,执行输入的command。
- 如果command没写,但args写了,那么Dockerfile中配置的ENTRYPOINT的命令会被执行,使用当前args的参数。
- 如果command和args都写了,那么Dockerfile的配置会被忽略,执行command并追加args参数。
4.环境变量
创建pod-env.yaml文件,内容如下
apiVersion: v1
kind: Pod
metadata:name: pod-envnamespace: dev
spec:containers:- name: busyboximage: busybox:1.30command: ['/bin/sh','-c','while true;do /bin/echo $(date +%T); sleep 60; done;']env: - name: "username"value: "admin"- name: "password"value: "123456"
env,环境变量,用于pod中的容器设置环境变量

这种方式不是很推荐,推荐将这些配置单独存储在配置文件中。
5.端口设置
首先查看一下ports支持的子选项
name #端口名称,如果指定,必须保证name和pod中是唯一的。
containerPort #容器要监听的端口
hostPort #容器在主机上公开的端口,如果设置,主机只能运行容器一个副本(一般省略)
hostIP #要将外部端口绑定到的主机ip(一般省略)
protocol #端口协议。必须是UDP,TCP或者SCTP。默认TCP

创建pod-ports.yaml,内容如下
apiVersion: v1
kind: Pod
metadata:name: pod-portsnamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1ports:- name: nginx-portcontainerPort: 80protocol: TCP

访问容器中的程序需要使用的是podIp:containerPort 访问
6.资源配额
容器中的程序要运行,肯定要占用一定的资源,比如CPU和内存等,如果不对某个容器的资源做限制,就可能吃掉大量资源,导致其他容器无法运行。针对这种情况,kubernetes提供了对内存和CPU的资源进行配额的机制,这种机制主要通过resources选项实现,他有两个子选项:
- limits:用于限制运行时容器的最大占用资源,当容器占用资源超过limits时会被终止,并进行重启。
- requests:用于设置容器需要的最小资源,如果环境资源不够,容器将无法启动。
可以通过上面两个选项设置资源的上下限。
创建pod-resources.yaml,内容如下:
apiVersion: v1
kind: Pod
metadata:name: pod-resourcenamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1resources:limits:cpu: "2"memory: "10Gi"requests:cpu: "1"memory: "10Gi"
在这里对cpu和memory的单位做一个说明:
- cpu:core数,可以为整数或者小数
- memory:内存大小,可以使用Gi、Mi、G、M等形式

可以看到,需要10G的内存,所以pod无法启动,状态一直是pending。
三、Pod的调度
在默认情况下,一个Pod在哪个Node节点上运行,是由Scheduler组件采用相应的算法计算出来的,这个过程是不受人工控制的。但是在实际使用中,这并不满足需求,因为很多情况下,我们想控制某些Pod到达某些节点上,那么应该怎么做呢?这就需要了解kubernetes对Pod的调度规则,kubernetes提供了四大类调度方式:
- 自动调度:运行在哪个节点上完全由Scheduler经过一系列的算法计算得出。
- 定向调度:NodeName、NodeSelector
- 亲和性调度:NodeAffinity、PodAffinity、PodAntiAffinity
- 污点(容忍)调度:Taints、Toleration
1.定向调度
定向调度,指的是利用Pod上声明nodeName或者nodeSelector,以此将Pod调度到期望的node节点上。注意,这里的调度是强制的,这就意味着即使要调度的目标Node不存在,也会向上面进行调度,只不过pod运行失败而已。
1.1.NodeName:用于强制约束将Pod调度到指定Name的Node节点上,这种方式,其实是直接跳过Scheduler的调度逻辑,直接写入PodList列表。
创建一个pod-nodename.yaml文件
apiVersion: v1
kind: Pod
metadata:name: pod-nodenamenamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1nodename: node1
1.2.NodeSelector用于将Pod调度到添加指定标签的node节点上,他通过kubernetes的label-selector机制实现的,也就是说,在pod创建之前,会由scheduler使用MatchNodeSelector调度策略进行label匹配,找出目标node,然后将pod调度到目标节点,该匹配规则是强制约束,如果NodeSelector指定的node标签不存在,则创建Pod失败。
#给node节点添加标签
#这里给node1节点添加标签nodeenv并且值为pro
kubectl label nodes node1 nodeenv=pro#这里给node2节点添加标签nodeenv并且值为test
kubectl label nodes node2 nodeenv=test
创建一个pod-nodeselector.yaml文件
apiVersion: v1
kind: Pod
metadata:name: pod-nodenamenamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1nodeSelector: nodeenv: pro
2.亲和性调度
定向调度使用非常方便,但是存在一定的问题,那就是如果没有满足条件的Node,那么Pod将不会被运行,即使在集群中还有可用Node列表也不行,这就限制了他的使用场景。
基于上面的问题,kubernetes还提供了一种亲和性调度(Affinity)。他在NodeSelector的基础上进行了扩展,可以通过配置的形式,实现优先选择满足条件的Node进行调度,如果没有,也可以调度到不满足条件的节点上,使调度更加灵活。
Affinity主要分为三类:
- nodeAffinity(node亲和性):以node为目标,解决pod可以调度到哪些node的问题。
- podAffinity(pod亲和性):以pod为目标,解决pod可以和哪些已存在的pod部署在同一拓扑域中的问题。
- podAntiAffinity(pod反亲和性):以pod为目标,解决pod不能和哪些已存在的pod部署在同一个拓扑域中的问题。
使用场景说明:
- 亲和性:如果两个应用频繁交互,那就有必要利用亲和性让两个应用尽可能的靠近,这样可以减少因网络通信而带来的性能损耗。
- 反亲和性:当应用的采用多副本部署时,有必要采用反亲和性让各个应用实例打散分布在各个node上,这样可以提高服务的高可用性。
1.NodeAffinity
NodeAffinity的可配置项:
pod.spec.affinity.nodeAffinityrequiredDuringSchedulingIgnoredDuringException # Node节点必须满足指定的所有规则才可以,相当于硬限制nodeSelectorTerms #节点选择列表matchFields #按节点字段列出的节点选择器要求列表matchExpressions #按节点标签列出的节点选择器要求列表(推荐)key #键values #值operator #关系符 支持Exists,DoesNotExist,In,NotIng,Gt,LtpreferredDuringSchedulingIgnoredDuringException # 优先调度到满足指定的规则的Node,相当于软限制(倾向)preference #一个节点选择器项,与相应的权重相关联matchFields #按节点字段列出的节点选择器要求列表matchExpressions #按节点标签列出的节点选择器要求列表(推荐)key #键values #值operator #关系符 支持Exists,DoesNotExist,In,NotIn,Gt,Ltweight #倾向权重,在范围1-100
关系符的使用说明:
- matchExpressions:- key: nodeenv #匹配存在标签的key为nodeenv的节点operator: Exists - key: nodeenv #匹配标签的key为nodeenv,且value是"xxx"或"yyy"的节点operator: Invalues: ["xxx","yyy"]- key: nodeenv #匹配标签的key为nodeenv,且value大于"xxx"的节点operator: Gtvalues: "xxx"
案例1:演示requiredDuringSchedulingIgnoredDuringExecution 硬限制
创建pod-nodeaffinity-required.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-nodeaffinity-requirednamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1affinity:nodeAffinity:requiredDuringSchedulingIgnoredDuringException:nodeSelectorTerms:- matchExpressions:- key: nodeenvoperator: Invalues: ["pro","test"]
案例2:演示preferredDuringSchedulingIgnoredDuringException 软限制
创建pod-nodeaffinity-preferred.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-nodeaffinity-preferrednamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1affinity:nodeAffinity:preferredDuringSchedulingIgnoredDuringException:- weight: 1preference:- matchExpressions:- key: nodeenvoperator: Invalues: ["pro","test"]
NodeAffinity规则设置的注意事项:
- 如果同时定义了nodeSelector和nodeAffinity,那么必须两个条件都得到满足,Pod才能运行在指定的Node上。
- 如果nodeAffinity指定了多个nodeSelectorTerms,那么只需要其中一个能够匹配成功即可。
- 如果一个nodeSelectorTerms中有多个matchExpressions,则一个节点中必须满足所有的才能匹配成功。
- 如果一个pod所在的Node在Pod运行期间其标签发生了变化,不再符合该Pod的节点亲和性需求,则系统将忽略此变化。
2.PodAffinity
PodAffinity主要实现以运行的Pod为参照,实现让新建的Pod跟参照Pod在一个区域的功能。
PodAffinity的可配置项:
pod.spec.affinity.podAffinityrequiredDuringSchedulingIgnoredDuringException # 硬限制namespaces #指定参照pod的namespacetopologyKey #指定调度作用域labelSelector #标签选择器matchExpressions #按节点标签列出的节点选择器要求列表(推荐)key #键values #值operator #关系符 支持Exists,DoesNotExist,In,NotIngmatchLabels #指多个matchExpressions映射的内容preferredDuringSchedulingIgnoredDuringException # 软限制(倾向)PodAffinityTerm #选项namespaces #指定参照pod的namespacetopologyKey #指定调度作用域labelSelector #标签选择器matchExpressions #按节点标签列出的节点选择器要求列表(推荐)key #键values #值operator #关系符 支持Exists,DoesNotExist,In,NotIngmatchLabels #指多个matchExpressions映射的内容weight: #倾向权重,在范围1-100
topologyKey用于指定调度时作用域,例如:
- 如果指定为kubernetes.io/hostname,那就是以Node节点为区分范围。
- 如果指定为beta.kubernetes.io/os,则以Node节点的操作系统类型来区分。
案例1 演示requiredDuringSchedulingIgnoredDuringException
首先创建一个参照Pod,创建pod-podaffinity-target.yaml文件
apiVersion: v1
kind: Pod
metadata:name: pod-podaffinity-targetnamespace: devlabels:podenv: prod #设置标签
spec:containers:- name: nginximage: nginx:1.17.1nodeName: node1
创建pod-podaffinity-required.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-podaffinity-requirednamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1affinity: podAffinity:requiredDuringSchedulingIgnoredDuringException - labelSelector matchExpressions - key: podenvvalues: ["prod","test"]operator: In topologyKey: kubernetes.io/hostname
配置的意思是新pod必须要与拥有标签podenv=prod或者podenv=test在同一Node上。
3.PodAntiAffinity
主要实现以运行的Pod为参照,让新创建的Pod跟参照Pod不再一个区域中的功能。
配置方式和选项跟PodAffinity一样。
继续使用pod-antifinity-target作为目标Pod,创建pod-podantiaffinity-required.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-podantiaffinity-requirednamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1affinity: podAntiAffinity:requiredDuringSchedulingIgnoredDuringException - labelSelector matchExpressions - key: podenvvalues: ["prod"]operator: In topologyKey: kubernetes.io/hostname
配置的意思是新pod必须要与拥有标签podenv=prod不在同一Node上。
3.污点和容忍
污点(Taints)
前面的调度方式都是站在Pod的角度上,通过Pod上添加属性,来确定是否要调度到指定的Node上,其实我们也可以站在Node的角度上,通过Node上的污点属性,来决定是否允许Pod调度过来。
Node被设置上污点之后,就和Pod之间存在了一种相斥关系,进而拒绝Pod调度进来,甚至可以将已经存在的Pod驱逐出去。
污点的格式为:key=value:effect,key和value是污点的标签,effect描述污点的作用,支持如下三个选项:
- PreferNoSchedule:kubernetes将尽量避免把Pod调度到具有该污点的Node上,除非没有其他的节点可调度。
- NoSchedule:kubernetes将不会把Pod调度到具有该污点的Node上,但不会影响当前的Node上已存在的Pod。
- NoExecute:kubernetes将不会把Pod调度到具有该污点的Node上,同时也会将Node上已存在的Pod驱离。

使用kubectl设置和去除污点的命令示例如下:
#设置污点
kubectl taint nodes node1 key=value:effect#去除污点
kubectl taint nodes node1 key:effect-#去除所有污点
kubectl taint nodes node1 key-
演示污点的效果:
1.准备节点node1(为了演示效果,暂时停止node2节点)
2.为node1节点设置一个污点:tag=ifun:PreferNoSchedule,然后创建pod1 (pod1正常)
3.修改node1节点,设置一个污点:tag=ifun:NoSchedule,然后创建pod2 (pod1正常 pod2失败)
4.修改node1节点,设置一个污点:tag=ifun:NoExecute,然后创建pod3 (都失败)
#为node1设置污点
kubectl taint nodes node1 tag=ifun:PreferNoSchedule#创建pod1
kubectl run taint1 --image=nginx:1.17.1 -n dev#去除node1污点PreferNoSchedule,设置NoSchedule
kubectl taint nodes node1 tag:PreferNoSchedule-
kubectl taint nodes node1 tag=ifun:NoSchedule#创建pod2
kubectl run taint2 --image=nginx:1.17.1 -n dev#去除node1污点NoSchedule,设置NoExecute
kubectl taint nodes node1 tag:NoSchedule-
kubectl taint nodes node1 tag=ifun:NoExecute#创建pod3
kubectl run taint3 --image=nginx:1.17.1 -n dev
小提示:使用kubernetes搭建集群,默认就会给master节点添加一个污点标记,所以pod不会调度到master节点上。
容忍(Toleration)
我们可以在Node上添加污点来拒绝pod调度上来,但是如果就是想将pod调度到一个有污点的node上去,这时候就要使用到容忍。

污点就是拒绝,容忍就是忽略,Node通过污点拒绝pod调度上去,pod通过容忍忽略污点。
容忍的配置项
tolerations #添加容忍key #对应要容忍污点的key,空意味着匹配所有的键value #对应要容忍污点的valueoperator #key-value的运算符,支持Equal和Exists(默认)effect #对应污点的effect,空意味着匹配所有tolerationSeconds #容忍时间,当effect为NoExecute时失效,表示pod在Node上的停留时间
创建一个pod-toleration.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-tolerationnamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1tolerations: #添加容忍- key: "tag" #污点的keyvalue: "ifun" #污点的valueoperator: "Equal" #操作符effect: "NoExecute" #添加容忍的规则,这里必须和标记的污点规则相同
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
