信息介绍

Pause容器

每个Pod都有一个特殊的被称为“根容器”的Pause容器。除了Pause容器,每个Pod还包含一个或多个紧密相关的用户业务容器。
image.png

###为什么Kubernetes会设计出一个全新的Pod的概念并且Pod有这样特殊的组成结构?
原因一:
在一组容器作为一个单元的情况下,难以简单地对“整体”进行状态判断。比如,一个容器死亡了,此时算是整体死亡么?是N/M的死亡率么?引入业务无关并且不易死亡的Pause容器作为Pod的根容器,以它的状态代表整个容器组的状态,就巧妙地解决了这个难题。

原因二:
Pod里的多个业务容器共享Pause容器的IP,共享Pause容器挂接的Volume,既简化了业务容器之间的通信问题,也解决了文件共享问题。

Pod创建流程

Pod 在 Kubernetes 集群中被创建的基本流程如下所示:
image.png

  • 用户通过 REST API 创建一个 Pod
  • apiserver 将其写入 etcd
  • scheduluer 检测到未绑定 Node 的 Pod,开始调度并更新 Pod 的 Node 绑定
  • kubelet 检测到有新的 Pod 调度过来,通过 container runtime 运行该 Pod
  • kubelet 通过 container runtime 取到 Pod 状态,并更新到 apiserver 中

Pod状态

命令 kubectl explain pod.status 可查看 Pod 状态字段,如下:

状态值 说明
Pending API Server 已创建该Pod,但在Pod内还有一个或多个容器的镜像没有创建,包括正在下载镜像的过程
Running Pod 内所有容器均已创建,且至少有一个容器处于运行状态、正在启动状态或正在重启状态
Succeeded Pod 内所有容器均成功执行后退出,且不会再重启
Failed Pod 内所有容器均已退出,但至少有一个容器退出为失败状态
Unknown 由于某种原因无法获取该Pod的状态,可能由于网络通信不畅导致

Pod重启策略

Pod的重启策略包括Always、OnFailure和Never,默认值为Always。
◎ Always:当容器失效时,由kubelet自动重启该容器。
◎ OnFailure:当容器终止运行且退出码不为0时,由kubelet自动重启该容器。
◎ Never:不论容器运行状态如何,kubelet都不会重启该容器。

不同类型的的控制器拥有不同的 Pod 重启策略:
◎ DaemonSet:必须设置为Always,需要保证该容器持续运行。
◎ Job:适用于一次性任务,如批量计算。重启策略只能是OnFailure或Never,确保容器执行完成后不再重启。

生命周期

Pod 的完整生命周期过程包含 Init Container、Pod Hook、Health Check 三个主要部分。
image.png

Init Container

介绍

特性init container用于在启动应用容器(app container)之前启动一个或多个初始化容器,完成应用容器所需的预置条件。init container与应用容器在本质上是一样的,但它们是仅运行一次就结束的任务,并且必须在成功执行完成后,系统才能继续执行下一个容器。

init container 应用场景:

  • 检查应用容器的关联组件正确运行(例如数据库或某个所依赖的服务)
  • 基于环境变量或配置模板生成配置文件
  • 从远程数据库获取本地所需配置,或者将自身注册到某个中央数据库中
  • 下载相关依赖包,或者对系统进行一些预配置操作

init container与应用容器的区别:

  • init container的运行方式与应用容器不同,它们必须先于应用容器执行完成,当设置了多个init container时,将按顺序逐个运行,并且只有前一个init container运行成功后才能运行后一个init container。当所有init container都成功运行后,Kubernetes才会初始化Pod的各种信息,并开始创建和运行应用容器。

示例

在 Nginx Pod 启动之前初始化首页内容(init-pod.yaml)

  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4. name: init-demo
  5. spec:
  6. volumes:
  7. - name: workdir
  8. emptyDir: {}
  9. initContainers:
  10. - name: install
  11. image: busybox
  12. command:
  13. - wget
  14. - "-O"
  15. - "/work-dir/index.html"
  16. - http://www.baidu.com
  17. volumeMounts:
  18. - name: workdir
  19. mountPath: "/work-dir"
  20. containers:
  21. - name: nginx·
  22. image: nginx
  23. ports:
  24. - containerPort: 80
  25. volumeMounts:
  26. - name: workdir
  27. mountPath: /usr/share/nginx/html

资源应用&状态查看

$ kubectl apply -f init-pod.yaml
$ kubectl get pods
NAME                            READY   STATUS     RESTARTS   AGE
init-demo                       0/1     Init:0/1   0          4s

查看Pod详细信息

$ kubectl describe pod init-demo
Name:         init-demo
Namespace:    default
Priority:     0
Node:         k8s-node3/10.151.30.57
Start Time:   Mon, 11 Nov 2019 20:10:54 +0800
Labels:       <none>
Annotations:  kubectl.kubernetes.io/last-applied-configuration:
                {"apiVersion":"v1","kind":"Pod","metadata":{"annotations":{},"name":"init-demo","namespace":"default"},"spec":{"containers":[{"image":"ngi...
Status:       Pending
IP:           10.244.3.54
IPs:
  IP:  10.244.3.54
Init Containers:
  install:
    Container ID:  docker://762f64641abb913804bc8a3b71b553744afeb1169547ef35d9f6ef0e09a458e0
    Image:         busybox
    Image ID:      docker-pullable://busybox@sha256:1303dbf110c57f3edf68d9f5a16c082ec06c4cf7604831669faf2c712260b5a0
    Port:          <none>
    Host Port:     <none>
    Command:
      wget
      -O
      /work-dir/index.html
      http://www.baidu.com
    State:          Running
      Started:      Mon, 11 Nov 2019 20:10:59 +0800
    Ready:          False
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-5tsh4 (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 default-token-5tsh4 (ro)
Conditions:
  Type              Status
  Initialized       False
  Ready             False
  ContainersReady   False
  PodScheduled      True
Volumes:
  workdir:
    Type:       EmptyDir (a temporary directory that shares a pod's lifetime)
    Medium:
    SizeLimit:  <unset>
  default-token-5tsh4:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-5tsh4
    Optional:    false
QoS Class:       BestEffort
Node-Selectors:  <none>
Tolerations:     node.kubernetes.io/not-ready:NoExecute for 300s
                 node.kubernetes.io/unreachable:NoExecute for 300s
Events:
  Type    Reason     Age        From                 Message
  ----    ------     ----       ----                 -------
  Normal  Scheduled  <unknown>  default-scheduler    Successfully assigned default/init-demo to k8s-node3
  Normal  Pulling    20s        kubelet, k8s-node3  Pulling image "busybox"
  Normal  Pulled     17s        kubelet, k8s-node3  Successfully pulled image "busybox"
  Normal  Created    17s        kubelet, k8s-node3  Created container install
  Normal  Started    16s        kubelet, k8s-node3  Started container install

稍等片刻后访问,显示百度页面信息

$ kubectl get pods -o wide
NAME                            READY   STATUS    RESTARTS   AGE     IP             NODE         NOMINATED NODE   READINESS GATES
init-demo                       1/1     Running   0          3m39s   10.244.3.54    ydzs-node3   <none>           <none>

$ curl 10.244.3.54
<!DOCTYPE html>
<!--STATUS OK--><html> ...>百度一下,你就知道</title><...> </body> </html>

Pod Hook

介绍

Kubernetes 为容器提供了生命周期的钩子,即Pod Hook,其是由 kubelet 发起的,当容器中的进程启动前或者容器中的进程终止之前运行,这是包含在容器的生命周期之中。

Kubernetes 中的两种钩子函数:

  • PostStart:钩子在容器创建后立即执行。主要用于资源部署、环境准备等。
  • PreStop:钩子在容器终止之前立即被调用。必须在删除容器的调用发出之前完成。主要用于优雅关闭应用程序、通知其他系统等。

如果 PostStart 或者 PreStop 钩子失败, 它会杀死容器。所以应该让钩子函数尽可能的轻量。

有两种方式来实现钩子函数:

  • Exec - 用于执行一段特定的命令,该命令消耗的资源会被计入容器。
  • HTTP - 对容器上的特定的端点执行 HTTP 请求。

示例

#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"]


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

  • 默认:K8S 通知 node 执行 stop 命令,先向容器中 PID 为 1 的进程发送系统信号SIGTERM,然后等待容器中的应用程序终止执行,如果等待时间达到设定的超时时间,或者默认超时时间(30s),会继续发送SIGKILL的系统信号强行 kill 掉进程
  • 使用 Pod 生命周期(利用PreStop回调函数),它在发送终止信号之前执行

默认所有的优雅退出时间都在30秒内。kubectl delete 命令支持 —grace-period=选项,此选项允许用户指定的值覆盖默认值。执行强制删除时须同时指定 —force —grace-period=0

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

在容器退出之前,优雅关闭 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']

应用

$ kubectl apply -f pod-prestop.yaml

$ kubectl get pods
NAME                            READY   STATUS    RESTARTS   AGE
hook-demo2                      1/1     Running   0          67s
hook-demo3                      1/1     Running   0          67s

查看Pod被调度到的节点

$ kubectl describe pod hook-demo3
Name:         hook-demo3
Namespace:    default
Priority:     0
Node:         k8s-node1/10.151.30.22
Start Time:   Tue, 12 Nov 2021 14:33:54 +0800
......

在 k8s-node1 节点上操作

$ ls /tmp/

$ kubectl delete pod hook-demo3
pod "hook-demo3" deleted

$ ls /tmp/
message

$ cat /tmp/message
Hello from the preStop Handler

Health Check

介绍

Kubernetes 对 Pod 的健康状态通过两类探针来检查:LivenessProbe 和 ReadinessProbe,kubelet 定期执行这两类探针来诊断容器的健康状况。

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实例上。

探针配置方式

LivenessProbe和ReadinessProbe均可配置三种实现方式。

#Exec Action
在容器内部执行一个命令,如果该命令的返回码为0,则表明容器健康。

示例: exec 方式检测容器的存活(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秒,这样能够确保容器能够有足够的时间启动起来。

说明:
清单内容显示在容器最开始的30秒内创建了一个/tmp/healthy文件,在这30秒内执行cat /tmp/healthy命令都会返回一个成功的返回码。30 秒后,删除这个文件,再执行cat /tmp/healthy就会失败(默认检测失败3次才认为失败),所以此刻会重启容器。

$ kubectl get pods
NAME            READY   STATUS    RESTARTS   AGE
liveness-exec   1/1     Running   2          1m

#HTTPGet Action
通过容器的IP地址、端口号及路径调用HTTP Get方法,若响应的状态码大于等于200且小于400,则认为容器健康。

示例:liveness-http.yaml

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

探针将向容器中的 server 的 8080 端口发送一个 HTTP GET 请求。如果 server 的 /healthz 路径的 handler 返回一个成功的返回码,kubelet 就会认定该容器健康的,如果返回失败的返回码,kubelet 将杀掉该容器并重启它。

#healthz的实现

http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
    duration := time.Now().Sub(started)
    if duration.Seconds() > 10 {
        w.WriteHeader(500)
        w.Write([]byte(fmt.Sprintf("error: %v", duration.Seconds())))
    } else {
        w.WriteHeader(200)
        w.Write([]byte("ok"))
    }
})

描述:最开始前 10s 返回状态码200,10s 过后就返回状态码500。所以当容器启动3秒后,kubelet 开始执行健康检查。第一次健康检查会成功,因为是在 10s 之内,但是 10 秒后,健康检查将失败,kubelet 将会杀掉和重启容器。

#TCPSocket Action
通过容器的IP地址和端口号执行TCP检查,如果能够建立TCP连接,则表明容器健康。

示例:liveness-tcp.yaml

apiVersion: v1
kind: Pod
metadata:
  name: liveness-tcp
spec:
  containers:
  - name: liveness
    image: cnych/liveness
    args:
    - /server
    livenessProbe:
      tcpSocket:
        port: 8080
      initialDelaySeconds: 3
      periodSeconds: 3

探测参数设置

每种探测方式,都要设置 initialDelaySeconds 和 timeoutSeconds 两个参数:
◎ initialDelaySeconds:启动容器后进行首次健康检查的等待时间,单位为s。
◎ timeoutSeconds:健康检查发送请求后等待响应的超时时间,单位为s。当超时发生时,kubelet会认为容器已经无法提供服务,将会重启该容器。

额外参数:
◎ timeoutSeconds:探测超时时间,默认1秒,最小1秒。
◎ successThreshold:探测失败后,最少连续探测成功多少次才被认定为成功。默认是 1。
◎ failureThreshold:探测成功后,最少连续探测失败多少次才被认定为失败。默认是 3,最小值是 1。

静态 Pod

介绍

静态 Pod 直接由节点上的 kubelet 进程来管理,不通过 master 节点上的 apiserver。无法与常用控制器 Deployment 或者 DaemonSet 进行关联,它由 kubelet 进程自己来监控,当 pod 崩溃时会重启该 pod,kubelet 也无法对它们进行健康检查。

静态 pod 始终绑定在某一个 kubelet 上,并且始终运行在同一个节点上。kubelet 会自动为每一个静态 pod 在 Kubernetes 的 apiserver 上创建一个镜像 Pod,所以可以在 apiserver 中查询到该 pod,但是不能通过 apiserver 进行控制(例如不能删除)。

创建

配置文件就是放在特定目录下的标准的 JSON 或 YAML 格式的 pod 定义文件。用 kubelet —pod-manifest-path= 启动 kubelet 进程,kubelet 定期的去扫描指定目录,根据这个目录下出现或消失的 YAML/JSON 文件来创建或删除静态 pod。

示例:node01 节点上用静态 pod 的方式启动 nginx 服务
#命令查看kubelet对应的启动配置文件
$ systemctl status kubelet
##查看到配置文件路径为 /var/lib/kubelet/config.yaml

$ cat /var/lib/kubelet/config.yaml|grep -i pod
staticPodPath: /etc/kubernetes/manifests

通过kubeadm方式安装的集群环境,kubelet 已经配置了静态 Pod 文件的路径,默认地址为 /etc/kubernetes/manifests,所以只需要在该目录下面创建一个标准的 Pod 的 JSON 或者 YAML 文件即可。

[root@ node01 ~] $ cat <<EOF >/etc/kubernetes/manifests/static-web.yaml
apiVersion: v1
kind: Pod
metadata:
  name: static-web
  labels:
    app: static
spec:
  containers:
    - name: web
      image: nginx
      ports:
        - name: web
          containerPort: 80
EOF

当上面命令执行完后集群中会创建出名称为 static-web 的 Pod,当把文件 static-web.yaml 移除后,自动删除。

用 kubeadm 安装的集群,master 节点上几个重要组件都是用静态 Pod 的方式运行的,登录到 master 节点查看/etc/kubernetes/manifests目录:
$ ls /etc/kubernetes/manifests/
etcd.yaml kube-apiserver.yaml kube-controller-manager.yaml kube-scheduler.yaml

这种方式将集群的一些组件容器化提供了可能,因为这些 Pod 都不会受到 apiserver 的控制,不然kube-apiserver怎么自己去控制自己呢?万一不小心把这个 Pod 删掉了呢?所以只能由kubelet来进行控制,这就是静态 Pod。

PodPreset

介绍

PodPreset(Pod 预设值)功能,能够为 Pod 自动填充一些字段。比如按照命名空间来划分不同的环境,然后在不同的环境上部署 Pod 后自动为加上环境相关的 Labels、Annotations 等等信息。提高了编写 YAML 的效率。

Kubernetes 提供了一个 PodPreset 准入控制器,当启用后,PodPreset 会将应用创建请求传入到该控制器上。当有 Pod 创建请求发生时,系统将执行以下操作:

  • 检索所有可用的 PodPreset
  • 检查有 PodPreset 的标签选择器上的标签与正在创建的 Pod 上的标签是否匹配
  • 尝试将由 PodPreset 定义的各种资源合并到正在创建的 Pod 中
  • 出现错误时,在该 Pod 上引发记录合并错误的事件,PodPreset 不会注入任何资源到创建的 Pod 中
  • 注释刚生成的修改过的 Pod spec,以表明它已被 PodPreset 修改过。注释的格式为 podpreset.admission.kubernetes.io/podpreset-“: “

每个 Pod 可以匹配零个或多个 PodPrestet,每个 PodPreset 可以应用于零个或多个 Pod。 PodPreset 应用于一个或多个 Pod 时,Kubernetes 会修改 Pod Spec。对于 Env、EnvFrom 和 VolumeMounts 的更改,Kubernetes 修改 Pod 中所有容器的 spec;对于 Volume 的更改,Kubernetes 修改 Pod Spec。

若期望 Pod 不会被任何 Pod Preset 所改变。可以在 Pod 的 Pod Spec 中添加内容:
annotation:podpreset.admission.kubernetes.io/exclude:”true”

应用

#启用 PodPreset
要启用 PodPreset ,需要在准入控制中加入 PodPreset,为了定义 PodPreset 对象,还需要在 APIServer 启动参数中添加如下配置:
- —enable-admission-plugins=NodeRestriction,PodPreset
- —runtime-config=settings.k8s.io/v1alpha1=true

若使用的 kubeadm 搭建的集群,故只需要修改静态 Pod 配置即可,路径 /etc/kubernetes/manifests/kube-apiserver.yaml,修改完成后,将 kube-apiserver.yaml 文件移除 manifests 目录,然后移动回来,相当于强制重启,再执行如下命令:
$ kubectl api-versions |grep settings
settings.k8s.io/v1alpha1

$ kubectl get podpreset
No resources found in default namespace.

出现如上的提示证明已经开启成功。

#示例
需求:同步 Pod 和宿主机的时间。

方式一:通过挂载宿主机的 localtime 实现
###常规做法是通过挂载宿主机的 localtime 来实现,如(time-demo.yaml)

apiVersion: apps/v1
kind: Pod
metadata:
  name: time-demo
  labels:
    app: time
spec:
  containers:
  - name: time-demo
    image: nginx
    ports:
    - containerPort: 80

创建

$ kubectl apply -f time-demo.yaml

$ kubectl get pods
NAME         READY   STATUS    RESTARTS   AGE
time-demo    1/1     Running   0          2m8s

#对比时间,可看到pod和节点时间不同步
$ date
Thu Nov 14 12:08:27 CST 2021
$ kubectl exec time-demo date
Thu Nov 14 04:08:41 UTC 2021

挂载文件实现时间同步

apiVersion: apps/v1
kind: Pod
metadata:
  name: time-demo
  labels:
    app: time
spec:
  volumes:
  - name: host-time
    hostPath:
      path: /etc/localtime
  containers:
  - name: time-demo
    image: nginx
    volumeMounts:
    - name: host-time
      mountPath: /etc/localtime
    ports:
    - containerPort: 80

验证

$ kubectl delete -f time-demo.yaml

$ kubectl apply -f time-demo.yaml

$ kubectl get pods
NAME         READY   STATUS    RESTARTS   AGE
time-demo    1/1     Running   0          2m8s

# 时间已同步
$ date
Thu Nov 14 12:13:40 CST 2021
$ kubectl exec time-demo date
Thu Nov 14 12:13:45 CST 2021

方式二:用 PodPreset 预设模板实现
###所有 Pod 都有时间同步的需求,都手动挂载显得繁琐,此时可利用 PodPreset 来预设模板(time-preset.yaml)

apiVersion: settings.k8s.io/v1alpha1
kind: PodPreset
metadata:
  name: time-preset
  namespace: default
spec:
  selector:
    matchLabels:
  volumeMounts:
  - name: localtime
    mountPath: /etc/localtime
  volumes:
  - name: localtime
    hostPath:
      path: /etc/localtime

Pod (time-demo.yaml)

apiVersion: apps/v1
kind: Pod
metadata:
  name: time-demo
  labels:
    app: time
spec:
  containers:
  - name: time-demo
    image: nginx
    ports:
    - containerPort: 80

应用

$ kubectl delete -f time-demo.yaml

$ kubectl apply -f time-demo.yaml

$  kubectl get pods -l app=time
NAME        READY   STATUS    RESTARTS   AGE
time-demo   1/1     Running   0          60s

# 验证时间是否同步,可看到已实现
$ date
Thu Nov 14 14:55:02 CST 2021
$ kubectl exec time-demo date
Thu Nov 14 14:55:21 CST 2021

查看Pod资源清单

$ kubectl get pod time-demo -o yaml
apiVersion: v1
kind: Pod
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"v1","kind":"Pod","metadata":{"annotations":{},"labels":{"app":"time"},"name":"time-demo","namespace":"default"},"spec":{"containers":[{"image":"nginx","name":"time-demo","ports":[{"containerPort":80}]}]}}
    podpreset.admission.kubernetes.io/podpreset-time-preset: "1469811"
  creationTimestamp: "2019-11-14T06:54:06Z"
  labels:
    app: time
  name: time-demo
  namespace: default
  resourceVersion: "1470398"
  selfLink: /api/v1/namespaces/default/pods/time-demo
  uid: 37a7ab94-c2c3-4b10-88df-0c486fb8d422
spec:
  containers:
  - image: nginx
    imagePullPolicy: Always
    name: time-demo
    ports:
    - containerPort: 80
      protocol: TCP
    resources: {}
    terminationMessagePath: /dev/termination-log
    terminationMessagePolicy: File
    volumeMounts:
    - mountPath: /etc/localtime
      name: localtime
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      name: default-token-5tsh4
      readOnly: true
...
...

说明:
1、信息显示 Pod 已被自动注入 PodPreset 声明的模板
2、该命名空间下所有 Pod 默认都会被注入该模板,意味着不同的命名空间需要创建不同的 PodPreset