Kubernetes Pod 使用介绍

91

在 VMware 的世界中,调度的原子单位是虚拟机(VM);在 Docker 的世界中,调度的原子单位是容器(Container);而在 Kubernetes 的世界中,调度的原子单位是 Pod。

在Kubernetes中,用户无法直接在集群中运行一个容器,而是需要将容器定义在 Pod 中来运行。Pod 是 Kubernetes 中最小的可部署和调度单元,通常包含一个或多个容器,这些紧密耦合容器共享名字空间和文件系统卷。

Pod 有两种运行方式:

一种是单独运行(叫做单例):这种方式运行的 Pod 没有自愈能力,一旦因为各种原因被删除就不会再重新创建。

另一种则是常见的在控制器管理下运行,控制器会持续监控 Pod 副本数量是否符合预期,并在 Pod 异常时重新创建新的 Pod 进行替换。

01 单独运行Pod

1.1 单独运行Pod

k8s 中的各种资源对象基本都是由 yaml 文件定义,Pod 也不例外。

定义Pod资源文件:nginx.yaml

# nginx.yaml
apiVersion: v1
kind: Pod  # 资源类型=pod
metadata:
  name: nginx-pod  # 需要在当前命名空间中唯一
  namespace: default # 命名默认是default,可省略
spec:
  containers: # pod内的容器组
    - name: nginx-container
      image: nginx  # 镜像默认来源 DockerHub

1.2 创建Pod

使用kubectl 访问api-server,创建Pod

kubectl apply -f nginx.yaml

1.3 查看Pod运行情况

[root@kube-01 data]# kubectl get pod nginx-pod
NAME        READY   STATUS    RESTARTS   AGE
nginx-pod   1/1     Running   0          29m

1.4 查看所有Pod

[root@kube-01 data]# kubectl get pods
NAME        READY   STATUS    RESTARTS   AGE
nginx-pod   1/1     Running   0          30m

1.5 删除Pod

[root@kube-01 data]# kubectl delete pod nginx-pod
pod "nginx-pod" deleted

注意:这种方式运行的 Pod 没有自愈能力,一旦因为各种原因被删除就不会再重新创建。

02 修改Pod

创建 Pod 后,我们可能会修改 Pod 的部分配置,比如修改镜像版本,修改容器启动参数等。

直接修改 yaml 文件然后再次 apply 即可

kubectl apply -f nginx.yaml

修改容器配置会触发 Pod 内的容器重启,Pod 本身不会完全重启。

03 Pod 与 Container 的不同

在刚刚创建的资源里,在最内层是我们的服务 nginx,运行在 Container 当中

Container (容器) 的本质是进程,而 Pod 是管理这一组进程的资源。

04 Sidecar 模式

Pod 可以管理多个 Container。例如在某些场景服务之间需要文件交换(日志收集),本地网络通信需求(使用 localhost 或者 Socket 文件进行本地通信),这时候就会部署多容器 Pod。 这是一种常见的容器设计模式,它有个名称叫做 Sidecar。Sidecar 模式中主要包含两类容器,一类是主应用容器,另一类是辅助容器(称为 sidecar 容器)提供额外的功能,它们共享相同的网络和存储空间。这个模式的灵感来自于摩托车上的辅助座位,因此得名 "Sidecar"。

05 Init 容器

Init 容器是一种特殊容器,在 Pod 内的应用容器启动之前运行。Init 容器可以包括一些应用镜像中不存在的实用工具和安装脚本。

Init 容器有一些特点如下:

  • 允许定义一个或多个 Init 容器

  • 它们会先于 Pod 内其他普通容器启动

  • 它们总会在运行短暂时间后进入Completed状态

  • 它们会严格按照定义的顺序启动,每个容器成功运行结束后才会启动下一个

  • 任意一个 Init 容器运行失败都会导致 Pod 进入异常状态,这会引起 Pod 重启,除非 PodSpec 中设置的restartPolicy 策略为Never

  • 当 Init 容器正常终止时(exitCode=0),即使 PodSpec 中设置的restartPolicy策略为Always,Pod 也不会重启

  • 修改 Init 容器的镜像会导致 Pod 重启

  • Init 容器支持普通容器的全部字段和特性,除了lifecycle、livenessProbe、readinessProbe 和 startupProbe

  • Pod 重启会导致 Init 容器重新执行

Init 容器具有与应用容器分离的单独镜像,我们可以使用它来完成一些常规的脚本任务,比如需要sed、awk、python 或 dig 这样的工具完成的任务,而没必要在应用容器中去安装这些命令。

例子:

apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app.kubernetes.io/name: MyApp
spec:
  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" ]
  containers:
    - name: myapp-container
      image: busybox:1.28
      command: [ 'sh', '-c', 'echo The app is running! && sleep 3600' ]

这个模板中定义了两个 Init 容器, 分别的任务是持续等待一个 Service 启动,等待期间会打印消息,当各自等待的 Service 都启动后,两个 Init 容器会自动退出,然后再启动普通容器。

06 Pod 的生命周期

通过kubectl get pods看到的STATUS字段存在以下情况:

Pending(挂起): Pod 正在调度中(主要是节点选择)。

ContainerCreating(容器创建中): Pod 已经被调度,但其中的容器尚未完全创建和启动(包含镜像拉取)。

Running(运行中): Pod 中的容器已经在运行。

Terminating(正在终止): 删除或重启 Pod 会使其进入此状态,Pod 默认有一个终止宽限时间是 30s,可以在模板中修改(Pod 可能由于某些原因会一直停留在此状态)。

Succeeded(已成功终止): 所有容器都成功终止,任务或工作完成,特指那些一次性或批处理任务而不是常驻容器。

Failed(已失败): 至少一个容器以非零退出码终止。

Unknown(未知): 无法获取 Pod 的状态,通常是与 Pod 所在节点通信失败导致。

07 Pod 的重启策略

spec.restartPolicy 字段,可选值为 Always/OnFailure/Never。

此策略对 Pod 内所有容器有效, 由 Pod 所在 Node 上的 kubelet 执行判断和重启。

由 kubelet 重启的已退出容器将会以递增延迟的方式(10s,20s,40s...) 尝试重启,上限 5min。成功运行 10min 后这个时间会重置。

一旦 Pod 绑定到某个节点上,除非节点自身问题或手动调整, 否则不会再调度到其他节点。

08 Pod 的销毁过程

1 用户发送 Pod 删除命令

2 API Server 更新 Pod:开始销毁,并设定宽限时间(默认 30s,可通过--grace-period=n 指定,为 0 时需要追加--force),超时强制 Kill

3 同时触发:

Pod 标记为 Terminating

kubelet 监听到 Terminating 状态,开始终止 Pod

Endpoint 控制器监控到 Pod 即将删除,将移除所有 Service 对象中与此 Pod 关联的 Endpoint 对象

3 如 Pod 定义了 prepStop 回调,则会在 Pod 中执行,并再次执行步骤 2,且增加宽限时间 2s

4 Pod 进程收到 SIGTERM 信号

5 到达宽限时间还在运行,kubelet 发送 SIGKILL 信号,设置宽限时间 0s,直接删除 Pod。

尽管可以指定--force参数,但有些 Pod 可能处于某种异常状态仍然不能从节点上被彻底删除, 即使你通过kubectl get po 看不到对应的 Pod。这对于 StatefulSet 类型的应用可能是灾难性的,通过https://kubernetes.io/zh-cn/docs/tasks/run-application/force-delete-stateful-set-pod/ 了解更多细节。

09 Pod中容器的状态

一旦调度器将 Pod 分派给某个节点,kubelet 就通过容器运行时开始为 Pod 创建容器。容器的状态有三种:Waiting(等待)、Running(运行中)和 Terminated(已终止)

使用 kubectl describe pod <pod 名称>来检查每个容器的状态(输出中的Containers.State)

10 Pod与控制器

在生产环境中,我们很少会直接部署一个单实例 Pod。

因为 Pod 被设计为一种临时性的一次性实体,当因为人为或资源不足等情况被删除时,Pod 不会自动恢复。

但是当我们使用控制器来创建 Pod 时,Pod 的生命周期就会受到控制器管理。这种管理具体表现为 Pod 将拥有横向扩展以及故障自愈 的能力。

常见的控制器类型如下:

  • Deployment

  • StatefulSet

  • DaemonSet

  • Job/CronJob

11 Pod联网

每个 Pod 能够获得一个独立的 IP 地址,Pod 中的容器共享网络命名空间,包括 IP 地址和网络端口。

Pod 内的容器可以使用 localhost 互相通信,它们也能通过如 SystemV 信号量或 POSIX 共享内存这类标准的进程间通信方式互相通信。

Pod 间通信或 Pod 对外暴露服务,需要使用 Service 资源。