k8s基本单位Pod

一、概述

Pod 是 Kubernetes 中最小的调度和管理单元,它代表着集群中运行的一个或多个容器实例。在一个 Pod 中,所有容器共享相同的网络命名空间、进程命名空间和存储卷,因此它们可以互相通信和共享数据。Pod 可以通过控制器进行创建、扩缩容和更新等操作。

二、Pod的基本操作

1、创建Pod

1.1、命令行方式启动

kubectl create deploy (pod控制器名称) [参数] 
kubectl create deploy nginx-deployment01 --image=nginx --port=8088 -n dev1
deployment.apps/nginx-deployment created

–-image 指定Pod的镜像

–port 指定端口

–n 指定namespace

1.2、创建Deployment资源清单启动

nginx-deployment02.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment02
  namespace: dev2
  labels:
    chapter: first-app
    function: test
spec:
  selector:
     matchLabels:
       app: nginx
  replicas: 1
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name : nginx02
        image: nginx
        ports:
        - containerPort: 8081
kubectl apply -f nginx-deployment02.yaml
deployment.apps/nginx-deployment02 created

1.3、直接创建Pod(资源清单)

apiVersion: v1
kind: Pod
metadata:
  name: nginx03
  namespace: dev3
  labels:
    function: test
    app: nginx
spec:
  containers:
  - image: nginx:latest
    name: pod
    ports:
    - name: nginx-port
      containerPort: 80
      protocol: TCP
kubectl apply -f pod-nginx03.yaml
pod/nginx03 created

1.4、直接创建(kubectl run)

kubectl run pod nginx04 --image=nginx -n dev4

2、查询

2.1、查询指定命名空间下所有的Pod

kubectl get pod -n <namespace>

2.2、查询指定Pod的信息

kubectl get pods <pod_name> -n <namespace>

2.3、对Pod状态进行实时监控

kubectl get pods -n <namespace> -w

2.4、格式化输出

kubectl get pods -n <namespace> -o json|yaml|name|go-template|go-template-file|template|templatefile|jsonpath|jsonpath-as-json|jsonpath-file|custom-columns-file|custom-columns|wide

2.5、显示详细信息(包括Events)

kubectl describe pods <pod_name> -n <namespace>

2.6、查看Pod的日志信息

kubectl logs <pod_name> -n <namespace>

2.7、显示Pod的对应标签

kubectl get pod <pod_name> -n <namespace> --show-labels

3、修改Pod

3.1、修改已存在的Pod属性(不是由控制器创建的)

kubectl replcae -f <createpods.yaml>

前提是Pod不是由更高级别的控制器(如Deployment)管理的。

  • 修改创建pod的资源清单

cp pod-nginx03.yaml replcae-pod-nginx03.yaml && vim replcae-pod-nginx03.yaml

apiVersion: v1
kind: Pod
metadata:
  name: nginx03
  namespace: dev3
  labels:
    function: replace
    app: nginx
spec:
  containers:
  - image: nginx:latest
    name: pod
    command: ["sh", "-c", "echo 'Hello Kubernetes replaced!'; sleep 3600"]
    ports:
    - name: nginx-port
      containerPort: 80
      protocol: TCP
  • 重建Pod
kubectl replace -f replcae-pod-nginx03.yaml --force

#Pod有很多属性是无法修改的,如果一定要修改,需要加上 --force 参数,相当于重建 Pod

  • 查看这个Pod的日志
kubectl logs nginx03 -n dev3

3.2、修改已存在的Pod属性(由控制器创建的)

  • 查看原本的Deployment资源清单
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment02
  namespace: dev2
  labels:
    chapter: first-app
    function: test
spec:
  selector:
     matchLabels:
       app: nginx
  replicas: 1
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name : nginx02
        image: nginx
        ports:
        - containerPort: 8081
  • 修改后的Deployment资源清单
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment02
  namespace: dev2
  labels:
    chapter: first-app
    function: test
spec:
  selector:
     matchLabels:
       app: nginx
  replicas: 1
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name : nginx02
        image: nginx
        ports:
        - containerPort: 8081
        command: ["sh", "-c", "echo 'Hello Kubernetes updated!'; sleep 3600"]
  • apply -f 更新Deployment
kubectl apply -f nginx-deployment02.yaml

3.3、edit在线编辑

kubectl edit nginx-deployment02-58759c87cb-dvg62 -n dev2

:wq保存退出后也可以达到更新的效果

4、删除Pod

4.1、未经Deployment创建的Pod

kubectl delete <pod_name> -n <namespace>

4.2、通过Deployment创建的Pod

kubectl delete -f <createpods.yaml>

二、Pod的生命周期

1、Pod的状态

我们可以通过 kubectl explain pod.status 命令来了解关于 Pod 状态的一些信息

Pod 的状态定义在 PodStatus 对象中,其中有一个 phase 字段,下面是 phase 的可能取值:

  • 挂起(Pending):Pod 信息已经提交给了集群,但是还没有被调度器调度到合适的节点或者 Pod 里的镜像正在下载

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

  • 成功(Succeeded):Pod 中的所有容器都被成功终止,并且不会再重启

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

  • 未知(Unknown):因为某些原因无法取得 Pod 的状态,通常是因为与 Pod 所在主机通信失败导致的

除此之外,PodStatus 对象中还包含一个 PodCondition 的数组,里面包含的属性有:

  • lastProbeTime:最后一次探测 Pod Condition 的时间戳。

  • lastTransitionTime:上次 Condition 从一种状态转换到另一种状态的时间。

  • message:上次 Condition 状态转换的详细描述。

  • reason:Condition 最后一次转换的原因。

  • status:Condition 状态类型,可以为 “True”, “False”, and “Unknown”.

  • type:Condition 类型,包括以下方面:

    • PodScheduled(Pod 已经被调度到其他 node 里)
    • Ready(Pod 能够提供服务请求,可以被添加到所有可匹配服务的负载平衡池中)
    • Initialized(所有的init containers已经启动成功)
    • Unschedulable(调度程序现在无法调度 Pod,例如由于缺乏资源或其他限制)
    • ContainersReady(Pod 里的所有容器都是 ready 状态)

2、重启策略

我们可以通过配置restartPolicy字段来设置 Pod 中所有容器的重启策略,其可能值为Always,OnFailure 和 Never,默认值为 Always。restartPolicy 仅指通过 kubelet 在同一节点上重新启动容器。通过 kubelet 重新启动的退出容器将以指数增加延迟(10s,20s,40s…)重新启动,上限为 5 分钟,并在成功执行 10 分钟后重置。不同类型的的控制器可以控制 Pod 的重启策略:

  • Job:适用于一次性任务如批量计算,任务结束后 Pod 会被此类控制器清除。Job 的重启策略只能是"OnFailure"或者"Never"。

  • Replication Controller, ReplicaSet, or Deployment,此类控制器希望 Pod 一直运行下去,它们的重启策略只能是"Always"。

  • DaemonSet:每个节点上启动一个 Pod,很明显此类控制器的重启策略也应该是"Always"。

3、初始化容器

了解了 Pod 状态后,首先来了解下 Pod 中最新启动的 Init Container,也就是我们平时常说的初始化容器Init Container就是用来做初始化工作的容器,可以是一个或者多个,如果有多个的话,这些容器会按定义的顺序依次执行。我们知道一个 Pod 里面的所有容器是共享数据卷和Network Namespace 的,所以Init Container里面产生的数据可以被主容器使用到。从上面的 Pod 生命周期的图中可以看出初始化容器是独立与主容器之外的,只有所有的初始化容器执行完之后,主容器才会被启动。那么初始化容器有哪些应用场景呢:

  • 等待其他模块 Ready:这个可以用来解决服务之间的依赖问题,比如我们有一个 Web 服务,该服务又依赖于另外一个数据库服务,但是在我们启动这个 Web 服务的时候我们并不能保证依赖的这个数据库服务就已经启动起来了,所以可能会出现一段时间内 Web 服务连接数据库异常。要解决这个问题的话我们就可以在 Web 服务的 Pod 中使用一个 InitContainer,在这个初始化容器中去检查数据库是否已经准备好了,准备好了过后初始化容器就结束退出,然后我们主容器的 Web 服务才被启动起来,这个时候去连接数据库就不会有问题了。

  • 做初始化配置:比如集群里检测所有已经存在的成员节点,为主容器准备好集群的配置信息,这样主容器起来后就能用这个配置信息加入集群。

  • 其它场景:如将 Pod 注册到一个中央数据库、配置中心等。

比如现在我们来实现一个功能,在 Nginx Pod 启动之前去重新初始化首页内容,如下所示的资源清单

init-pod.yaml

apiVersion: v1
kind: Pod
metadata:
  name: init-demo
spec:
  volumes:
  - name: workdir
    emptyDir: {}
  initContainers:
  - name: install
    image: busybox
    command:
    - wget
    - "-O"
    - "/work-dir/index.html"
    - http://www.baidu.com
    volumeMounts:
    - name: workdir
      mountPath: "/work-dir"
  containers:
  - name: nginx
    image: nginx
    ports:
    - containerPort: 80
    volumeMounts:
    - name: workdir
      mountPath: /usr/share/nginx/html

上面的资源清单中我们首先在 Pod 顶层声明了一个名为 workdir 的 Volume,前面我们用了 hostPath 的模式,这里我们使用的是 emptyDir{},这个是一个临时的目录,数据会保存在 kubelet 的工作目录下面,生命周期等同于 Pod 的生命周期。

然后我们定义了一个初始化容器,该容器会下载一个 html 文件到 /work-dir 目录下面,但是由于我们又将该目录声明挂载到了全局的 Volume,同样的主容器 nginx 也将目录 /usr/share/nginx/html 声明挂载到了全局的 Volume,所以在主容器的该目录下面会同步初始化容器中创建的 index.html 文件。

  • 直接应用资源清单来创建上面的Pod
kubectl apply -f init-pod.yaml

  • 创建完成后可以查看该 Pod 的状态
kubectl get pods -n default

可以发现 Pod 现在的状态处于 Init:0/1 状态,意思就是现在第一个初始化容器还在执行过程中

  • 此时我们可以查看 Pod 的详细信息
kubectl describe pods init-demo -n default
Name:         init-demo
Namespace:    default
Priority:     0
Node:         k8s-node2/192.168.112.60
Start Time:   Thu, 18 Jul 2024 23:53:56 +0800
Labels:       <none>
Annotations:  <none>
Status:       Pending
IP:           10.244.2.41
IPs:
  IP:  10.244.2.41
Init Containers:
  install:
    Container ID:  docker://d7ab75ce40d80daa676a547482ee32eba9f2488a9c791051fb5aa5ddcfbcc5b1
    Image:         busybox
    Image ID:      docker-pullable://busybox@sha256:5acba83a746c7608ed544dc1533b87c737a0b0fb730301639a0179f9344b1678
    Port:          <none>
    Host Port:     <none>
    Command:
      wget
      -O
      /work-dir/index.html
      http://www.baidu.com
    State:          Terminated
      Reason:       Completed
      Exit Code:    0
      Started:      Thu, 18 Jul 2024 23:56:21 +0800
      Finished:     Thu, 18 Jul 2024 23:56:24 +0800
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-2kmc4 (ro)
      /work-dir from workdir (rw)
Containers:
  nginx:
    Container ID:
    Image:          nginx
    Image ID:
    Port:           80/TCP
    Host Port:      0/TCP
    State:          Waiting
      Reason:       PodInitializing
    Ready:          False
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /usr/share/nginx/html from workdir (rw)
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-2kmc4 (ro)
Conditions:
  Type              Status
  Initialized       True
  Ready             False
  ContainersReady   False
  PodScheduled      True
Volumes:
  workdir:
    Type:       EmptyDir (a temporary directory that shares a pod's lifetime)
    Medium:
    SizeLimit:  <unset>
  kube-api-access-2kmc4:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  3607
    ConfigMapName:           kube-root-ca.crt
    ConfigMapOptional:       <nil>
    DownwardAPI:             true
QoS Class:                   BestEffort
Node-Selectors:              <none>
Tolerations:                 node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                             node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
  Type     Reason     Age                  From               Message
  ----     ------     ----                 ----               -------
  Normal   Scheduled  2m49s                default-scheduler  Successfully assigned default/init-demo to k8s-node2
  Warning  Failed     64s                  kubelet            Failed to pull image "busybox": rpc error: code = Unknown desc = Error response from daemon: Get "https://registry-1.docker.io/v2/": net/http: TLS handshake timeout
  Warning  Failed     64s                  kubelet            Error: ErrImagePull
  Normal   BackOff    63s                  kubelet            Back-off pulling image "busybox"
  Warning  Failed     63s                  kubelet            Error: ImagePullBackOff
  Normal   Pulling    51s (x2 over 2m49s)  kubelet            Pulling image "busybox"
  Normal   Pulled     25s                  kubelet            Successfully pulled image "busybox" in 26.135324609s
  Normal   Created    25s                  kubelet            Created container install
  Normal   Started    25s                  kubelet            Started container install
  Normal   Pulling    21s                  kubelet            Pulling image "nginx"

从上面的描述信息里面可以看到初始化容器已经启动了,现在处于 Running 状态,所以还需要稍等,到初始化容器执行完成后退出初始化容器会变成 Completed 状态,然后才会启动主容器。待到主容器也启动完成后,Pod 就会变成Running 状态

  • 然后我们去访问下 Pod 主页,验证下是否有我们初始化容器中下载的页面信息:
kuebctl get pods -o wide

 curl 10.244.2.41

4、pod hook

我们知道 Pod 是 Kubernetes 集群中的最小单元,而 Pod 是由容器组成的,所以在讨论 Pod 的生命周期的时候我们可以先来讨论下容器的生命周期。

实际上 Kubernetes 为我们的容器提供了生命周期的钩子,就是我们说的Pod Hook,Pod Hook 是由 kubelet 发起的,当容器中的进程启动前或者容器中的进程终止之前运行,这是包含在容器的生命周期之中。我们可以同时为 Pod 中的所有容器都配置 hook。

4.1、两种钩子函数

Kubernetes 为我们提供了两种钩子函数:

  • PostStart:这个钩子在容器创建后立即执行。但是,并不能保证钩子将在容器 ENTRYPOINT 之前运行,因为没有参数传递给处理程序。主要用于资源部署、环境准备等。不过需要注意的是如果钩子花费太长时间以至于不能运行或者挂起,容器将不能达到 running 状态。

  • PreStop:这个钩子在容器终止之前立即被调用。它是阻塞的,意味着它是同步的,所以它必须在删除容器的调用发出之前完成。主要用于优雅关闭应用程序、通知其他系统等。如果钩子在执行期间挂起,Pod 阶段将停留在 running 状态并且永不会达到 failed 状态。

如果 PostStart 或者 PreStop 钩子失败, 它会杀死容器。所以我们应该让钩子函数尽可能的轻量。当然有些情况下,长时间运行命令是合理的, 比如在停止容器之前预先保存状态。

4.2、两种实现方式

  • Exec - 用于执行一段特定的命令,不过要注意的是该命令消耗的资源会被计入容器。

  • HTTP - 对容器上的特定的端点执行 HTTP 请求。

4.3、PostStart钩子函数

以下示例中,定义了一个 Nginx Pod,其中设置了 PostStart 钩子函数,即在容器创建成功后,写入一句话到 /usr/share/message 文件中:

pod-poststart.yaml

apiVersion: v1
kind: Pod
metadata:
  name: hook-demo1
spec:
  containers:
  - name: hook-demo1
    image: nginx
    lifecycle:
      postStart:
        exec:
          command: ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"]
  • 应用资源清单
kubectl apply -f pod-poststart.yaml
 
kubectl get pods
 
NAME                            READY   STATUS    RESTARTS   AGE
hook-demo1                      1/1     Running   0          43s
  • 创建成功后查看容器中的 /usr/share/message 文件是否内容正确

4.4.、PreStop钩子函数

当用户请求删除含有 Pod 的资源对象时(如 Deployment 等),K8S 为了让应用程序优雅关闭(即让应用程序完成正在处理的请求后,再关闭软件),K8S 提供两种信息通知:

  • 默认:K8S 通知 node 执行docker stop命令,docker 会先向容器中 PID 为 1 的进程发送系统信号SIGTERM,然后等待容器中的应用程序终止执行,如果等待时间达到设定的超时时间,或者默认超时时间(30s),会继续发送SIGKILL的系统信号强行 kill 掉进程

  • 使用 Pod 生命周期(利用PreStop回调函数),它在发送终止信号之前执行默认所有的优雅退出时间都在30秒内。kubectl delete 命令支持 --grace-period=选项,这个选项允许用户用他们自己指定的值覆盖默认值。值'0'代表强制删除 pod。 在 kubectl 1.5 及以上的版本里,执行强制删除时必须同时指定 --force --grace-period=0。

强制删除一个 pod 是从集群状态还有 etcd 里立刻删除这个 pod,只是当 Pod 被强制删除时, APIServer 不会等待来自 Pod 所在节点上的 kubelet 的确认信息:pod 已经被终止。在 API 里 pod 会被立刻删除,在节点上, pods 被设置成立刻终止后,在强行杀掉前还会有一个很小的宽限期。

以下示例中,定义了一个 Nginx Pod,其中设置了PreStop钩子函数,即在容器退出之前,优雅的关闭 Nginx

pod-prestop.yaml

apiVersion: v1
kind: Pod
metadata:
  name: hook-demo2
spec:
  containers:
  - name: hook-demo2
    image: nginx
    lifecycle:
      preStop:
        exec:
          command: ["/usr/sbin/nginx","-s","quit"]  # 优雅退出
 
---
apiVersion: v1
kind: Pod
metadata:
  name: hook-demo3
spec:
  volumes:
  - name: message
    hostPath:
      path: /tmp
  containers:
  - name: hook-demo2
    image: nginx
    ports:
    - containerPort: 80
    volumeMounts:
    - name: message
      mountPath: /usr/share/
    lifecycle:
      preStop:
        exec:
          command: ['/bin/sh', '-c', 'echo Hello from the preStop Handler > /usr/share/message']

上面定义的两个 Pod,一个是利用 preStop 来进行优雅删除,另外一个是利用 preStop 来做一些信息记录的事情

  • 同样直接应用资源清单创建上面的 Pod:
kubectl apply -f pod-prestop.yaml

  • 查看对应Pod状态
kubectl get pods |grep ^hook

创建完成后,我们可以直接删除 hook-demo2 这个 Pod,在容器删除之前会执行 preStop 里面的优雅关闭命令,这个用法在后面我们的滚动更新的时候用来保证我们的应用零宕机非常有用。

第二个 Pod 我们声明了一个 hostPath 类型的 Volume,在容器里面声明挂载到了这个 Volume,所以当我们删除 Pod,退出容器之前,在容器里面输出的信息也会同样的保存到宿主机(一定要是 Pod 被调度到的目标节点)的 /tmp 目录下面

  • 查看 hook-demo3 这个 Pod 被调度的节点
kubectl describe pods hook-demo3

可以看到这个 Pod 被调度到了 k8s-node1 这个节点上,我们可以先到该节点上查看 /tmp 目录下面目前没有想要的内容:

ls /tmp/

  • 删除hook-demo3
kubectl delete pod hook-demo3 

现在我们来删除 hook-demo3 这个 Pod,安装我们的设定在容器退出之前会执行 preStop 里面的命令,也就是会往 message 文件中输出一些信息

  • 回到被调度到的节点/tmp/目录下查看
ls /tmp

cat /tmp/message   

另外 Hook 调用的日志没有暴露个给 Pod,所以只能通过 describe 命令来获取,如果有错误将可以看到 FailedPostStartHook 或 FailedPreStopHook 这样的 event。

三、Pod健康检查

在Kubernetes中,Pod健康检查是通过使用探针(probes)来实现的,这些探针用于确定Pod中的容器是否处于健康状态。健康检查机制允许Kubernetes系统在容器出现问题时做出响应,例如重启容器或从服务路由中移除容器。

1、Kubernetes中三种主要的探针类型

1.1、存活探针(Liveness Probes)

存活探针用于检查容器是否正在运行并响应。如果存活探针失败,Kubernetes将根据Pod的restartPolicy重启容器。这通常用于检测应用程序的崩溃或陷入死锁的情况。存活探针可以配置为以下几种类型:

  • HTTP GET:对容器的 IP 地址上指定端口和路径执行 HTTP GET 请求。如果响应的状态码大于等于 200 且小于 400,则诊断被认为是成功的。

  • TCP Socket:对容器的 IP 地址上的指定端口执行 TCP 检查。如果端口打开,则诊断被认为是成功的。 如果远程系统(容器)在打开连接后立即将其关闭,这算作是健康的。

  • Exec:在容器内执行指定命令。如果命令退出时返回码为 0 则认为诊断成功。

  • gRPC:使用 gRPC 执行一个远程过程调用。 目标应该实现 gRPC 健康检查。 如果响应的状态是 "SERVING",则认为诊断成功。

1.2、 就绪探针(Readiness Probes)

就绪探针用于确认容器是否准备好接收流量。如果就绪探针失败,Kubernetes将不会把该Pod视为服务的活动实例,这意味着不会将网络流量路由到该Pod。一旦就绪探针开始成功,Pod将开始接收流量。就绪探针可以是以下几种类型:

  • HTTP GET

  • TCP Socket

  • Exec

  • gRPC

1.3、启动探针(Startup Probes)

启动探针用于检测容器是否已启动并运行。在启动探针成功之前,其他探针(如存活探针和就绪探针)不会执行。这有助于避免在容器初始化阶段错误地重启或标记容器未准备好。启动探针同样可以配置为:

  • HTTP GET

  • TCP Socket

  • Exec

  • gRPC

2、探针配置参数

探针配置包含几个重要的参数:

  • initialDelaySeconds:探针开始执行前等待的秒数。

  • periodSeconds:探针执行之间的间隔秒数。

  • timeoutSeconds:探针执行超时秒数。

  • successThreshold:连续成功多少次后探针被认为是成功的。

  • failureThreshold:连续失败多少次后探针被认为是失败的。

3、探针状态

探针的结果可以是:

  • Success:容器通过了探针的检查。

  • Failure:容器没有通过探针的检查。

  • Unknown:探针执行失败或未知状态,此时不采取任何行动,kubelet会继续检查。

4、示例(存活探针--exec)

用 exec 执行命令的方式来检测容器的存活

4.1、编写资源清单

liveness-exec.yaml

apiVersion: v1
kind: Pod
metadata:
  name: liveness-exec
spec:
  containers:
  - name: liveness
    image: busybox
    args:
    - /bin/sh
    - -c
    - touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
    livenessProbe:
      exec:
        command:
        - cat
        - /tmp/healthy
      initialDelaySeconds: 5
      periodSeconds: 5
  • periodSeconds:表示让 kubelet 每隔5秒执行一次存活探针,也就是每5秒执行一次上面的cat /tmp/healthy命令,如果命令执行成功了,将返回0,那么 kubelet 就会认为当前这个容器是存活的,如果返回的是非0值,那么 kubelet 就会把该容器杀掉然后重启它。默认是10秒,最小1秒。
  • initialDelaySeconds:表示在第一次执行探针的时候要等待5秒,这样能够确保我们的容器能够有足够的时间启动起来。大家可以想象下,如果你的第一次执行探针等候的时间太短,是不是很有可能容器还没正常启动起来,所以存活探针很可能始终都是失败的,这样就会无休止的重启下去了

4.2、.spec.containers.args

我们在容器启动的时候,执行了如下命令:

/bin/sh -c "touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600"

意思是说在容器最开始的30秒内创建了一个/tmp/healthy文件,在这30秒内执行cat /tmp/healthy命令都会返回一个成功的返回码。30 秒后,我们删除这个文件,现在执行cat /tmp/healthy是不是就会失败了(默认检测失败3次才认为失败),所以这个时候就会重启容器了。

4.3、创建该pod

kubectl apply -f liveness-exec.yaml

4.4、查看Pod的Event

kubectl describe pod liveness-exec

我们可以观察到容器是正常启动的,在隔一会儿,再查看下 Pod 的 Event,在最下面有一条信息显示 liveness probe 失败了,容器将要重启。然后可以查看到 Pod 的 RESTARTS 值加 1 了:

kubectl get pods|grep ^live

5、示例(存活探针--HTTP GET)

5.1、编写资源清单

apiVersion: v1
kind: Pod
metadata:
  name: liveness-http
spec:
  containers:
  - name: liveness
    image: cnych/liveness
    args:
    - /server
    livenessProbe:
      httpGet:
        path: /healthz
        port: 8080
        httpHeaders:
        - name: X-Custom-Header
          value: Awesome
      initialDelaySeconds: 3
      periodSeconds: 3

同样的,根据 periodSeconds 属性我们可以知道 kubelet 需要每隔3秒执行一次 liveness Probe,该探针将向容器中的 server 的 8080 端口发送一个 HTTP GET 请求。如果 server 的 /healthz 路径的 handler 返回一个成功的返回码,kubelet 就会认定该容器是活着的并且很健康,如果返回失败的返回码,kubelet 将杀掉该容器并重启它。initialDelaySeconds 指定kubelet 在该执行第一次探测之前需要等待3秒钟。

通常来说,任何大于200小于400的状态码都会认定是成功的返回码。其他返回码都会被认为是失败的返回码。

四、Pod资源配置

实际上上面几个步骤就是影响一个 Pod 生命周期的大的部分,但是还有一些细节也会在 Pod 的启动过程进行设置,比如在容器启动之前还会为当前的容器设置分配的 CPU、内存等资源,我们知道我们可以通过 CGroup 来对容器的资源进行限制,同样的,在 Pod 中我们也可以直接配置某个容器的使用的 CPU 或者内存的上限。那么 Pod 是如何来使用和控制这些资源的分配的呢?

首先对于 CPU,我们知道计算机里 CPU 的资源是按“时间片”的方式来进行分配的,系统里的每一个操作都需要 CPU 的处理,所以,哪个任务要是申请的 CPU 时间片越多,那么它得到的 CPU 资源就越多。

1、CGroup 里面对于 CPU 资源的单位换算

1 CPU =  1000 millicpu(1 Core = 1000m)
 
0.5 CPU = 500 millicpu (0.5 Core = 500m)

这里的 m 就是毫、毫核的意思,Kubernetes 集群中的每一个节点可以通过操作系统的命令来确认本节点的 CPU 内核数量,然后将这个数量乘以1000,得到的就是节点总 CPU 总毫数。比如一个节点有四核,那么该节点的 CPU 总毫量为 4000m,如果你要使用0.5 core,则你要求的是 4000*0.5 = 2000m。在 Pod 里面我们可以通过下面的两个参数来现在和请求 CPU 资源:

  • spec.containers[].resources.limits.cpu:CPU 上限值,可以短暂超过,容器也不会被停止

  • spec.containers[].resources.requests.cpu:CPU请求值,Kubernetes 调度算法里的依据值,可以超过

这里需要明白的是,如果resources.requests.cpu设置的值大于集群里每个节点的最大 CPU 核心数,那么这个 Pod 将无法调度,因为没有节点能满足它。

到这里应该明白了,requests 是用于集群调度使用的资源,而 limits 才是真正的用于资源限制的配置,如果你需要保证的你应用优先级很高,也就是资源吃紧的情况下最后再杀掉你的 Pod,那么你就把你的 requests 和 limits 的值设置成一致。

2、创建容器的资源清单

2.1、编写资源清单

pod-resource-demo1.yaml

apiVersion: v1
kind: Pod
metadata:
  name: resource-demo1
spec:
  containers:
  - name: resource-demo1
    image: nginx
    ports:
    - containerPort: 80
    resources:
      requests:
        memory: 50Mi
        cpu: 50m
      limits:
        memory: 100Mi
        cpu: 100m

这里,CPU 我们给的是 50m,也就是 0.05core,这 0.05 core 也就是占了 1 CPU 里的 5% 的资源时间。而限制资源是给的是 100m,但是需要注意的是 CPU 资源是可压缩资源,也就是容器达到了这个设定的上限后,容器性能会下降,但是不会终止或退出。

2.2、创建该Pod

kubectl apply -f pod-resource-demo1.yaml

2.3、查看被调度到的节点

kubectl get pods resource-demo1 -o wide

可以看到Pod被调度到k8s-node2这个节点上了

2.4、查看Pod里的容器

docker ps |grep resource-demo1

其中第一个容器就是我们的主容器,第二容器是 Infra 容器

2.5、查看主容器的信息

docker inspect 78638fd35d88   

实际上我们就可以看到这个容器的一些资源情况,Pod 上的资源配置最终也还是通过底层的容器运行时去控制 CGroup 来实现的,我们可以进入对应目录查看 CGroup 的配置,该目录就是 CGroup 父级目录,而 CGroup 是通过文件系统来进行资源限制的,所以我们上面限制容器的资源就可以在该目录下面反映出来:

截图里CgroupParent对应的目录名

cd /sys/fs/cgroup/cpu/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-poda77b9886_5413_4368_8ee0_5e4c153c8d01.slice

cat cpu.cfs_quota_us 

其中 cpu.cfs_quota_us 就是 CPU 的限制值,如果要查看具体的容器的资源,我们也可以进入到容器目录下面去查看即可。

内存是可压缩性资源,如果容器使用内存资源到达了上限,那么会OOM,造成内存溢出,容器就会终止和退出。

热门相关:性是谎言2   战斗就变强   我能看到隐藏机缘   风流医圣   大金主,你别假正经了