【k8s】Kubernetes 中的核心概念 Pod(三)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


一、为什么要有 Pod

Pod 这个词原意是“豌豆荚”,后来又延伸出“舱室”“太空舱”等含义,你可以看一下这张图片,形象地来说 Pod 就是包含了很多组件、成员的一种结构。

容器技术,它让进程在一个“沙盒”环境里运行,具有良好的隔离性,对应用是一个非常好的封装。不过,当容器技术进入到现实的生产环境中时,这种隔离性就带来了一些麻烦。因为很少有应用是完全独立运行的,经常需要几个进程互相协作才能完成任务,比如搭建 WordPress 网站的时候,就需要 Nginx、WordPress、MariaDB 三个容器一起工作。

WordPress 例子里的这三个应用之间的关系还是比较松散的,它们可以分别调度,运行在不同的机器上也能够以 IP 地址通信。但还有一些特殊情况,多个应用结合得非常紧密以至于无法把它们拆开。比如,有的应用运行前需要其他应用帮它初始化一些配置,还有就是日志代理,它必须读取另一个应用存储在本地磁盘的文件再转发出去。这些应用如果被强制分离成两个容器,切断联系,就无法正常工作了。

那么把这些应用都放在一个容器里运行可不可以呢?
当然可以,但这并不是一种好的做法。因为容器的理念是对应用的独立封装,它里面就应该是一个进程、一个应用,如果里面有多个应用,不仅违背了容器的初衷,也会让容器更难以管理。

为了解决这样多应用联合运行的问题,同时还要不破坏容器的隔离,就需要在容器外面再建立一个“收纳舱”,让多个容器既保持相对独立,又能够小范围共享网络、存储等资源,而且永远是“绑在一起”的状态。

所以,Pod 的概念也就呼之欲出了,容器正是“豆荚”里那些小小的“豌豆”,你可以在 Pod 的 YAML 里看到,“spec.containers”字段其实是一个数组,里面允许定义多个容器。

Pod 就是由客厅、卧室、厨房等预制房间拼装成的一个齐全的生活环境,不仅同样具备易于拆装易于搬迁的优点,而且要比单独的“一居室”功能强大得多,能够让进程“住”得更舒服。

二、为什么 Pod 是 Kubernetes 的核心对象

因为 Pod 是对容器的“打包”,里面的容器是一个整体,总是能够一起调度、一起运行,绝不会出现分离的情况,而且 Pod 属于 Kubernetes,可以在不触碰下层容器的情况下任意定制修改。所以有了 Pod 这个抽象概念,Kubernetes 在集群级别上管理应用就会“得心应手”了。

Kubernetes 让 Pod 去编排处理容器,然后把 Pod 作为应用调度部署的最小单位,Pod 也因此成为了 Kubernetes 世界里的“原子”(当然这个“原子”内部是有结构的,不是铁板一块),基于 Pod 就可以构建出更多更复杂的业务形态了。

在这里插入图片描述
这张图现在已经不能全面地描述 Kubernetes 的资源对象了,但是我们还是可以从图中看出 Kubernetes 资源都是直接或者间接通过 Pod 实现,Pod 确实是 Kubernetes 的核心对象。

三、如何使用 YAML 描述 Pod

提示:我们始终可以用命令 kubectl explain 来查看pod任意字段的详细说明,包括但不限于pod对象

因为 Pod 也是 API 对象,所以它也必然具有 apiVersionkindmetadataspec 这四个基本组成部分。
apiVersionkind这两个字段很简单,对于 Pod 来说分别是固定的值 v1 和 Pod,而一般来说,metadata里应该有 namelabels 这两个字段。

Kubernetes 里,Pod 必须要有一个名字,这也是 Kubernetes 里所有资源对象的一个约定。在课程里,我通常会为 Pod 名字统一加上 pod 后缀,这样可以和其他类型的资源区分开。name 只是一个基本的标识,信息有限,所以 labels 字段就派上了用处。它可以添加任意数量的 Key-Value,给 Pod“贴”上归类的标签,结合 name 就更方便识别和管理了。

下面这段 YAML 代码就描述了一个简单的 Pod,名字是“busy-pod”,再附加上一些标签:

apiVersion: v1
kind: Pod
metadata:
  name: busy-pod
  labels:
    owner: chrono
    env: demo
    region: north
    tier: back

metadata一般写上 namelabels 就足够了,而spec字段由于需要管理、维护 Pod 这个 Kubernetes 的基本调度单元,里面有非常多的关键信息。其中至关重要的是 container。还有 restartPolicy hostname 等字段,可以使用kubectl explain 查询。

containers 是一个数组,里面的每一个元素又是一个 container 对象,也就是容器。和 Pod 一样,container 对象也必须要有一个 name 表示名字,然后当然还要有一个 image 字段来说明它使用的镜像,这两个字段是必须要有的,否则 Kubernetes 会报告数据验证错误。

下面是几个基本的 container 对象的字段:

  • ports:列出容器对外暴露的端口,和 Docker 的 -p 参数有点像。
  • imagePullPolicy:指定镜像的拉取策略,可以是 Always/Never/IfNotPresent,一般默认是 IfNotPresent,也就是说只有本地不存在才会远程拉取镜像,可以减少网络消耗。
  • env:定义 Pod 的环境变量,和 Dockerfile 里的 ENV 指令有点类似,但它是运行时指定的,更加灵活可配置。
  • command:定义容器启动时要执行的命令,相当于 Dockerfile 里的 ENTRYPOINT 指令。
  • args:它是 command 运行时的参数,相当于 Dockerfile 里的 CMD 指令,这两个命令和 Docker 的含义不同,要特别注意。

下面就是编写“busy-pod”的 spec 部分,添加 env、command、args 等字段:

spec:
  containers:
  - image: busybox:latest
    name: busy
    imagePullPolicy: IfNotPresent
    env:
      - name: os
        value: "ubuntu"
      - name: debug
        value: "on"
    command:
      - /bin/echo
    args:
      - "$(os), $(debug)"

这里我为 Pod 指定使用镜像 busybox:latest,拉取策略是 IfNotPresent ,然后定义了 os 和 debug 两个环境变量,启动命令是 /bin/echo,参数里输出刚才定义的环境变量。把这份 YAML 文件和 Docker 命令对比一下,你就可以看出,YAML 在 spec.containers 字段里用“声明式”把容器的运行状态描述得非常清晰准确,要比 docker run 那长长的命令行要整洁的多,对人、对机器都非常友好。

四、使用 kubectl 操作 Pod

kubectl apply、kubectl delete 它们可以使用 -f 参数指定 YAML 文件创建或者删除 Pod,例如:

$ kubectl apply -f busy-pod.yml
$ kubectl delete -f busy-pod.yml
也可以使用 pod 名字进行删除
$ kebectl delete pod busy-pod

我们可以用命令 kubectl logs,它会把 Pod 的标准输出流信息展示给我们看,在这里就会显示出预设的两个环境变量的值:

$ kubectl logs busy-pod

在这里插入图片描述
我们可以使用下面的命令查看 Pod 的状态:

$ kubelet get pods

在这里插入图片描述
你会发现这个 Pod 运行有点不正常,状态是CrashLoopBackOff,那么我们可以使用命令 kubectl describe 来检查它的详细状态,它在调试排错时很有用:

$ kubectl describe pod busy-pod
Name:             busy-pod
Namespace:        default
Priority:         0
Service Account:  default
Node:             minikube/192.168.49.2
Start Time:   Sat, 08 Apr 2023 20:22:02 +0800
Labels:           env=dev
                  owner=backend-dev-team
                  region=north
                  tier=back
Annotations:      <none>
Status:           Running
IP:               172.17.0.3
IPs:
  IP:  172.17.0.3
Containers:
  busy:
    Container ID:  docker://3dc27aa07b30f65887c7d525e4aa60eb633250963fa8f2db4f995a94452f3e07
    Image:         busybox:latest
    Image ID:      docker-pullable://busybox@sha256:ad9bd57a3a57cc95515c537b89aaa69d83a6df54c4050fcf2b41ad367bec0cd5
    Port:          <none>
    Host Port:     <none>
    Command:
      /bin/echo
    Args:
      $(os), $(debug)
    State:          Waiting
      Reason:       CrashLoopBackOff
    Last State:     Terminated
      Reason:       Completed
      Exit Code:    0
      Started:      Sat, 08 Apr 2023 20:25:11 +0800
      Finished:     Sat, 08 Apr 2023 20:25:11 +0800
    Ready:          False
    Restart Count:  6
    Environment:
      os:     ubuntu
      debug:  on
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-d2k7b (ro)
Conditions:
  Type              Status
  Initialized       True 
  Ready             False 
  ContainersReady   False 
  PodScheduled      True 
Volumes:
  kube-api-access-d2k7b:
    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  9m48s                   default-scheduler  Successfully assigned default/busy-pod to minikube
  Normal   Pulling    9m47s                   kubelet            Pulling image "busybox:latest"
  Normal   Pulled     9m34s                   kubelet            Successfully pulled image "busybox:latest" in 13.564367044s
  Normal   Created    8m (x5 over 9m34s)      kubelet            Created container busy
  Normal   Started    8m (x5 over 9m33s)      kubelet            Started container busy
  Normal   Pulled     8m (x4 over 9m33s)      kubelet            Container image "busybox:latest" already present on machine
  Warning  BackOff    4m44s (x24 over 9m32s)  kubelet            Back-off restarting failed container

通常需要关注的是末尾的Events部分,它显示的是 Pod 运行过程中的一些关键节点事件。对于这个 busy-pod,因为它只执行了一条 echo 命令就退出了,而 Kubernetes 默认会重启 Pod,所以就会进入一个反复停止 - 启动的循环错误状态。

因为 Kubernetes 里运行的应用大部分都是不会主动退出的服务

另外,kubectl也提供与 docker 类似的 cp 和 exec 命令,kubectl cp 可以把本地文件拷贝进 Pod,kubectl exec 是进入 Pod 内部执行 Shell 命令,用法也差不多。

$ kubectl cp example.txt busy-pod:/tmp
$ kubectl exec -it ngx-pod -- sh

扩展

  • Pod 管理了多个进程,也可以理解为 Linux 里的“进程组”这个概念。Pod 也可以被称为“容器组”,或者“逻辑主机”;

  • Pod 内部有一个名为 infra 的容器,它实际上代表 Pod,维护 Pod 内多容器共享的主机名、网络和存储。infra 容器的镜像叫 pause,只有不到 500 KB;

  • metadata 里面的标签不能随意写,必须符合域名规范(FQDN);

  • kubectl get pods 里面的 READY 一栏显示的是 Pod 内的容器状态,格式是 x/y,表示总共定义 y 个容器,其中x 个是正常的。

总结

  1. 现实中经常会有多个进程密切协作才能完成任务的应用,而仅使用容器很难描述这种关系,所以就出现了 Pod,它“打包”一个或多个容器,保证里面的进程能够被整体调度。
  2. Pod 是 Kubernetes 管理应用的最小单位,其他的所有概念都是从 Pod 衍生出来的
  3. Pod 也应该使用 YAML“声明式”描述,关键字段是“spec.containers”,列出名字、镜像、端口等要素,定义内部的容器运行状态。
  4. 操作 Pod 的命令很多与 Docker 类似,如 kubectl run、kubectl cp、kubectl exec 等,但有的命令有些小差异,使用的时候需要注意。

虽然 Pod 是 Kubernetes 的核心概念,非常重要,但事实上在 Kubernetes 里通常并不会直接创建 Pod,因为它只是对容器做了简单的包装,比较脆弱,离复杂的业务需求还有些距离,需要 Job、CronJob、Deployment 等其他对象增添更多的功能才能投入生产使用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值