基础概念

Pod 是可以在 Kubernetes 中创建和管理的、最小的可部署的计算单元。

Pod是一组(一个或多个) 容器,这些容器共享存储、网络、以及怎样运行这些容器的声明。其中共享上下文包括一组 Linux namespace、控制组(cgroup)和可能一些其他的隔离方面, 即用来隔离容器的技术。 在 Pod 的上下文中,每个独立的应用可能会进一步实施隔离。

Pod 通常不是直接创建的,而是使用工作负载资源创建的

Pod的一些小知识点

运行方式

pod很少有直接创建的,当然,个别排查工具就是直接拉起一个pod,比如dnsutils。专门用于排查dns问题,更多的情况下,pod是有控制器(Deployment 、DS控制器、sts等)进行创建并进行资源管理,Pod不具备自愈能力,挂了就是挂了,因此需要控制器进行管理

内部容器之间协调工作

因为pod是由一组容器构建的最小化的工作单元,所以多个容器之间是可以互相协调工作的。但是前提是pod内各个容器的内部数据资源需要共享

例如:假设此时Pod内有以下资源

  • 业务容器(service-a) ,具有挂载卷v-log
  • 日志收集服务(log-agent),具有挂载卷v-log

业务容器通过挂载卷v-log,将日志写入至 /mnt/log/service 目录
日志收集服务可以通过挂载卷v-log,读取 /mnt/log/service 目录内的数据内容,并进行相应处理,这就实现了一个简单的边车采集日志功能

Pod的生命周期

  • Pending
    Pod 已被 Kubernetes 系统接受,但有一个或者多个容器尚未创建亦未运行。此阶段包括等待 Pod 被调度的时间和通过网络下载镜像的时间。
  • Running
    至少其中有一个主要容器正常启动,或者正在重启
  • Succeeded or Failed
    取决于 Pod 中是否有容器以失败状态结束
  • Unknown
    出现了一些未知的异常情况

Pod的检测探针

每个pod运行的情况不一样,有些pod确实需要存活检测,如果有问题,k8s可以及时发现,并重新拉起一个新的pod,k8s中一共有三种探针机制

livenessProbe(存活性检测)

指示容器是否正在运行。如果存活态探测失败,则 kubelet 会杀死容器, 并且容器将根据其重启策略决定未来。如果容器不提供存活探针, 则默认状态为 Success。

readinessProbe(就绪检测,可以拨入流量)

指示容器是否准备好为请求提供服务。如果就绪态探测失败, 端点控制器将从与 Pod 匹配的所有服务的端点列表中删除该 Pod 的 IP 地址。 初始延迟之前的就绪态的状态值默认为 Failure。 如果容器不提供就绪态探针,则默认状态为 Success。

startupProbe(启动完毕检测)

指示容器中的应用是否已经启动。如果提供了启动探针,则所有其他探针都会被 禁用,直到此探针成功为止。如果启动探测失败,kubelet 将杀死容器,而容器依其 重启策略进行重启。 如果容器没有提供启动探测,则默认状态为 Success。

正确的停止一个pod

以JAVA的微服务为例子,程序就绪之后,一般会向注册中心发送注册信息,并暴露自己的ip、端口等基本内容,但是在程序停止时,如果没有正确的接受到停止运行的命令,则只能等待注册中心将当前容器标记为不健康,并移除,但是等待期间,依然会有流量进入,那就肯定是404或者500,对用户造成不好的体验

使用PreStop进行处理

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

  • PostStart :这个钩子在容器创建后立即执行。但是,并不能保证钩子将在容器ENTRYPOINT之前运行,因为没有参数传递给处理程序。 主要用于资源部署、环境准备等。不过需要注意的是如果钩子花费时间过长以及于不能运行或者挂起,容器将不能达到Running状态。另外,PostStart的执行相对于容器的代码执行是异步的。
  • PreStop :钩子在容器终止前立即被调用。它是阻塞的,意味着它是同步的,所以它必须在删除容器的调用触发之前完成。主要用于优雅关闭应用程序、通知其他系统等。如果钩子在执行期间挂起,Pod阶段将停留在Running状态并且不会达到failed状态。另外,PreStop的执行现对于SIGTERM信息,也是异步的,k8s也不会等待PreStop执行完成。
简单的示例代码
apiVersion: v1
kind: Pod
metadata:
  name: erp-service
spec:
  containers:
    - name: erp-service
      image: xxxxx/erp-service:1.0.0.20210314
      ports:
        - containerPort: 8080
      lifecycle:
        preStop:
          exec:
            ## 发送停止命令到当前服务,让其主动脱离注册中心并离线
            command:
              - bash
              - -c
              - 'curl -X GET http://127.0.0.1:8080/service-shutdown'

使用Init容器进行协助初始化

每个 Pod 中可以包含多个容器, 应用运行在这些容器里面,同时 Pod 也可以有一个或多个先于应用容器启动的 Init 容器。

Init 容器特点

  • 它们总是运行到完成
  • 每个都必须在下一个启动之前成功完成。如果 Pod 的 Init 容器失败,kubelet 会不断地重启该 Init 容器直到该容器成功为止
  • 不支持 lifecycle、livenessProbe、readinessProbe 和 startupProbe, 因为它们必须在 Pod 就绪之前运行完成
  • 如果为一个 Pod 指定了多个 Init 容器,这些容器会按顺序逐个运行。 每个 Init 容器必须运行成功,下一个才能够运行。当所有的 Init 容器运行完成时, Kubernetes 才会为 Pod 初始化应用容器并像平常一样运行
  • 每个 Init 容器会在网络和数据卷初始化之后按顺序启动。
  • 在所有的 Init 容器没有成功之前,Pod 将不会变成 Ready 状态。 Init 容器的端口将不会在 Service 中进行聚集。正在初始化中的 Pod 处于 Pending 状态, 但会将状况 Initializing 设置为 false。
  • 如果 Pod 重启,所有 Init 容器必须重新执行。因为 Init 容器可能会被重启、重试或者重新执行,所以 Init 容器的代码应该是幂等的。 特别地,基于 emptyDirs 写文件的代码,应该对输出文件可能已经存在做好准备。

简单的 Init 容器示例

yml来自k8s官方模版

apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app.kubernetes.io/name: MyApp
spec:
  containers:
    - name: myapp-container
      image: busybox:1.28
      command: ['sh', '-c', 'echo The app is running! && sleep 3600']
  initContainers:
    - name: init-myservice
      image: busybox:1.28
      command: ['sh', '-c', "until nslookup myservice.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for myservice; sleep 2; done"]
    - name: init-mydb
      image: busybox:1.28
      command: ['sh', '-c', "until nslookup mydb.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for mydb; sleep 2; done"]