天天看点

k8s——Pod的基本概念(2)

作者:fana

pod文件定义

yaml的基本语法规则如下:

  • 大小写敏感
  • 使用缩进表示层级关系
  • 缩进时不允许使用Tab键,只允许使用空格。
  • 缩进的空格数目不重要,只要相同层级的元素左侧对齐即可
  • #表示注释,从这个字符一直到行尾,都会被解析器忽略。
apiVersion: v1
 kind: Pod
 metadata:
   name: string
   namaspace: string
   labels:
   - name: string
       annotations:
   - name: string
   spec:
     containers:
   - name: string
     images: string
     imagePullPolice: [Always | Never | IfNotPresent]
     command: [string]
     args: [string]
     workingDir: string
     volumeMounts:
     - name: string
       mountPath: string
       readOnly: boolean
       ports:
     - name: string
       containerPort: int
       hostPort: int
       protocol: string
       env:
     - name: string
       value: string
       resources:
       limits:
         cpu: string
         memory: string
       requests:
         cpu: string
         memory: string
       livenessProbe:
       exec:
         command: [string]
       httpGet:
         path: string
         port: int
         host: string
         scheme: string
         httpHeaders:
         - name: string
           value: string
           tcpSocket:
             port: int
           initialDelaySeconds: number
           timeoutSeconds: number
           periodSeconds: number
           successThreshold: 0
           failureThreshold: 0
           securityContext:
           privileged: false
           restartPolicy: [Always | Never | OnFailure]   
           nodeSelector: object
           imagePullSecrets:
   - name: string
       hostNetwork: false
         volumes:
   - name: string
     emptyDir: {}
     hostPath:
       path: string
     secret:
       secretName: string
       items:
       - key: string
         path: string
         configMap:
           name: string
           items:
       - key: string
         path: string           

包含init容器的定义:一个应用容器和两个init容器

apiVersion: v1
 kind: Pod
 metadata:
   name: test-init-pod
   labels:
     app: init
 spec:
   containers:
   - name: myapp
     image: busybox
     resources:
       requests:
         cpu: 100m
     command: ['sh', '-c', 'echo The app is running! && sleep 3600']
   initContainers:
   - name: init-01
     image: busybox
     command: ['sh', '-c', 'sleep 10']
     resources:
       requests:
         cpu: 20m
   - name: init-02
     image: busybox
     command: ['sh', '-c', 'sleep 10']
     resources:
       requests:
         cpu: 30m           

kube-scheduler调度pod的方式

  • 当只有应用容器时:由于应用容器是同时运行的,因此为了保证应用容器的正常运行,请求的资源总量应当是所有应用容器的各自请求的资源之和。
  • 当有init容器时:由于init容器会先于应用容器运行,只有当init运行成功并且退出后,应用容器才会运行,因此为了保证所有的容器(不仅包括应用容器,还包括init容器)的运行,pod的资源总量的计算公式如下: max(应用容器请求资源之和,max(所有的init容器请求资源))

如果为一个Pod指定了多个Init容器,那些容器会按顺序一次运行一个。只有当前面的Init容器必须运行成功后,才可以运行下一个Init容器。当所有的Init容器运行完成后,k8s才初始化Pod和运行应用容器。

Pod重启,会导致Init容器重新执行,主要有如下几个原因:

  • 用户更新PodSpec导致Init容器镜像发生改变。应用容器镜像的变更只会重启应用容器。
  • Pod基础设施容器被重启。这不多见,但某些具有root权限可访问Node的人可能会这样做。
  • 当restartPolicy设置为Always,Pod中所有容器会终止,强制重启,由于垃圾收集导致Init容器完整的记录丢失。

Pod hook

是由Kubernetes管理的kubelet发起的,当容器中的进程启动前或者容器中的进程终止之前运行,这是包含在容器的生命周期之中。可以同时为Pod中的所有容器都配置hook,包括两种:

  • exec:执行一段命令
  • HTTP:发送HTTP请求。

postStart在容器创建之后(但并不能保证钩子会在容器ENTRYPOINT之前)执行,这时候Pod已经被调度到某台node上,被某个kubelet管理了,这时候kubelet会调用postStart操作,该操作跟容器的启动命令是在同步执行的,也就是说在postStart操作执行完成之前,kubelet会锁住容器,不让应用程序的进程启动,只有在postStart 操作完成之后容器的状态才会被设置成为RUNNING。

PreStop在容器终止之前被同步阻塞调用,常用于在容器结束前优雅的释放资源。如果postStart或者preStop hook失败,将会终止容器

apiVersion: v1
 kind: Pod
 metadata:
   name: lifecycle-demo
 spec:
   containers:
   - name: lifecycle-demo-container
     image: nginx
     lifecycle:
       postStart:
         exec:
           command: ["/bin/sh", "-c", "echo Hello from the postStart handler> /usr/share/message"]
       preStop:
         exec:
           command: ["/usr/sbin/nginx","-s","quit"]
 
 #Hook调用的日志没有暴露给Pod的event,所以只能通过describe命令来获取,如果有错误将可以看到FailedPostStartHook或FailedPreStopHook这样的event。           

Pod Preset

Preset就是预设,有时候想要让一批容器在启动的时候就注入一些信息,比如secret、volume、volume mount和环境变量,而又不想一个一个的改这些Pod的template,这时候就可以用到PodPreset这个资源对象了。但是又在v1.20版本取消了。

#1.编写Preset YAML
 apiVersion: settings.k8s.io/v1alpha1
 kind: PodPreset
 metadata:
   name: demo-podpreset
 spec:
   selector:
     matchLabels:
       role: developer
   volumeMounts:
     - mountPath: /cache
       name: cache-volume
   volumes:
     - name: cache-volume
       emptyDir: {}
 
 #2.Pod YAML
 apiVersion: v1
 kind: Pod
 metadata:
   name: test-web
   labels:
     app: test-web
     role: developer
 spec:
   containers:
     - name: website
       image: nginx
       ports:
         - containerPort: 80
 
 #3.最终的
 kubectl get pod test-web -o yaml
 # 查询结果
 apiVersion: v1
 kind: Pod
 metadata:
   name: test-web
   labels:
     app: test-web
     role: developer
   annotations:
     podpreset.admission.kubernetes.io/podpreset-allow-database: "resource version"
 spec:
   containers:
     - name: website
       image: nginx
       volumeMounts:
         - mountPath: /cache
           name: cache-volume
       ports:
         - containerPort: 80
   volumes:
     - name: cache-volume
       emptyDir: {}           

Pod中断与PDB

kubectl drain将某个节点上的容器驱逐走的时候, kubernetes会依据Pod的PodDistruptionBuget来进行Pod的驱逐。如果不设置任何明确的PodDistruptionBuget,Pod将会被直接杀死,然后在别的节点重新调度,这可能导致服务中断!

非自愿中断:指是由那些不可控因数导致Pod中断退出操作。Pod不会消失,直到有人将其销毁,或者当出现不可避免的硬件或系统软件错误。我们把这些不可避免的情况称为应用的非自愿性中断。例如:

  • 后端节点物理机的硬件故障
  • 集群管理员错误地删除虚拟机(实例)
  • 云提供商或管理程序故障使虚拟机消失
  • 内核恐慌(kernel panic)
  • 节点由于集群网络分区而从集群中消失
  • 由于节点资源不足而将容器逐出

自愿中断:指由用户特地执行的管理操作导致Pod中断则称为自愿中断,典型的应用程序所有者操作包括:

  • 删除管理该pod的Deployment或其他控制器
  • 更新了Deployment的pod模板导致pod重启
  • 直接删除pod(意外删除)
  • 排空drain节点进行修复或升级。
  • 从集群中排空节点以缩小集群。
  • 从节点中移除一个pod,以允许其他pod使用该节点。
#尽管Deployment或ReplicaSet一类的控制器能够确保相应Pod对象的副本数量不断逼近期望的数量,但它却无法保证在某一时刻一定会纯在指定数量或比例的Pod对象,然而这种需求在某些强调发我可用性的场景中却是必备的。于是,Kubernetes自从1.4版本开始引入Pod中断预算(PodDisruptionBudget,简称PDB)类型的资源,用于为那些自愿的(Voluntary)中断做好预算方案(Budget),限制可自愿中断的最大Pod副本数或确保最少可用的Pod副本数,以确保服务的高可用性。
 
 
 #PDB是一个单独的CR自定义资源
 apiVersion: policy/v1beta1
 kind: PodDisruptionBudget
 metadata:
   name: podinfo-pdb
 spec:
   #如果不满足PDB,Pod驱逐将会失败!
   minAvailable: 1    #最少也要维持一个Pod可用
 # maxUnavailable: 1  #最大不可用的Pod数,与minAvailable不能同时配置!二选一
   selector:
     matchLabels:
       app: podinfo           

pod生命周期

Pod的生命周期一般通过Controler的方式管理,每种Controller都会包含PodTemplate来指明Pod的相关属性,Controller可以自动对pod的异常状态进行重新调度和恢复,除非通过Controller的方式删除其管理的Pod,不然kubernetes始终运行用户预期状态的Pod。

控制器的分类

  • 使用Job运行预期会终止的Pod,例如批量计算。Job仅适用于重启策略为OnFailure或 Never的Pod。
  • 对预期不会终止的Pod使用ReplicationController、ReplicaSet和Deployment,例如Web服务器。 ReplicationController仅适用于具有restartPolicy为Always的Pod。
  • 提供特定于机器的系统服务,使用DaemonSet为每台机器运行一个Pod 。

如果节点死亡或与集群的其余部分断开连接,则Kubernetes将应用一个策略将丢失节点上的所有Pod的phase设置为Failed。

Pod的phase是Pod生命周期中的简单宏观描述,定义在Pod的PodStatus对象的phase字段中。

状态值 说明
Pending Pod已被Kubernetes系统接受,但有一个或者多个容器镜像尚未创建。等待时间包括调度Pod的时间和通过网络下载镜像的时间。
Running 该Pod已经绑定到了一个节点上,Pod中所有的容器都已被创建。至少有一个容器正在运行,或者正处于启动或重启状态。
Succeeded Pod中的所有容器都被成功终止,并且不会再重启。
Failed Pod中的所有容器都已终止了,并且至少有一个容器是因为失败终止。也就是说,容器以非0状态退出或者被系统终止。
Unknown 因为某些原因无法取得Pod的状态,通常是因为与Pod所在主机通信失败。

Pod有一个PodStatus对象,其中包含一个PodCondition 数组。 PodCondition包含以下以下字段:

  • lastProbeTime:Pod condition最后一次被探测到的时间戳。
  • lastTransitionTime:Pod最后一次状态转变的时间戳。
  • message:状态转化的信息,一般为报错信息,例如:containers with unready status: [c-1]。
  • reason:最后一次状态形成的原因,一般为报错原因,例如:ContainersNotReady。
  • status:包含的值有True、False和Unknown。
  • type:Pod状态的几种类型。

其中type字段包含以下几个值:

  • PodScheduled:Pod已经被调度到运行节点。
  • Ready:Pod已经可以接收请求提供服务。
  • Initialized:所有的init container已经成功启动。
  • Unschedulable:无法调度该Pod,例如节点资源不够。
  • ContainersReady:Pod中的所有容器已准备就绪。

pod健康检查

Kubelet可以选择是否在容器上运行探针执行和做出反应

  • startupProbe判断容器内的应用程序是否已启动。如果提供了启动探测,则禁用所有其他探测,直到它成功为止。如果启动探测失败,kubelet将杀死容器,容器将服从其重启策略。如果容器没有提供启动探测,则默认状态为成功。
  • livenessProbe:指示容器是否正在运行。如果存活探测失败,则kubelet会杀死容器,并且容器将受到其重启策略的影响。如果容器不提供存活探针,则默认状态为Success
  • readinessProbe:指示容器是否准备好服务请求。如果就绪探测失败,端点控制器将从与Pod匹配的所有Service的端点中删除该Pod的 IP 地址。初始延迟之前的就绪状态默认为Failure。如果容器不提供就绪探针,则默认状态为Success

以上探针支持的三种类型

  • Exec:Probe执行容器中的命令并检查命令退出的状态码,如果状态码为0则说明已经就绪。
  • HTTP GET:往容器的IP:Port发送HTTP GET请求,如果Probe收到2xx或3xx,说明已经就绪。
  • TCP Socket:尝试与容器建立TCP连接,如果能建立连接说明已经就绪。

Pod提供了三种探针,均支持使用Command、HTTP API、TCP Socket这三种手段来进行服务可用性探测。每个探针都有三个结果之一:

  • Success:容器已通过诊断。
  • Failure:容器未通过诊断。
  • Unknown:诊断失败,因此不会采取任何行动。

Pod通过restartPolicy字段指定重启策略,重启策略类型为:Always、OnFailure和Never,默认为 Always。

  • Always:当容器失效时,由kubelet自动重启该容器
  • OnFailure:当容器终止运行且退出码不为0时,由kubelet自动重启该容器
  • Never:不论容器运行状态如何,kubelet都不会重启该容器

可以管理Pod的控制器有Replication Controller,Job,DaemonSet,及kubelet(静态Pod)。

RC和DaemonSet:必须设置为Always,需要保证该容器持续运行。

Job:OnFailure或Never,确保容器执行完后不再重启。

kubelet:在Pod失效的时候重启它,不论RestartPolicy设置为什么值,并且不会对Pod进行健康检查。

apiVersion: v1
 kind: Pod
 metadata:
   name: liveness-exec
 spec:
   containers:
   - name: liveness
     image: tomcagcr.io/google_containers/busybox
     args:
     - /bin/sh
     - -c
     - echo ok > /tmp/health;sleep 10;rm -fr /tmp/health;sleep 600
     livenessProbe:
       exec:
         command:
         - cat
         - /tmp/health
       initialDelaySeconds: 15
       timeoutSeconds: 1           
apiVersion: v1
 kind: Pod
 metadata:
   name: pod-with-healthcheck
 spec:
   containers:
   - name: nginx
     image: nginx
     ports:
     - containnerPort: 80
     livenessProbe:
       tcpSocket:
         port: 80
       initialDelaySeconds: 15
       timeoutSeconds: 1           
apiVersion: v1
 kind: Pod
 metadata:
   name: pod-with-healthcheck
 spec:
   containers:
   - name: nginx
     image: nginx
     ports:
     - containnerPort: 80
     livenessProbe:
       httpGet:
         path: /_status/healthz
         port: 80
       initialDelaySeconds: 15
       timeoutSeconds: 1           

Pod正在运行,并只有一个容器。容器退出成功。

  • 记录完成事件
  • 如果restartPolicy是:
    • Always: 重启容器,Pod的phase保持Running
    • OnFailure: Pod的phase变成Succeeded.
    • Never: Pod的phase变成Succeeded.

Pod正在运行,并只有一个容器。容器退出失败。

  • 记录失败事件
  • 如果restartPolicy是:
    • Always: 重启容器,Pod的phase保持Running.
    • OnFailure: 重启容器,Pod的phase保持Running.
    • Never: Pod的phase变成Failed.

Pod正在运行,并且有两个容器,Container 1退出失败。

  • 记录失败事件
  • 如果restartPolicy是:
    • Always:重启容器,Pod的phase保持Running.
    • OnFailure:重启容器; Pod的phase保持Running.
    • Never:不会重启容器,Pod的phase保持Running.

如果Container 1不在运行,Container退出

  • 记录失败事件
  • 如果restartPolicy是:
    • Always:重启容器,Pod的phase保持Running.
    • OnFailure:重启容器,Pod的phase保持Running.
    • Never:Pod的phase变成Failed.

Pod正在运行,并且只有一个容器,容器内存溢出:

  • 容器失败并终止
  • 记录OOM事件
  • 如果restartPolicy是:
    • Always: 重启容器,Pod的phase保持Running.
    • OnFailure: 重启容器,Pod的phase保持Running.
    • Never: 记录失败事件,Pod的phase变成Failed.

Pod正在运行,磁盘损坏:

  • 杀死所有容器
  • 记录适当事件
  • Pod的phase 变成Failed
  • 如果Pod是用Controller创建的,Pod将在别处重建
  • Pod正在运行,Node被分段
    • Node Controller等待,直到超时
    • Node Controller将Pod的phase设为Failed
    • 如果Pod是用Controller创建的,Pod将在别处重建

pod安全策略

SecurityContext

通过设置Pod的SecurityContext,可以为每个Pod设置特定的安全策略。

SecurityContext有两种类型:

  • spec.securityContext: 这是一个PodSecurityContext对象。顾名思义,它对Pod中的所有contaienrs都有效。
  • spec.containers[*].securityContext: 这是一个SecurityContext对象。container私有的SecurityContext

一些提升权限的安全策略:

  • 特权容器:spec.containers[*].securityContext.privileged
  • 添加(Capabilities)可选的系统级能力: spec.containers[*].securityContext.capabilities.add只有 ntp 同步服务等少数容器,可以开启这项功能。请注意这非常危险。
  • Sysctls: 系统参数: spec.securityContext.sysctls

强烈建议在所有Pod上按需配置如下安全策略!

  1. spec.volumes: 所有的数据卷都可以设定读写权限
  2. spec.securityContext.runAsNonRoot: true Pod必须以非root用户运行
  3. spec.containers[*].securityContext.readOnlyRootFileSystem:true 将容器层设为只读,防止容器文件被篡改。
  4. 如果微服务需要读写文件,建议额外挂载emptydir类型的数据卷。
  5. spec.containers[*].securityContext.allowPrivilegeEscalation: false 不允许 Pod 做任何权限提升!
  6. spec.containers[*].securityContext.capabilities.drop: 移除(Capabilities)可选的系统级能力
apiVersion: v1
 kind: Pod
 metadata:
   name: <Pod name>
 spec:
   containers:
   - name: <container name>
     image: <image>
     imagePullPolicy: IfNotPresent
     # ......此处省略 500 字
     securityContext:
       readOnlyRootFilesystem: true  #将容器层设为只读,防止容器文件被篡改。
       allowPrivilegeEscalation: false  #禁止Pod做任何权限提升
       capabilities:
         drop:
         #禁止容器使用raw套接字,通常只有hacker才会用到raw套接字。
         #raw_socket可自定义网络层数据,避开tcp/udp协议栈,直接操作底层的ip/icmp数据包。可实现ip伪装、自定义协议等功能。
         #去掉net_raw会导致tcpdump无法使用,无法进行容器内抓包。需要抓包时可临时去除这项配置
         - NET_RAW
         #更好的选择:直接禁用所有capabilities
         #- ALL
   securityContext:
     #runAsUser: 1000  #设定用户
     #runAsGroup: 1000  #设定用户组
     runAsNonRoot: true  #Pod必须以非root用户运行
     seccompProfile:  #security compute mode
       type: RuntimeDefault           

Pod Security Admission

Pod Security Policy是一个赋予集群管理员控制Pod安全规范的内置准入控制器,可以让管理人员控制Pod实例安全的诸多方面,例如禁止采用root权限、防止容器逃逸等等。Pod Security Policy定义了一组Pod运行时必须遵循的条件及相关字段的默认值,Pod必须满足这些条件才能被成功创建,Pod Security Policy对象Spec包含以下字段也即是Pod Security Policy能够控制的方面

apiVersion: policy/v1beta1 
 kind: PodSecurityPolicy
 metadata:
   name: psp-example
 spec:
   privileged: false  #不允许特权pod,正常pod会允许,下面是一些必要的字段,都是允许
   seLinux:
     rule: RunAsAny
   supplementalGroups:
     rule: RunAsAny
   runAsUser:
     rule: RunAsAny
   fsGroup:
     rule: RunAsAny
   volumes:
   - '*'           

通过对PodSecurityPolicy使用,应该也会发现它的问题,例如没有dry-run和审计模式、不方便开启和关闭等,并且使用起来也不那么清晰。种种缺陷造成的结果是PodSecurityPolicy在Kubernetes V1.21被标记为弃用,并且将在V1.25中被移除,在Kubernets V1.22中则增加了新特性Pod Security Admission

Pod Security Admission机制在易用性和灵活性上都有了很大提升,从使用角度有以下四点显着不同:

  1. 可以在集群中默认开启,只要不添加约束条件就不会触发对pod的校验。
  2. 只在命名空间级别生效,可以为不同命名空间通过添加标签的方式设置不同的安全限制。
  3. 可以为特定的用户、命名空间或者运行时设置豁免规则。
  4. 根据实践预设了三种安全等级,不需要由用户单独去设置每一个安全条件。

Pod Security Admission将原来Pod Security Policy的安全条件划分成三种预设的安全等级:

  • privileged: 不受限,向pod提供所有可用的权限。
  • baseline:最低限度的限制策略,防止已知的特权升级。
  • restricted:严格限制策略,遵循当前Pod加固的最佳实践。

当pod与安全等级冲突时,我们可通过三种模式来选择不同的处理方式:

  • enforce:只允许符合安全等级要求的pod,拒绝与安全等级冲突的pod。
  • audit:只将安全等级冲突记录在集群event中,不会拒绝pod。
  • warn:与安全等级冲突时会向用户返回一个警告信息,但不会拒绝pod。
#应用安全策略不再需要创建单独的集群资源,只需要为命名空间设置控制标签
 apiVersion: v1
 kind: Namespace
 metadata:
   name: psa-test
   labels:
     pod-security.kubernetes.io/enforce: baseline
     pod-security.kubernetes.io/enforce-version: v1.25
     pod-security.kubernetes.io/audit: restricted
     pod-security.kubernetes.io/audit-version: v1.25
     pod-security.kubernetes.io/warn: restricted
     pod-security.kubernetes.io/warn-version: v1.25
 
 
 #命名空间的标签用来表示不同的模式所应用的安全策略级别,存在以下两种格式:
 pod-security.kubernetes.io/<MODE>: <LEVEL>
 #<MODE>:必须是enforce、audit或warn之一
 #<LEVEL>:必须是privileged、baseline或restricted之一
 pod-security.kubernetes.io/<MODE>-version: <VERSION>
 #该标签为可选,可以将安全性策略锁定到Kubernetes版本号。
 #<MODE>:必须是enforce、audit或warn之一
 #<VERSION>:Kubernetes版本号。例如 v1.25,也可以使用latest。           

排查pod的问题

Pod 有多种状态,这里罗列一下:

  • Error: Pod启动过程中发生错误
  • NodeLost: Pod所在节点失联
  • Unkown: Pod所在节点失联或其它未知异常
  • Waiting: Pod等待启动
  • Pending: Pod等待被调度
  • ContainerCreating: Pod容器正在被创建
  • Terminating: Pod正在被销毁
  • CrashLoopBackOff: 容器退出,kubelet正在将它重启
  • InvalidImageName: 无法解析镜像名称
  • ImageInspectError: 无法校验镜像
  • ErrImageNeverPull: 策略禁止拉取镜像
  • ImagePullBackOff: 正在重试拉取
  • RegistryUnavailable: 连接不到镜像中心
  • ErrImagePull: 通用的拉取镜像出错
  • CreateContainerConfigError: 不能创建kubelet使用的容器配置
  • CreateContainerError: 创建容器失败
  • RunContainerError: 启动容器失败
  • PreStartHookError: 执行preStart hook报错
  • PostStartHookError: 执行postStart hook报错
  • ContainersNotInitialized: 容器没有初始化完毕
  • ContainersNotReady: 容器没有准备完毕
  • ContainerCreating:容器创建中
  • PodInitializing:pod初始化中
  • DockerDaemonNotReady:docker还没有完全启动
  • NetworkPluginNotReady: 网络插件还没有完全启动

Pod一直处于Pending状态

  • Pending状态说明Pod还没有被调度到某个节点上,需要看下Pod事件进一步判断原因
  • 节点资源不够
  • 不满足nodeSelector与affinity
  • Node存在Pod没有容忍的污点

Pod一直处于ContainerCreating或Waiting状态

  • Pod配置错误
    • 检查是否打包了正确的镜像
    • 检查配置了正确的容器参数
  • 挂载Volume失败
  • 磁盘爆满
  • 节点内存碎片化
  • limit设置太小或者单位不对
  • 拉取镜像失败
  • CNI网络错误
  • controller-manager异常
  • 存在同名容器

Pod处于CrashLoopBackOff状态

  • 容器进程主动退出,退出状态码一般在0-128之间
  • 系统OOM
  • Cgroup OOM
  • 节点内存碎片化
  • 健康检查失败

Pod一直处于Terminating状态

  • 磁盘爆满:磁盘被写满,docker无法正常,运行kubelet调用docker删除容器没反应
  • 存在i文件属性:
  • #报错显示如下:

    rpc error: code = Unknown desc = failed to remove container "xxxxxxx": Error response from daemon: container xxxxxxx: driver "overlay2" failed to remove root filesystem: remove /data/docker/overlay2/xxxxxxx/diff/usr/bin/bash: operation not permitted

  • 存在Finalizers
  • k8s资源的metadata里如果存在finalizers,那么该资源一般是由某程序创建的,并且在其创建的资源的metadata里的finalizers加了一个它的标识,这意味着这个资源被删除时需要由创建资源的程序来做删除前的清理,清理完了它需要将标识从该资源的finalizers中移除,然后才会最终彻底删除资源.

    #kubectl edit手动编辑资源定义,删掉finalizers

Pod一直处于Unknown状态

  • 通常是节点失联,没有上报状态给apiserver,到达阀值后controller-manager认为节点失联并将其状态置为Unknown,可能原因:
    • 节点高负载导致无法上报
    • 节点宕机
    • 节点被关机
    • 网络不通

Pod一直处于Error状态

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

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

Pod一直处于ImagePullBackOff状态

  • 私有镜像仓库认证失败
  • 镜像文件损坏
  • 镜像拉取超时
  • 镜像不不存在

Pod一直处于ImageInspectError状态

  • 通常是镜像文件损坏了,可以尝试删除损坏的镜像重新拉取

Pod健康检查失败

  • 健康检查配置不合理
  • 节点负载过高
  • 容器进程被木马进程杀死
  • 容器内进程端口监听挂掉
  • SYN backlog设置过小
  • netstat -s | grep TCPBacklogDrop可以看到有多少是因为backlog满了导致丢弃的新连接。

    如果确认是backlog满了导致的丢包,建议调高backlog的值,内核参数为: net.ipv4.tcp_max_syn_backlog

容器进程主动退出

容器进程如果是自己主动退出(不是被外界中断杀死),退出状态码一般在0-128之间,根据约定,正常退出时状态码为0,1-127说明是程序发生异常,主动退出了,比如检测到启动的参数和条件不满足要求,或者运行过程中发生panic但没有捕获处理导致程序退出。除了可能是业务程序BUG,还有其它许多可能原因

  • DNS无法解析
  • 程序配置有误

使用临时容器排查问题

shareProcessNamespace: true #可以共享PID namespace,也就是说你在一个容器中能看到另外一个容器的进程列表
 
 #使用临时容器在线debug
 参考:https://kubernetes.io/zh-cn/docs/tasks/debug/debug-application/debug-running-pod/
 
 #有2个pod,nginx没有ps命令,busybox包含ps命令
 apiVersion: v1
 kind: Pod
 metadata:
   name: nginx
 spec:
   shareProcessNamespace: true
   containers:
   - name: nginx
     image: nginx
     securityContext:
       capabilities:
         add:
         - SYS_PTRACE
     stdin: true
     tty: true
 ---
 apiVersion: v1
 kind: Pod
 metadata:
   name: shell
 spec:
   shareProcessNamespace: true
   containers:
   - name: shell
     image: busybox:1.28
     securityContext:
       capabilities:
         add:
         - SYS_PTRACE
     stdin: true
     tty: true
 
 kubectl get pod 
 NAME    READY   STATUS    RESTARTS   AGE
 nginx   1/1     Running   0          95s
 shell   1/1     Running   0          70s
 
 #使用debug命令就可以添加一个容器,进行调试,此命令添加一个新的busybox容器并将其挂接到该容器
 kubectl debug -it nginx --image=busybox:1.28 --target=nginx
 ##--target参数指定另一个容器的进程命名空间
 
 
 kubectl debug -it nginx --image=ubuntu --share-processes --copy-to=nginx-debug
 ##--share-processes 允许在此Pod中的其他容器中查看该容器的进程
 
 #拷贝出来一个容器
 kubectl debug -it nginx --copy-to=myapp-debug --container=nginx -- sh           

继续阅读