一、什么是POD

Pod是Kubernetes最重要的基本概念,我们看到每个Pod都有一个特殊的被称为“根容器”的Pause容器。Pause容器对应的镜像属于Kubernetes平台的一部分,除了Pause容器,每个Pod还包含一个或多个紧密相关的用户业务容器。

二、POD的生命周期

kubernetes CrashLoopBackOff 如何定位 kubernetes pause_Pod

三、pause容器

        用"kubernetes/pause"镜像为每个Pod都创建一个容器。该Pause容器用于接管Pod中所有其他容器的网络。每创建一个新的Pod,kubelet都会先创建一个Pause容器, 然后创建其他容器。"kubernetes/pause"镜像大概有270KB,是个非常小的容器镜像。

四、初始化容器initContainers

因为init容器具有与应用容器分离的单独镜像,所以它们启动相关代码有具有如下优势:

  • 它们可以包含并运行实用工具,但是出于安全考虑,是不建议在应用程序容器镜像中包含这些实用工具的。
  • 它们可以包含使用工具和定制化代码来安装,但是不能出现在应用程序镜像中。例如,创建镜像没必要FROM另一个镜像,只需要在安装过程中使用类似sed、awk、python或dig这样的工具。
  • 应用程序镜像可以分离出创建和部署的角色,而没必要联合它们构建一个单独镜像。
  • Init容器使用Linux namespace,所以相对应用程序容器来说具有不同的文件系统视图。因此,它能够访问secret的权限,而应用程序则不能。(分权限治理)
  • 由于 Init 容器必须在应用容器启动之前运行完成,因此 Init 容器提供了一种机制来阻塞或延迟应用容器的启动,直到满足了一组先决条件。一旦前置条件满足,Pod内的所有的应用容器会并行启动。

注意事项:

  • 在Pod启动过程中,Init容器会按顺序在网络和数据卷初始化之后启动。每个容器必须在下一个容器启动之前成功退出。
  • 如果由于运行时或失败退出,将导致容器启动失败,它会根据Pod的RestartPolicy指定的策略进行重试。如果是Never,它不会重新启动。
  • 如果Pod重启,则Init容器会重启执行。
  • init container不能设置readinessProbe探针,因为必须在它们成功运行后才能继续运行在Pod中定义的普通容器。

五、POD重启策略

当某个容器异常退出或者健康检查失败, kubelet将根据RestartPolicy的设置来进行相应的操作, 重启策略有Always , OnFailure, Never。

Always:Pod一旦终止运行,无论容器是如何终止的,kubelet都将重启它。
OnFailure:当容器终止运行非零退出时,由kubelet自动重启该容器。
Never:不论容器运行状态如何,kubelet都不会重启该容器。

六、imagePullPolicy镜像拉取策略

Always: 表示每次都尝试重新拉取镜像
IfNotPresent: 表示如果本地有镜像, 则使用本地的镜像, 本地不存在时拉取镜像
Never: 表示仅使用本地镜像

七、三种探针startupProbe、livenessProbe和readinessProbe

startupProbe探针:用于判断容器内应用程序是否已经启动,如果配置了startuprobe,优先于其它两种探测支行,直到它成功为止,成功后将不再进行startupProbe探测。

LivenessProbe探针:用于判断容器是否存活(Running状态),如果LivenessProbe探针探测到容器不健康,则kubelet将杀掉该容器,并根据容器的重启策略做相应的处理。 
如果一个容器不包含LivenessProbe探针,那么kubelet认为该容器的LivenessProbe探针返回的值永远是Success。

ReadinessProbe探针:用于判断容器服务是否可用(Ready状态),达到Ready状态的Pod才可以接收请求。对于被Service管理的Pod,Service与Pod Endpoint的关联关系
也将基于Pod是否Ready进行设置。如果在运行过程中Ready状态变为False,则系统自动将其从Service的后端Endpoint列表中隔离出去,后续再把恢复到Ready状态的Pod加回
后端Endpoint列表。这样就能保证客户端在访问Service时不会被转发到服务不可用的Pod实例上。

 包括三种探测方式:

  • ExecAction:在容器内部执行一个命令,如果该命令的返回码为0,则表明容器健康。
livenessProbe:
  exec:
    command: ['test', '-e', '/tmp/live']
  initialDelaySeconds: 1       # 启动容器后进行首次健康检查的等待时间
  periodSeconds: 3             # 检测失败后重试时间
  • TCPSocketAction:通过容器的IP地址和端口号执行TCP检查,如果能够建立TCP连接,则表明容器健康。
readinessProbe:
  tcpSocket:
    port: 80
  initialDelaySeconds: 5       # 启动容器后进行首次健康检查的等待时间
  failureThreshold: 30         # 检测失败后重试时间
  periodSeconds: 10            # 执行探测的间隔时间
  • HTTPGetAction:通过容器的IP地址、 端口号及路径调用HTTP Get方法,如果响应的状态码大于等于200且小于400,则认为容器健康。
livenessProbe:
  httpGet:
    path: /index.html
    port: 80
  initialDelaySeconds: 1 
  periodSeconds: 3

一些可选参数

timeoutSeconds:探测超时的秒数。默认为1秒。
initialDelaySeconds:启动容器后进行首次健康检查的等待时间,单位为s
periodSeconds:检测失败后重试时间3秒
failureThreshold:失败的容忍次数失败几次 判断为彻底失败,放弃检查并重启
successThreshold:失败后探测成功的最小成功次数。默认为1。最小值为1。

八、资源限制requests和limits

  • Request工作原理

Request 的值并不代表给容器实际分配的资源大小,用于提供给调度器。调度器会检测每个节点可用于分配的资源(可分配资源减 request 之和),同时记录每个节点已经被分配的资源(节点上所有 Pod 中定义的容器 request 之和)。如发现节点剩余的可分配资源已小于当前需被调度的 Pod 的 request,则该 Pod 就不会被调度到此节点。反之,则会被调度到此节点。

  • 避免 request 与 limit 值过大

若服务使用单副本或少量副本,且 request 及 limit 的值设置过大,使服务可分配到足够多的资源去支撑业务。则某个副本发生故障时,可能会给业务带来较大影响。当 Pod 所在节点发生故障时,由于 request 值过大,且集群内资源分配的较为碎片化,其余节点无足够可分配资源满足该 Pod 的 request,则该 Pod 无法实现漂移,无法自愈,会加重对业务的影响。

建议尽量减小 request 及 limit,通过增加副本的方式对服务支撑能力进行水平扩容,使系统更加灵活可靠。

resources:
  requests:
    cpu: 500m
    memory: 1024Mi
  limits:
    cpu: 1000m         # 1000m=1核CPU
    memory: 2048Mi

九、command 容器的启动命令 

        创建 Pod 时,可以为其下的容器设置启动时要执行的命令及其参数。如果要设置命令,就填写在配置文件的 command 字段下,如果要设置命令的参数,就填写在配置文件的 args 字段下。一旦 Pod 创建完成,该命令及其参数就无法再进行更改了。参数会覆盖docker镜像中的启动命令。

# 用shell脚本的方式执行命令
command: ["/bin/sh"]
args: ["-c", "while true; do echo hello; sleep 10;done"]

# 或者只写一条----------------------------------------------
command: ["sh", "-c", "/startup.sh"]

# 使用环境变量来设置参数
env:
- name: MESSAGE
  value: "hello world"
command: ["/bin/echo"]
args: ["$(MESSAGE)"]

十、env环境变量

env:
- name: MESSAGE
  value: "hello world"
command: ["/bin/echo"]
args: ["$(MESSAGE)"]

env:
- name: POD_IP
  valueFrom:
    fieldRef:
      fieldPath: status.podIP

- name: POD_NAME
  valueFrom:
    fieldRef:
      fieldPath: metadata.name

十一、POD的调度

在Kubernetes平台上, 我们很少会直接创建一个Pod, 在大多数情况下会通过RC、 Deployment、 DaemonSet、 Job等控制器完成对一组Pod副本的创建、 调度及全生命周期的自动控制任务。

  • 11.1 Deployment或者ReplicaSet

主要功能之一就是自动部署一个容器应用的多份副本, 以及持续监控副本的数量, 在集群内始终维持用户指定的副本数量。

 

# cat nginx-deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: mynginx image: nginx:1.18.0 ports: - containerPort: 80 resources: requests: cpu: 100m memory: 128Mi limits: cpu: 1000m memory: 1024Mi

 

  • 11.2 NodeSelector:定向调度

NodeSelector通过标签的方式,简单实现了限制Pod所在节点的方法。

# kubectl label node k8s-node01 app=nginx     # 为k8s-node01节点打上app=nginx的标签

# kubectl get node --show-labels |grep app=nginx
k8s-node01   Ready    <none>   13d   v1.19.0   app=nginx,beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,
kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-node01,kubernetes.io/os=linux

# cat nginx-deployment.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      nodeSelector:                           # 选择node标签为app=nginx的标签进行调度
        app: nginx
      containers:
      - name: mynginx
        image: nginx:1.18.0
        ports:
        - containerPort: 80
        resources:
          requests:
            cpu: 100m
            memory: 128Mi
          limits:
            cpu: 1000m
            memory: 1024Mi

# kubectl apply -f nginx-deployment.yaml 

# kubectl get pod -o wide                      # 所有的pod都调度到了k8s-node01节点上
NAME                                READY   STATUS    RESTARTS   AGE   IP              NODE         NOMINATED NODE   READINESS GATES
nginx-deployment-7cc69bccbc-ffn88   1/1     Running   0          45s   10.244.85.205   k8s-node01   <none>           <none>
nginx-deployment-7cc69bccbc-mmqgw   1/1     Running   0          42s   10.244.85.207   k8s-node01   <none>           <none>
nginx-deployment-7cc69bccbc-t45gg   1/1     Running   0          43s   10.244.85.206   k8s-node01   <none>           <none>
  • 11.3 NodeAffinity Node亲和性调度

是用于替换NodeSelector的全新调度策略。目前有两种节点亲和性表达。

RequiredDuringSchedulingIgnoredDuringExecution:必须满足指定的规则才可以调度Pod到Node上( 功能与nodeSelector很像,但是使用的是不同的语法),相当于硬限制。

PreferredDuringSchedulingIgnoredDuringExecution:强调优先满足指定规则,调度器会尝试调度Pod到Node上, 但并不强求,相当于软限制。多个优先级规则还可以设置权重(weight)值,以定义执行的先后顺序。

# 举例
apiVersion: v1
kind: Pod
metadata:
  name: with-node-affinity
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:      # 硬亲和性,要求pod在amd64节点上运行
        nodeSelectorTerms:
        - matchExpressions:
          - key: beta.kubernetes.io/arch
            operator: In
            values:
            - amd64
      preferredDuringSchedulingIgnoredDuringExecution:     # 软亲和性,尽量运行在磁盘类型为ssd的节点上
      - weight: 1
        preference:
          matchExpressions:
          - key: disk-type
            operator: In
            values:
            - ssd
  containers:
  - name: with-node-affinity
    image: k8s.gcr.io/pause:2.0

NodeAffinity语法支持的操作符包括In、 NotIn、 Exists、 DoesNotExist、 Gt、 Lt。
NodeAffinity规则设置的注意事项如下:
  如果同时定义了nodeSelector和nodeAffinity,那么必须两个条件都得到满足,Pod才能最终运行在指定的Node上。
  如果nodeAffinity指定了多个nodeSelectorTerms,那么其中一个能够匹配成功即可。
  如果在nodeSelectorTerms中有多个matchExpressions,则一个节点必须满足所有matchExpressions才能运行该Pod 

  • 11.4 PodAffinity POD亲和性

和节点亲和相同, Pod亲和与互斥的条件设置也是requiredDuringSchedulingIgnoredDuringExecution和preferredDuringSchedulingIgnoredDuringExecution。

# 举例pod亲和性,三个pod在运行在同一个k8s节点上
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  namespace: default
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      affinity:
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - nginx
            topologyKey: kubernetes.io/hostname  # 同一个node为一个拓扑域
      containers:
      - name: mynginx
        image: nginx:1.18.0
        ports:
        - containerPort: 80
        resources:
          requests:
            cpu: 500m
            memory: 1024Mi
          limits:
            cpu: 1000m
            memory: 2048Mi

# 举例,pod反亲和性三个pod尽量运行在不同的k8s同节点上
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  namespace: default
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 100
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - key: app
                  operator: In
                  values:
                  - nginx
              topologyKey: kubernetes.io/hostname
      containers:
      - name: mynginx
        image: nginx:1.18.0
        ports:
        - containerPort: 80
        resources:
          requests:
            cpu: 500m
            memory: 1024Mi
          limits:
            cpu: 1000m
            memory: 2048Mi

十二、污点(Taint)与容忍(Toleration)

 Taint需要和Toleration配合使用,让Pod避开那些不合适的Node。在Node上设置一个或多个Taint之后,除非Pod明确声明能够容忍这些污点, 否则无法在这些Node上运行。Toleration是Pod的属性, 让Pod能够(注意, 只是能够, 而非必须)运行在标注了Taint的Node上。

污点(Taint)

使用`kubectl taint `命令可以给革个Node节点设置污点,Node被设置上污点之后就和Pod之间存在一种相斥的关系,可以让Node拒绝Pod的高度执行,甚至将Node上已经存在的Pod驱逐出去。 

# 每个污点的组成如下
key=vavlue:effect
# 每个污点有一个key和value作为污点标签,其中value可以为空,effec描述污点的作用。当前tain effect支持以下三个选项:

NoSchedule: 表示K8s不会将Pod调度到具有该污点的Node上
PreferNoSchedule:表示k8s将尽量避免将Pod调度到具有该污点的Node上
NoExecute:表示K8s将不会将Pod调度到具有该污点的Node上,同时会将Node上已经存在的Pod驱逐出去。

注:使用noexecute,k8s会直接杀死节点上的所有的pod,并在其它节点上面重新pod,如果业务都在这一个节点上业务可能会出现同时不可用的情况,建议使用for循环来杀pod。
例:for i in `kubectl get pod -n default -l app=nginx | awk {'print $1'}`; do kubectl delete pod $i -n default; done

命令设置

# 设置污点
kubectl taint node k8s-node01 key=value:NoSchedule

# 查看污点
# kubectl describe node k8s-node01 | grep Taints
Taints:             key=value:NoSchedule

# 移出污点
kubectl taint node k8s-node01 key=value:NoSchedule-

 容忍(Tolerations)
设置了污点的Node将根据taint的effect:NoSchedule、PreferNoSchedule、NoExecute和Pod之间产生互斥的关系,pod将在一定程度上不会调度到Node上,但我们可以在Pod上设置容忍,意思是设置了容忍的Pod将可以容忍污点的存在,可以被调度到存在污点的Node上。

系统允许在同一个Node上设置多个Taint,也可以在Pod上设置多个Toleration。 Kubernetes调度器处理多个Taint和Toleration的逻辑顺序为: 首先列出节点中所有的Taint,然后忽略Pod的Toleration能够匹配的部分, 剩下的没有忽略的Taint就是对Pod的效果了。

一般来说, 如果给Node加上effect=NoExecute的Taint, 那么在该Node上正在运行的所有无对应Toleration的Pod都会被立刻驱逐, 而具有相应Toleration的Pod永远不会被驱逐。 不过, 系统允许给具有NoExecute效果的Toleration加入一个可选的tolerationSeconds字段, 这个设置表明Pod可以在Taint添加到Node之后还能在这个Node上运行多久(单位为s) :

# cat nginx19.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: pod-3
  labels:
    app: pod-3
spec:
  containers:
  - name: pod-3
    image: nginx:1.18.0
  tolerations:
  - key: "key"                       # 要与Node上设置的taint保持一致
    operator: "Equal"
    value: "value"                  # 要与Node上设置的taint保持一致
    effect: "NoExecute"          # 要与Node上设置的taint保持一致
    tolerationSeconds: 3600

十三、固定节点调度nodeName

Pod.spec.nodeName将pod直接调度到指定的node节点上,会跳过schedule的调度策略,该匹配规则是强制匹配。

# cat nginx8.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 5
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      nodeName: k8s-node01
      containers:
      - name: mynginx
        image: nginx:1.18.0
        ports:
        - containerPort: 80
        resources:
          requests:
            cpu: 100m
            memory: 128Mi
          limits:
            cpu: 1000m
            memory: 1024Mi