Kubernetes 资源编排系列之一: Pod YAML 篇

优采云 发布时间: 2022-07-29 22:44

  Kubernetes 资源编排系列之一: Pod YAML 篇

  作 者 |周虚(应金挺)

  来 源 |阿里智能运维团队

  SREWorks的开源吸引了大量用户来尝试部署和使用我们的产品,其中不乏一些初次接触Kubernetes的朋友。随着SREWorks云原生运维平台使用的持续深入,部分用户对于其中的原理和概念还存在一些困惑。因此,我们特推出《Kubernetes资源编排系列》,从底层的Pod YAML开始,逐步递进地讲解相关内容,希望能够解答大家对于Kubernetes的一些疑问,让用户对于云原生相关技术有更深入的了解。

  01Pod 整体结构

  Pod YAML的整体结构,可以初步分为Resource(资源)、Object(元数据)、Spec(规范)、Status(状态)。本文将会围绕这四部分一一展开。

  02Resource(资源)- Rest API

  k8s资源按照Scope可以分为Namespace资源、Cluster资源,Namespace在k8s可以认为是软租户的效果,实现资源层面的隔离,Pod资源就是属于Namespace资源,而Namespace不光体现在YAML参数中,也表现在k8s Rest API中。

  Rest API的整体结构,以Pod举例

  <p style="white-space: pre-wrap;margin: 0px 8px;font-size: 14px;font-weight: normal;word-spacing: 0px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);padding: 0.5em;line-height: 1.75em;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;">apiVersion: v1<br />kind: Pod<br />metadata:<br />  name: test-pod<br />  namespace: default<br /></p>

  基于上述YAML,可以明确出namespace为default, name为test-pod的Pod资源对象,也就是明确出Pod为Namespace资源,该Pod资源对象对应的apiVersion为v1,后续k8s自内联相关的Group为/api,自然而然,我们就将该对象的数据分离出来了:

  基于上述的数据展示,apiserver自然而然会相应的注册出下列rest api:

  后续基于扩展,我们就需要明确出method,这样一个真正完整的Rest API就诞生了。

  03Object(元数据)

  在rest api中明确了Resource的kind、apiVersion, 也确定了Object的namespace、name,作为凡是k8s资源对象都会引用的公共结构,自然也存在很多公共机制供使用。

  <p style="white-space: pre-wrap;margin: 0px 8px;font-size: 14px;font-weight: normal;word-spacing: 0px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);padding: 0.5em;line-height: 1.75em;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;">metadata:<br />  annotations:<br />    alibabacloud.com/owner: testdemo<br />    k8s.aliyun.com/pod-eni: "true"<br />  creationTimestamp: "2022-06-02T07:21:36Z"<br />  deleteTimestamp: "2022-06-02T07:22:51Z"<br />  labels:<br />    app: taihao-app-cn-shanghai-pre-cloud-resource<br />    pod-template-hash: 5bbb759f78<br />  name: testdemo-5bbb759f78-27v88<br />  namespace: default<br />  ownerReferences:<br />  - apiVersion: apps/v1<br />    blockOwnerDeletion: true<br />    controller: true<br />    kind: ReplicaSet<br />    name: testdemo-5bbb759f78<br />    uid: 9c3f268a-c0d1-4038-bb2b-b92928f45e3d<br />  resourceVersion: "60166035"<br />  uid: e4236960-8be2-41bf-ac44-e7460378afbb<br /></p>

  观察上述YAML,我们将其整理一下,有这样一些字段:

  label & labelSelector

  Deployment 会根据自己的labelseletor:app=taihao-app-cluster 以及计算出podtemplate的hash lable:pod-template-hash: 5b8b879786 , 筛选出出符合的replicaset, replicaset再根据自己的labelselector 去筛选出符合的pods, 相应的服务发现service,也是通过labelselector去筛选出符合的Pod

  Owner & GC(垃圾回收)

  基于Pod的metadata.ownerReferences找寻到对应的replicaset,replicaset基于自身的metadata.ownerReferences 找寻到deploy;当deployment被删除后,基于原有owner构建的树状,回收原有的rs与pod。

  Deploy & Replicaset

  基于label&labelselector,明确了从上到下的筛选归纳;基于owner&GC,明确了关联资源的回收流程。

  <p style="white-space: pre-wrap;margin: 0px 8px;font-size: 14px;font-weight: normal;word-spacing: 0px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);padding: 0.5em;line-height: 1.75em;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;">apiVersion: apps/v1<br />kind: ReplicaSet<br />metadata:<br />  generation: 1<br />  labels:<br />    app: testdemo<br />    pod-template-hash: bcd889947<br />  name: testdemo-bcd889947<br />  namespace: taihao<br />  ownerReferences:<br />  - apiVersion: apps/v1<br />    blockOwnerDeletion: true<br />    controller: true<br />    kind: Deployment<br />    name: testdemo<br />    uid: 1dddc849-c254-4cf5-aec8-9e1c2b5e65af<br />spec:<br />  replicas: 1<br />  selector:<br />    matchLabels:<br />      app: testdemo<br />      pod-template-hash: bcd889947<br />  template:<br />    metadata:<br />      creationTimestamp: null<br />      labels:<br />        app: testdemo<br />        pod-template-hash: bcd889947<br />    spec:<br />      containers:<br />      - args:<br />        - -c<br />        - sleep 1000000<br />        command:<br />        - sh<br />        image: centos:7<br />        imagePullPolicy: IfNotPresent<br />        name: testdemo<br />status:<br />  fullyLabeledReplicas: 1<br />  observedGeneration: 1<br />  replicas: 1<br /></p>

  replicaset.spec.replicas: 实例数,rs控制下的Pod个数

  replicaset.spec.selector:基于label 筛选出对应的Pod

  

  replicaset.spec.template:replicaset创建的Pod会基于podtemplate

  replicaset.status:replicaset 当前管理Pod的状态

  <p style="white-space: pre-wrap;margin: 0px 8px;font-size: 14px;font-weight: normal;word-spacing: 0px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);padding: 0.5em;line-height: 1.75em;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;">apiVersion: apps/v1<br />kind: Deployment<br />metadata:<br />  labels:<br />    app: testdemo<br />  name: testdemo<br />spec:<br />  replicas: 1<br />  revisionHistoryLimit: 10<br />  selector:<br />    matchLabels:<br />      app: testdemo<br />  strategy:<br />    rollingUpdate:<br />      maxSurge: 25%<br />      maxUnavailable: 25%<br />    type: RollingUpdate<br />  template:<br />    metadata:<br />      creationTimestamp: null<br />      labels:<br />        app: testdemo<br />    spec:<br />      containers:<br />      - args:<br />        - -c<br />        - sleep 1000000<br />        command:<br />        - sh<br />        image: centos:7<br />        imagePullPolicy: IfNotPresent<br />        name: testdemo<br />status:<br />  availableReplicas: 1<br />  observedGeneration: 2<br />  readyReplicas: 1<br />  replicas: 2<br />  unavailableReplicas: 1<br />  updatedReplicas: 1<br /></p>

  deploy.spec.replicas: deploy期望的pod实例格式

  deploy.spec.revisionHistoryLimit:deploy 管理replicaset的保留三个月

  deploy.spec.selector:deploy 筛选符合标签

  deploy.spec.strategy:deploy的升级策略

  deploy.template:deploy基于此模版要创建的pod格式

  04Spec(规范)

  Spec作为Pod的期望状态,一定程度上也覆盖了Pod完整生命周期的逻辑,Pod的生命周期分为以下阶段

  Pod生命周期: Pending

  Pod资源创建完毕后,处于还未调度阶段,这个时候scheduler(调度器)基于pod yaml本身的配置与节点资源状态情况,来进行调度。

  scheduler会去分析podyaml,将其中的策略提取出来,与节点组中的节点配置进行匹配,若匹配成功后,会选出最佳节点,重新修改pod yaml,将spec.nodeName更新掉,完成整个调度环节

  资源策略

  资源策略表明Pod运行需要的资源情况,以demo为例,Pod需要2核4G的资源,那么调度过去的节点也需要有2核4G的资源剩余,Pod才能运行在该节点上

  节点标签筛选策略

  节点标签筛选策略,筛选节点是否存在topology.kubernetes.io/region: cn-hangzhou

  亲和策略

  亲和策略,有节点亲和与Pod亲和(Pod所在节点优先调度),常规来说可以优先满足亲和的节点上,当前例子就是节点亲和,满足标签disk-type=aaa或者disk-type=bbb

  污点策略

  污点策略,当节点上配置了污点,若Pod没有容忍该污点的策略,则Pod不允许调度到该节点上

  Pod生命周期: Creating

  当Pod调度完毕后,开始创建阶段,kubelet会基于pod.spec 期望状态来创建出Podkubelet 在创建Pod阶段,总共大致经历以下过程

  上述阶段,会选择部分关键概念进行详细说明

  image

  <p style="white-space: pre-wrap;margin: 0px 8px;font-size: 14px;font-weight: normal;word-spacing: 0px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);padding: 0.5em;line-height: 1.75em;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;">spec:<br />  containers:<br />  - image: testdemo:v1<br />    imagePullPolicy: Always<br />    name: test-config<br />  imagePullSecrets:<br />  - name: image-regsecret<br /></p>

  containers

  

  注意这个containers用的是复数,可以填多个容器镜像: 比如可以放 nginx 和 业务容器。这样做的好处是可以尽量减少业务容器中与业务无关的代码或进程。

  container涉及很多配置,其中有涉及到volume、env、dnsconfig、host等基础配置

  <p style="white-space: pre-wrap;margin: 0px 8px;font-size: 14px;font-weight: normal;word-spacing: 0px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);padding: 0.5em;line-height: 1.75em;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;">spec:<br />  containers:<br />  - env:<br />    - name: TZ<br />      value: Asia/Shanghai<br />    image: testdemo:v1<br />    name: taihao-app-cn-shanghai-pre-share<br />    volumeMounts:<br />    - mountPath: /home/admin<br />      name: test-config<br />      readOnly: true<br />  dnsConfig:<br />    nameservers:<br />    - 100.100.1.1<br />    - 100.100.2.1<br />    options:<br />    - name: ndots<br />      value: "3"<br />    - name: timeout<br />      value: "3"<br />    - name: attempts<br />      value: "3"<br />    searches:<br />    - default.svc.cluster.local<br />    - svc.cluster.local<br />    - cluster.local<br />  hostAliases:<br />  - hostnames:<br />    - kubernetes<br />    - kubernetes.default<br />    - kubernetes.default.svc<br />    - kubernetes.default.svc.cluster.local<br />    ip: 1.1.1.1<br />  volumes:<br />  - configMap:<br />      defaultMode: 420<br />      name: test-config<br />    name: test-config<br /></p>

  env:配置Pod的环境变量

  dnsConfig:配置Pod的域名解析

  hostALiases:配置/etc/hosts文件内容

  volume/volumeMount: 配置文件挂载到容器内,也可以配置文件存储系统挂载到容器内

  postStart

  <p style="white-space: pre-wrap;margin: 0px 8px;font-size: 14px;font-weight: normal;word-spacing: 0px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);padding: 0.5em;line-height: 1.75em;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;">containers:<br />  - image: testdemo:v1<br />    imagePullPolicy: Always<br />    lifecycle:<br />      postStart:<br />        exec:<br />          command:<br />          - /bin/sh<br />          - -c<br />          - sleep 5<br /></p>

  当前poststart demo 是发起command命令,也可以发起http请求,主要作用可以作为资源部署以及环境准备。

  Pod 生命周期: Running

  在Pod running阶段的时候,Pod就迎来对其健康的检查,当前kubelet 提供三种方式判定

  <p style="white-space: pre-wrap;margin: 0px 8px;font-size: 14px;font-weight: normal;word-spacing: 0px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);padding: 0.5em;line-height: 1.75em;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;">spec:<br />  readinessGates:<br />  - conditionType: TestPodReady<br />  containers:<br />  - image: testdemo:v1<br />    imagePullPolicy: Always<br />    livenessProbe:<br />      failureThreshold: 3<br />      initialDelaySeconds: 45<br />      periodSeconds: 5<br />      successThreshold: 1<br />      tcpSocket:<br />        port: 8080<br />      timeoutSeconds: 1<br />    readinessProbe:<br />      failureThreshold: 3<br />      httpGet:<br />        path: /actuator/health<br />        port: 8989<br />        scheme: HTTP<br />      initialDelaySeconds: 25<br />      periodSeconds: 3<br />      successThreshold: 1<br />      timeoutSeconds: 1<br /></p>

  readiness与liveness检查参数都是一致的

  readiness、liveness虽然参数不一样,但对检验的结果行为不一致。

  readinessGate 是Pod健康的扩展,kubelet会基于此,默认在pod.status.conditions上配置对应的condition, 比如当前例子readinessGate为conditionType: TestPodReady, 则相应就会有conditions

  <p style="white-space: pre-wrap;margin: 0px 8px;font-size: 14px;font-weight: normal;word-spacing: 0px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);padding: 0.5em;line-height: 1.75em;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;">status:<br />  conditions:<br />  - lastProbeTime: null<br />    lastTransitionTime: "2022-07-05T09:16:07Z"<br />    status: "false"<br />    type: TestPodReady<br /></p>

  当该condition.status为false时,则Pod就会一直是不健康,哪怕readiness检查通过,直到第三方系统去操作更新Pod该condition.status为true,才可以将Pod变为健康,这样就可以接入更多的Pod健康指标。

  Pod 生命周期: Terminating

  client 在发起请求删除Pod的时候,实际上是配置

  pod.metadata.deletionTimestamp,kubelet感知到后,开始进行Pod回收流程

  整个Pod的回收周期,常规来说preStop—>SIGTERM—>SIGKILL

  <p style="white-space: pre-wrap;margin: 0px 8px;font-size: 14px;font-weight: normal;word-spacing: 0px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);padding: 0.5em;line-height: 1.75em;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;">lifecycle:<br />  preStop:<br />    exec:<br />      command:<br />      - /bin/sh<br />      - -c<br />      - sleep 5<br /></p>

  当kubelet进行preStop后,开始发起SIGTERM给容器内进程,若超过总默认耗时30S(metadata.DeletionGracePeriodSeconds),则强制发起SIGKILL给容器,也就是prestop+SIGTERM总耗时不允许超过30s。

  05Status(状态)

  <p style="white-space: pre-wrap;margin: 0px 8px;font-size: 14px;font-weight: normal;word-spacing: 0px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);padding: 0.5em;line-height: 1.75em;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;">status:<br />  conditions:<br />  - lastProbeTime: null<br />    lastTransitionTime: "2022-07-05T09:16:07Z"<br />    status: "True"<br />    type: TestPodReady<br />  - lastProbeTime: null<br />    lastTransitionTime: "2022-07-05T09:16:07Z"<br />    status: "True"<br />    type: Initialized<br />  - lastProbeTime: null<br />    lastTransitionTime: "2022-07-05T09:16:14Z"<br />    status: "True"<br />    type: Ready<br />  - lastProbeTime: null<br />    lastTransitionTime: "2022-07-05T09:16:14Z"<br />    status: "True"<br />    type: ContainersReady<br />  - lastProbeTime: null<br />    lastTransitionTime: "2022-07-05T09:16:07Z"<br />    status: "False"<br />    type: ContainerDiskPressure<br />  - lastProbeTime: null<br />    lastTransitionTime: "2022-07-05T09:16:07Z"<br />    status: "True"<br />    type: PodScheduled<br />  containerStatuses:<br />  - containerID: containerd://xxxxx<br />    image: docker.io/library/testdemo:v1<br />    imageID: docker.io/library/centos@sha256:xxxx<br />    lastState: {}<br />    name: zxtest<br />    ready: true<br />    restartCount: 0<br />    started: true<br />    state:<br />      running:<br />        startedAt: "2022-07-05T09:16:13Z"<br />  hostIP: 21.1.96.23<br />  phase: Running<br />  podIP: 10.11.17.172<br />  podIPs:<br />  - ip: 10.11.17.172<br />  qosClass: Guaranteed<br />  startTime: "2022-07-05T09:16:07Z"<br /></p>

  基于上述YAML样例,将Pod status状态拆建出来分析一下:

  通过以上Pod四个部分拆解,我们基本搞清了一个Pod在k8s下“从哪里来”的这个问题。本系列的后续的文章会对“到哪里去”这个问题继续展开:Kubernetes的魅力在于不仅仅是拉起一个工作负载,而是能够召之即来挥之即去地编排海量工作负载。

  技 术 好 文

  企 业 案 例

  云 专 栏

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线