[云原生] K8s之pod进阶

2024-03-05 1349阅读

温馨提示:这篇文章已超过379天没有更新,请注意相关的内容是否还可用!

一、pod的状态说明

(1)Pod 一直处于Pending状态

Pending状态意味着Pod的YAML文件已经提交给Kubernetes,API对象已经被创建并保存在Etcd当中。但是,这个Pod里有些容器因为某种原因而不能被顺利创建。比如,调度不成功(可以通过kubectl describe pod命令查看到当前Pod的事件,进而判断为什么没有调度)。

可能原因:资源不足(集群内所有的Node都不满足该Pod请求的CPU、内存、GPU等资源);      HostPort  已被占用(通常推荐使用Service对外开放服务端口)。

(2)Pod一直处于Waiting 或 ContainerCreating状态

首先还是通过 kubectl describe pod命令查看当前Pod的事件。可能的原因有:

1)镜像拉取失败,比如镜像地址配置错误、拉取不了国外镜像源(gcr.io)、私有镜像密钥配置错误、镜像太大导致拉取超时 (可以适当调整kubelet的-image-pull-progress-deadline和-runtime-request-timeout选项)等。

2)CNI网络错误,一般需要检查CNI网络插件的配置,比如:无法配置Pod 网络、无法分配IP地址。

3)容器无法启动,需要检查是否打包了正确的镜像或者是否配置了正确的容器参数

4)Failed create pod sandbox,查看kubelet日志,原因可能是磁盘坏道(input/output error)。

(3)Pod 一直处于ImagePullBackOff状态


通常是镜像名称配置错误或者私有镜像的密钥配置错误导致。

(4)Pod 一直处于CrashLoopBackOff状态

 
此状态说明容器曾经启动了,但又异常退出。这时可以先查看一下容器的日志。

通过命令kubectl logs 和kubectl logs --previous 可以发下一些容器退出的原因,比如:容器进程退出、健康检查失败退出;此时如果还未发现线索,还而已到容器内执行命令(kubectl exec cassandra - cat /var.log/cassandra/system.loq)来进一步查看退出原因;如果还是没有线索,那就需要SSH登录该Pod所在的Node上,查看Kubelet或者Docker的日志进一步排查。

(5)  Pod处于Error状态


通常处于Error状态说明Pod启动过程中发生了错误。

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

(6)  Pod 处于Terminating或 Unknown状态

 从v1.5开始,Kubernetes不会因为Node失联而删除其上正在运行的Pod,而是将其标记为Terminating 或 Unknown 状态。想要删除这些状态的Pod有三种方法:

1)从集群中删除Node。使用公有云时,kube-controller-manager会在VM删除后自动删除对应的Node。而在物理机部署的集群中,需要管理员手动删除Node(kubectl delete node)。

2)Node恢复正常。kubelet会重新跟kube-apiserver通信确认这些Pod的期待状态,进而再决定删除或者继续运行这些Pod。用户强制删除,用户可以执行(kubectl delete pods pod-name --grace-period=0 --force)强制删除Pod。除非明确知道Pod的确处于停止状态(比如Node所在VM或物理机已经关机),否则不建议使用该方法。特别是StatefulSet 管理的Pod,强制删除容易导致脑裂或数据丢失等问题。

3)Pod行为异常,这里所说的行为异常是指Pod没有按预期的行为执行,比如没有运行podSpec 里面设置的命令行参数。这一般是podSpec yaml文件内容有误,可以尝试使用 --validate 参数重建容器,比如(kubectl delete pod mypod 和 kubectl create --validate -f mypod.yaml);也可以查看创建后的podSpec是否是对的,比如(kubectl get pod mypod -o yaml);修改静态Pod的Manifest后未自动重建,kubelet 使用inotify 机制检测 /etc/kubernetes/manifests 目录(可通过 kubelet 的 -pod-manifest-path 选项指定)中静态Pod的变化,并在文件发生变化后重新创建相应的 Pod。但有时也会发现修改静态Pod的 Manifest后未自动创建新 Pod的情景,此时已过简单的修复方法是重启 Kubelet。

Unknown 这个异常状态意味着Pod的状态不能持续地被 kubelet汇报给 kube-apiserver,这很有可能是主从节点(Master 和 Kubelet)间的通信出现了问题。

(7)pod从创建到成功或失败的事件

PodScheduled

pod正处于调度中,刚开始调度的时候,hostip还没绑定上,持续调度之后,有合适的节点就会绑定hostip,然后更新etcd数据

Initialized

pod中的所有初始化容器已经初启动完毕

Ready

pod中的容器可以提供服务了

Unschedulable

不能调度,没有合适的节点

 

CrashLoopBackOff:    容器退出,kubelet正在将它重启

InvalidImageName:    无法解析镜像名称

ImageInspectError:   无法校验镜像

ErrImageNeverPull:   策略禁止拉取镜像

ImagePullBackOff:    正在重试拉取

RegistryUnavailable: 连接不到镜像中心

ErrImagePull:        通用的拉取镜像出错

CreateContainerConfigError: 不能创建kubelet使用的容器配置

CreateContainerError: 创建容器失败

m.internalLifecycle.PreStartContainer 执行hook报错

RunContainerError:   启动容器失败

PostStartHookError:   执行hook报错

ContainersNotInitialized: 容器没有初始化完毕

ContainersNotReady:   容器没有准备完毕

ContainerCreating:    容器创建中

PodInitializing:pod   初始化中

DockerDaemonNotReady:  docker还没有完全启动

NetworkPluginNotReady: 网络插件还没有完全启动

Evicte:     pod被驱赶

二、Pod 容器资源限制 

2.1 资源限制的了解 

  在我前面Docker的Cgroup文章中,就提到过,为什么我们对容器进行资源限制。同理:首先K8s中pod使用宿主机的资源默认情况下是无节制的,但是当一个集群搭建成功后并投入生产环境中。如果其中的某一个pod因为不明原因出现了bug,疯狂占用宿主机资源,抢占其他pod的资源。势必会导致整个集群的瘫痪,所以pod资源的限制是非常有必要的

 在资源控制器中我们也可以准确的找到相应的资源控制字段:

kubectl explain deployment.spec.template.spec.containers.resources
kubectl explain  statefulset.spec.template.spec.containers.resources
 

[云原生] K8s之pod进阶

[云原生] K8s之pod进阶

在官方文档中(资源配额 | Kubernetes) 我们可以得知:pod控制的资源总共为三大类:

cpu,memory,hugepages(巨页)。其中我们运用最多的限制还是cpu和memory。

 (1)cpu限制的参数了解

 pod对于cpu限制的参数有两种表达形式:

第一种是指定个数的表达形式,例如:1 ,2, 0.5 ,0.2 ,0.3 指定cpu的个数(该个数可以为整数,也可以为小数点后一位的小数)

第二种是以毫核为单位的表达形式:100m  500m   1000m   2000m (这里1000m等价于一个cpu,该方式来自于cpu时间分片原理得来) 

(2)memory的表达形式 

pod对内存参数的要求十分严谨。对硬件有过研究的朋友,应该会了解到,我们常常提到的GB,MB,TB其实是于实际字节总数是有误差的(原因是GB是以10为底数计量,而真正的换算是以2为底数计量。导致了总量的误差。)而真正没有此误差的单位是Ki  Mi  Gi  Ti 

所以k8s中pod资源限制为了对pod的内存限制更为精准,采用的单位是 Ki  Mi  Gi  Ti 

2.2 实例运用 

需求:创建一个deployment资源,镜像使用nginx:latest,创建3个pod副本,镜像拉取策略使用IfNotPresent,重启策略使用Always,容器资源预留cpu 0.25个,内存128MiB,上限最多1个cpu,1g内存

kubectl create deployement nginx-test --images=nginx:latest  --dry-run=client -o yaml >test.yaml
vim test.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: nginx-test
  name: nginx-test
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx-test
  template:
    metadata:
      labels:
        app: nginx-test
    spec:
      containers:
      - image: nginx:latest
        name: nginx
        resources:
          requests:
            memory: "128Mi"
            cpu: "250m"
          limits:
            memory: "1Gi"
            cpu: "1"
        imagePullPolicy: IfNotPresent
      restartPolicy: Always
 
 
kubectl apply -f test.yaml
#查看pod的资源使用情况
kubectl describe pod nginx-test

[云原生] K8s之pod进阶

#查看node节点pod资源使用详情
kubectl describe node node02

[云原生] K8s之pod进阶

三、Pod 容器的探针 

3.1 探针的概念及其作用 

 探针是由 kubelet 对容器执行的定期诊断(pod中探针又分为三类):

 存活探针(livenessProbe)探测容器是否运行正常。如果探测失败则kubelet杀掉容器(不是Pod),容器会根据重启策略决定是否重启

就绪探针(readinessProbe)探测Pod是否能够进入READY状态,并做好接收请求的准备。如果探测失败Pod则会进入NOTREADY状态(READY为0/1)并且从所关联的service资源的端点(endpoints)中踢出,service将不会再把访问请求转发给这个Pod

启动探针(startupProbe)探测容器内的应用是否启动成功,在启动探针探测成功之前,其它类型的探针都会暂时处于禁用状态 

 注意:启动探针只是在容器启动后按照配置满足一次后就不再进行后续的探测了。存活探针和就绪探针会一直探测到Pod生命周期结束为止

kubectl explain pod.spec.containers

[云原生] K8s之pod进阶

3.2 探针的探测方式  

exec : 通过command字段设置在容器内执行的Linux命令来进行探测,如果命令返回码为0,则认为探测成功,返回码非0则探测失败

httpGet : 通过向容器的指定端口和uri路径发起HTTP GET请求,如果HTTP返回状态码为 >=200且 index1.html # exit kubectl get pods kubectl exec -it readiness-httpget -- rm -rf /usr/share/nginx/html/index.html kubectl get pods -w

[云原生] K8s之pod进阶 [云原生] K8s之pod进阶

vim readiness-myapp.yaml
apiVersion: v1
kind: Pod
metadata:
  name: myapp1
  labels:
     app: myapp
spec:
  containers:
  - name: myapp
    image: soscscs/myapp:v1
    ports:
    - name: http
      containerPort: 80
    readinessProbe:
      httpGet:
        port: 80
        path: /index.html
      initialDelaySeconds: 5
      periodSeconds: 5
      timeoutSeconds: 10 
---
apiVersion: v1
kind: Pod
metadata:
  name: myapp2
  labels:
     app: myapp
spec:
  containers:
  - name: myapp
    image: soscscs/myapp:v1
    ports:
    - name: http
      containerPort: 80
    readinessProbe:
      httpGet:
        port: 80
        path: /index.html
      initialDelaySeconds: 5
      periodSeconds: 5
      timeoutSeconds: 10 
---
apiVersion: v1
kind: Pod
metadata:
  name: myapp3
  labels:
     app: myapp
spec:
  containers:
  - name: myapp
    image: soscscs/myapp:v1
    ports:
    - name: http
      containerPort: 80
    readinessProbe:
      httpGet:
        port: 80
        path: /index.html
      initialDelaySeconds: 5
      periodSeconds: 5
      timeoutSeconds: 10 
---
apiVersion: v1
kind: Service
metadata:
  name: myapp
spec:
  selector:
    app: myapp
  type: ClusterIP
  ports:
  - name: http
    port: 80
    targetPort: 80
kubectl create -f readiness-myapp.yaml
kubectl get pods,svc,endpoints -o wide
NAME         READY   STATUS    RESTARTS   AGE     IP            NODE     NOMINATED NODE   READINESS GATES
pod/myapp1   1/1     Running   0          3m42s   10.244.2.13   node02              
pod/myapp2   1/1     Running   0          3m42s   10.244.1.15   node01              
pod/myapp3   1/1     Running   0          3m42s   10.244.2.14   node02              
NAME                 TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE     SELECTOR
......
service/myapp        ClusterIP   10.96.138.13           80/TCP    3m42s   app=myapp
NAME                   ENDPOINTS                                      AGE
......
endpoints/myapp        10.244.1.15:80,10.244.2.13:80,10.244.2.14:80   3m42s
kubectl exec -it pod/myapp1 -- rm -rf /usr/share/nginx/html/index.html
//readiness探测失败,Pod 无法进入READY状态,且端点控制器将从 endpoints 中剔除删除该 Pod 的 IP 地址
kubectl get pods,svc,endpoints -o wide
NAME         READY   STATUS    RESTARTS   AGE     IP            NODE     NOMINATED NODE   READINESS GATES
pod/myapp1   0/1     Running   0          5m17s   10.244.2.13   node02              
pod/myapp2   1/1     Running   0          5m17s   10.244.1.15   node01              
pod/myapp3   1/1     Running   0          5m17s   10.244.2.14   node02              
NAME                 TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE     SELECTOR
......
service/myapp        ClusterIP   10.96.138.13           80/TCP    5m17s   app=myapp
NAME                   ENDPOINTS                       AGE
......
endpoints/myapp        10.244.1.15:80,10.244.2.14:80   5m17s

[云原生] K8s之pod进阶

(3)启动探针的使用  

启动探针存在的必要:

有时候,会有一些现有的应用在启动时需要较长的初始化时间。 要这种情况下,若要不影响对死锁作出快速响应的探测,设置存活探测参数是要技巧的。 技巧就是使用相同的命令来设置启动探测,针对 HTTP 或 TCP 检测,可以通过将 failureThreshold * periodSeconds 参数设置为足够长的时间来应对糟糕情况下的启动时间。(因为启动探针在启动后,其他的两种探针就会进入短暂的禁用装态,于是给容器启动预留了充足的时间,保证容器中业务启动的顺利进行。而且启动探针只会启动一次,后续工作就完全交给其他两个探针,直到容器的生命周期结束)。

ports:
- name: liveness-port
  containerPort: 8080
  hostPort: 8080
 
livenessProbe:
  httpGet:
    path: /healthz
    port: liveness-port
  failureThreshold: 1
  periodSeconds: 10
 
startupProbe:
  httpGet:
    path: /healthz
    port: liveness-port
  failureThreshold: 30
  periodSeconds: 10

在这里,幸亏有了启动探测,应用程序将会有最多 5 分钟(30 * 10 = 300s)的时间来完成其启动过程。 一旦启动探测成功一次,存活探测任务就会接管对容器的探测,对容器死锁作出快速响应。 如果启动探测一直没有成功,容器会在 300 秒后被杀死,并且根据 restartPolicy 来执行进一步处置。 

四、Pod 容器的启动和退出动作 

postStart    配置 exec.command 字段设置 Linux 命令,实现当应用容器启动时,会执行的额外操作

preStop      配置 exec.command 字段设置 Linux 命令,实现当应用容器退出时,会执行的最后一个操作

实例运用
apiVersion: v1
kind: Pod
metadata:
  name: lifecycle-demo
spec:
  containers:
  - name: lifecycle-demo-container
    image: soscscs/myapp:v1
    lifecycle:   #此为关键字段
      postStart:
        exec:
          command: ["/bin/sh", "-c", "echo Hello from the postStart handler >> /var/log/nginx/message"]      
      preStop:
        exec:
          command: ["/bin/sh", "-c", "echo Hello from the prestop handler >> /var/log/nginx/message"]
    volumeMounts:
    - name: message-log
      mountPath: /var/log/nginx/
      readOnly: false
  initContainers:
  - name: init-myservice
    image: soscscs/myapp:v1
    command: ["/bin/sh", "-c", "echo 'Hello initContainers'   >> /var/log/nginx/message"]
    volumeMounts:
    - name: message-log
      mountPath: /var/log/nginx/
      readOnly: false
  volumes:
  - name: message-log
    hostPath:
      path: /data/volumes/nginx/log/
      type: DirectoryOrCreate
kubectl create -f post.yaml
kubectl get pods -o wide
NAME             READY   STATUS    RESTARTS   AGE    IP            NODE     NOMINATED NODE   READINESS GATES
lifecycle-demo   1/1     Running   0          2m8s   10.244.2.28   node02              
kubectl exec -it lifecycle-demo -- cat /var/log/nginx/message
Hello initContainers
Hello from the postStart handler
//在 node02 节点上查看
[root@node02 ~]# cd /data/volumes/nginx/log/
[root@node02 log]# ls
access.log  error.log  message
[root@node02 log]# cat message 
Hello initContainers
Hello from the postStart handler
#由上可知,init Container先执行,然后当一个主容器启动后,Kubernetes 将立即发送 postStart 事件。
//删除 pod 后,再在 node02 节点上查看
kubectl delete pod lifecycle-demo
[root@node02 log]# cat message 
Hello initContainers
Hello from the postStart handler
Hello from the poststop handler
#由上可知,当在容器被终结之前, Kubernetes 将发送一个 preStop 事件。

[云原生] K8s之pod进阶 [云原生] K8s之pod进阶

VPS购买请点击我

文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

目录[+]