一、简介

在Kubernetes集群中,Pod是所有业务类型的基础,也是K8S管理的最小单位级,它是一个或多个容器的组合。这些容器共享存储、网络和命名空间,以及如何运行的规范。在Pod中,所有容器都被统一安排和调度,并运行在共享的上下文中。对于具体应用而言,Pod是它们的逻辑主机,Pod包含业务相关的多个应用容器。

二、Pod实现机制与设计模式

每个Pod都有一个特殊的被称为"根容器"的Pause 容器(Pause容器,又叫Infrastructure容器)。 Pause容器对应的镜像属于Kubernetes平台的一部分,除了Pause容器,每个Pod还包含一个或者多个紧密相关的用户业务容器。

众所周知,容器之间是通过Namespace隔离的,Pod要想解决上述应用场景,那么就要让Pod里的容器之间高效共享。
具体分为两个部分:网络和存储

  • 共享网络

kubernetes的解法是这样的:会在每个Pod里先启动一个infra container小容器,然后让其他的容器连接进来这个网络命名空间,然后其他容器看到的网络试图就完全一样了,即网络设备、IP地址、Mac地址等,这就是解决网络共享问题。在Pod的IP地址就是infra container的IP地址。

  • 共享存储

比如有两个容器,一个是nginx,另一个是普通的容器,普通容器要想访问nginx里的文件,就需要nginx容器将共享目录通过volume挂载出来,然后让普通容器挂载的这个volume,最后大家看到这个共享目录的内容一样。

例如:

# pod-write-read.yaml
apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  containers:
  - name: write
    image: centos
    command: ["bash","-c","for i in {1..100};do echo $i >> /data/hello;sleep 1;done"]
    volumeMounts:
      - name: data
        mountPath: /data
 
  - name: read
    image: centos
    command: ["bash","-c","tail -f /data/hello"]
    volumeMounts:
      - name: data
        mountPath: /data
   
  volumes:
  - name: data
    emptyDir: {}

上述示例中有两个容器,write容器负责提供数据,read消费数据,通过数据卷将写入数据的目录和读取数据的目录都放到了该卷中,这样每个容器都能看到该目录。
验证:

$ kubectl apply -f pod-write-read.yaml
$ kubectl logs my-pod -c read -f

在Pod中容器分为以下几个类型:

  • Infrastructure Container:基础容器,维护整个Pod网络空间,对用户不可见
  • InitContainers:初始化容器,先于业务容器开始执行,一般用于业务容器的初始化工作
  • Containers:业务容器,具体跑应用程序的镜像

三、镜像拉取策略

apiVersion: v1
kind: Pod
metadata:
  name: pod001
spec:
  containers:
    - name: busybox001
      image: busybox
      imagePullPolicy: IfNotPresent

imagePullPolicy 字段有三个可选值:

  • IfNotPresent:镜像在宿主机上不存在时才拉取

  • Always:默认值,每次创建 Pod 都会重新拉取一次镜像

  • Never: Pod 永远不会主动拉取这个镜像

注意,这里的重启是指在Pod所在Node上面本地重启,并不会调度到其他Node上去。

四、资源限制

Pod资源配额有两种:

申请配额:调度时使用,参考是否有节点满足该配置

  • spec.containers[].resources.limits.cpu

  • spec.containers[].resources.limits.memory

限制配额:容器能使用的最大配置

  • spec.containers[].resources.requests.cpu

  • spec.containers[].resources.requests.memory

apiVersion: v1
kind: Pod
metadata:
  name: pod002
spec:
  containers:
  - name: busybox002
    image: busybox
    resources:
      requests:
        memory: "64Mi"
        cpu: "250m"
      limits:
        memory: "128Mi"
        cpu: "500m"

其中cpu值比较抽象,可以这么理解:

1核=1000m

1.5核=1500m

那上面限制配置就是1核的二分之一(500m),即该容器最大使用半核CPU。

该值也可以写成浮点数,更容易理解:

半核=0.5

1核=1

1.5核=1.5

五、重启策略


apiVersion: v1
kind: Pod
metadata:
  name: pod003
spec:
  containers:
  - name: busybox003
    image: busybox
  restartPolicy: Always

restartPolicy字段有三个可选值:

  • Always:当容器终止退出后,总是重启容器,默认策略

  • OnFailure:当容器异常退出(退出状态码非0)时,才重启容器。适于job

  • Never:当容器终止退出,从不重启容器。适于job

六、 健康检查

默认情况下,kubelet 根据容器状态作为健康依据,但不能容器中应用程序状态,例如程序假死。这就会导致无法提供服务,丢失流量。因此引入健康检查机制确保容器健康存活。

健康检查有两种类型:

  • livenessProbe

如果检查失败,将杀死容器,根据Pod的restartPolicy来操作。

  • readinessProbe

如果检查失败,Kubernetes会把Pod从service endpoints中剔除。

这两种类型支持三种检查方法:

  • httpGet

发送HTTP请求,返回200-400范围状态码为成功。

  • exec

执行Shell命令返回状态码是0为成功。

  • tcpSocket

发起TCP Socket建立成功。

# health-check.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-exec
spec:
  containers:
  - name: liveness
    image: busybox
    args:
    - /bin/sh
    - -c
    - touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 60
    livenessProbe:
      exec:
        command:
        - cat
        - /tmp/healthy
      initialDelaySeconds: 5
      periodSeconds: 5

上述示例:启动容器第一件事在容器内创建文件,停止30s,删除该文件,再停止60s,确保容器还在运行中。

验证现象:容器启动正常,30s后异常,会restartPolicy策略自动重建,容器继续正常,反复现象。

$ kubectl apply -f health-check.yaml
$ kubectl describe pod liveness-exec
$ 

七、调度策略

先看下创建一个Pod的工作流程: create pod -> apiserver -> write etcd -> scheduler -> bind pod to node -> write etcd -> kubelet( apiserver get pod) -> dcoekr api,create container -> apiserver -> update pod status to etcd -> kubectl get pods

Pod根据调度器默认算法将Pod分配到合适的节点上,一般是比较空闲的节点。但有些情况我们希望将Pod分配到指定节点,该怎么做呢?

这里给你介绍调度策略:nodeName、nodeSelector和污点

1)nodeName

nodeName用于将Pod调度到指定的Node名称上。
例如:下面示例会绕过调度系统,直接分配到k8s-node1节点。

# SchedulePolicy-nodeName.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: busybox
  name: busyboxnn
  namespace: default
spec:
  nodeName: k8s-node1
  containers:
  - image: busybox
    name: bs
    command:
    - "ping"
    - "baidu.com"

执行&查看

$ kubectl apply -f SchedulePolicy-nodeName.yaml
$ kubectl get pod busyboxnn -o wide

2)nodeSelector

nodeSelector用于将Pod调度到匹配Label的Node上。先给规划node用途,然后打标签,例如将两台node划分给不同团队使用:

$ kubectl label nodes k8s-node1 team=a
$ kubectl label nodes k8s-node2 team=b

后在创建Pod只会被调度到含有team=a标签的节点上。

# SchedulePolicy-nodeSelector.yaml
apiVersion: v1
kind: Pod
metadata:
  name: busyboxsn
  namespace: default
spec:
  nodeSelector:
    team: b
  containers:
  - image: busybox
    name: bs
    command:
    - "ping"
    - "baidu.com"

执行&查看

$ kubectl apply -f SchedulePolicy-nodeSelector.yaml
$ kubectl get pod busyboxsn -o wide

3)taint(污点)与tolerations(容忍)

  • 污点应用场景:节点独占,例如具有特殊硬件设备的节点,如GPU
    设置污点命令:
    kubectl taint node [node] key=value[effect]

其中[effect] 可取值:

  • NoSchedule :一定不能被调度。
  • PreferNoSchedule:尽量不要调度。
  • NoExecute:不仅不会调度,还会驱逐Node上已有的Pod。

示例:

先给节点设置污点,说明这个节点不是谁都可以调度过来的:

$ kubectl taint node k8s-node1  abc=123:NoSchedule

查看污点:

$ kubectl describe node k8s-node1 |grep Taints

然后在创建Pod只有声明了容忍污点(tolerations),才允许被调度到abc=123污点节点上

# SchedulePolicy-tolerations.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: busybox
  name: busybox3
  namespace: default
spec:
  tolerations:
  - key: "abc"
    operator: "Equal"
    value: "123"
    effect: "NoSchedule"
  containers:
  - image: busybox
    name: bs
    command:
    - "ping"
    - "baidu.com"

如果不配置容忍污点,则永远不会调度到k8s-node1。(也可以叫做反亲和性
去掉污点:

kubectl taint node k8s-node1 abc=123:NoSchedule

$ kubectl taint node k8s-node1 abc:NoSchedule-

master节点默认是打了污点标记,不调度的,去掉污点标记

#添加 尽量不调度 PreferNoSchedule 
kubectl taint nodes k8s-master node-role.kubernetes.io/master:PreferNoSchedule
#去除污点NoSchedule,最后一个"-"代表删除
kubectl taint nodes k8s-master node-role.kubernetes.io/master:NoSchedule-

八、Pod状态

1)Pod常见状态

1、Pending:等待中

Pod已经被创建,但还没有完成调度,或者说有一个或多个镜像正处于从远程仓库下载的过程。处在这个阶段的Pod可能正在写数据到etcd中、调度、pull镜像或启动容器。

2、Running:运行中

该 Pod 已经绑定到了一个节点上,Pod 中所有的容器都已被创建。至少有一个容器正在运行,或者正处于启动或重启状态。

3、Succeeded:正常终止

Pod中的所有的容器已经正常的执行后退出,并且不会自动重启,一般会是在部署job的时候会出现。

4、Failed:异常停止

Pod 中的所有容器都已终止了,并且至少有一个容器是因为失败终止。也就是说,容器以非0状态退出或者被系统终止。

5、Terminating 或 Unknown 状态

从 v1.5 开始,Kubernetes 不会因为 Node 失联而删除其上正在运行的 Pod,而是将其标记为 Terminating 或 Unknown 状态。

6、Error 状态

通常处于 Error 状态说明 Pod 启动过程中发生了错误。常见的原因包括:

  • 依赖的 ConfigMap、Secret 或者 PV 等不存在
  • 请求的资源超过了管理员设置的限制,比如超过了 LimitRange 等
  • 违反集群的安全策略,比如违反了 PodSecurityPolicy 等
  • 容器无权操作集群内的资源,比如开启 RBAC 后,需要为 ServiceAccount 配置角色绑定。

7、Completed状态

状态由ContainerCreating变为Completed再变为CrashLoopBackOff,原因是command: 或args 参数错误无法正常执行。

用一张图来表示Pod的各个状态

2)Pod 其它状态详细说明

状态描述
ContainerCreating 容器创建中
PodInitializing pod 初始化中
CrashLoopBackOff 容器曾经启动了,但可能又异常退出了,kubelet正在将它重启
InvalidImageName 无法解析镜像名称
ImageInspectError 无法校验镜像
ErrImageNeverPull 策略禁止拉取镜像
ImagePullBackOff 正在重试拉取
RegistryUnavailable 连接不到镜像中心
ErrImagePull 通用的拉取镜像出错
CreateContainerConfigError 不能创建kubelet使用的容器配置
CreateContainerError 创建容器失败
m.internalLifecycle.PreStartContainer 执行hook报错
RunContainerError 启动容器失败
PostStartHookError 执行hook报错
ContainersNotInitialized 容器没有初始化完毕
ContainersNotRead 容器没有准备完毕
DockerDaemonNotReady docker还没有完全启动
NetworkPluginNotReady 网络插件还没有完全启动

pod启动后停止问题总结(ContainerCreating-》Completed-》CrashLoopBackOff):

pod 是否能持续运行,是由执行命令决定的,执行命令如果一执行就停止,控制台也停止持续输出,pod生命周期就结束,pod状态就会变成CrashLoopBackOff。

pod资源清单

apiVersion: v1     #必选,版本号,例如v1
kind: Pod         #必选,资源类型,例如 Pod
metadata:         #必选,元数据
  name: string     #必选,Pod名称
  namespace: string  #Pod所属的命名空间,默认为"default"
  labels:           #自定义标签列表
    - 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,默认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 #内存请求,容器启动的初始可用数量
    lifecycle: #生命周期钩子
		postStart: #容器启动后立即执行此钩子,如果执行失败,会根据重启策略进行重启
		preStop: #容器终止前执行此钩子,无论结果如何,容器都会终止
    livenessProbe:  #对Pod内各容器健康检查的设置,当探测无响应几次后将自动重启该容器
      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的重启策略
  nodeName: <string> #设置NodeName表示将该Pod调度到指定到名称的node节点上
  nodeSelector: obeject #设置NodeSelector表示将该Pod调度到包含这个label的node上
  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的存储卷,挂载集群与定义的secret对象到容器内部
      scretname: string  
      items:     
      - key: string
        path: string
    configMap:         #类型为configMap的存储卷,挂载预定义的configMap对象到容器内部
      name: string
      items:
      - key: string
        path: string
posted on 2023-02-20 16:06  知趣。  阅读(187)  评论(0编辑  收藏  举报