一. 简介

Pod,是 Kubernetes 项目中最小的 API 对象。一个Pod里面可以运行多个用户容器,容器之间可以存在一定的拓扑结构。

但是,Pod 只是一个逻辑概念,提供的是一种编排思想,而不是具体的技术方案。准确来说,Kubernetes 真正处理的,还是宿主机操作系统上 Linux 容器的 Namespace 和 Cgroups,而并不存在一个所谓的 Pod 的边界或者隔离环境。

关于本文的案例代码,都在此链接中:GitHub项目地址

二. 共享网络

2.1 原理

Pod,其实是一组共享了某些资源的容器。具体的说:Pod 里的所有容器,共享的是同一个 Network Namespace,并且可以声明共享同一个 Volume。

2.2 Infra容器

在 Kubernetes 项目里,Pod 的实现需要使用一个中间容器,这个容器叫作 Infra 容器。在这个 Pod 中,Infra 容器永远都是第一个被创建的容器,而其他用户定义的容器,则通过Join Network Namespace的方式,与 Infra 容器关联在一起。

例如上图,这个 Pod 里有两个容器Container-1 和Container-2,还有一个 Infra 容器。在 Kubernetes 项目里,Infra 容器一定要占用极少的资源,所以它使用的是一个非常特殊的镜像,叫作:k8s.gcr.io/pause
这个镜像是一个用汇编语言编写的、永远处于“暂停”状态的容器,解压后的大小也只有100~200 KB 左右。

如下就能看到一对一的关系:

2.3 流程

对于 Pod 里的Container-1 和Container-2 来说:

  • 容器可以直接使用 localhost 进行通信。
  • 容器看到的网络设备跟 Infra 容器看到的完全一样。
  • 一个 Pod 只有一个 IP 地址,也就是这个 Pod 的 Network Namespace 对应的 IP 地址。
  • 其他的所有网络资源,都是一个 Pod 一份,并且被该 Pod 中的所有容器共享。
  • Pod 的生命周期只跟 Infra 容器一致,而与容器 A 和 B 无关。

三. 共享存储

3.1 原理

在Pod中共享文件或目录很简单,实际上是通过将Volume转移到Pod级别来完成的。容器中的所有容器共享所有Volume。

3.2 案例

如下案例,描述共享存储的案例:

apiVersion: v1
kind: Pod
metadata:
  name: demo-containers
spec:
  restartPolicy: Never
  volumes:
  - name: shared-data
    hostPath:      
      path: /data
  containers:
  - name: nginx-container
    image: nginx
    volumeMounts:
    - name: shared-data
      mountPath: /usr/share/nginx/html
  - name: debian-container
    image: debian
    volumeMounts:
    - name: shared-data
      mountPath: /pod-data
    command: ["/bin/sh"]
    args: ["-c", "echo Hello World > /pod-data/index.html"]

在这个例子中,debian-containernginx-container 都声明挂载了 shared-data 这个 Volume。而shared-datahostPath 类型。所以,它对应在宿主机上的目录就是:/data
而这个目录,其实就被同时绑定挂载进了上述两个容器当中,所以 nginx-container可以从它的/usr/share/nginx/html目录中读取到 debian-container生成的 index.html 文件。

四. 拓展

4.1 超亲密关系

Pod 是一种“超亲密关系”容器的设计思想。当用户需要思考多个容器之间是否需要部署进同一个Pod时,这是一个首要问题。

简单的场景如下:

关于“超亲密关系”,可以分为以下几类:

  • 文件在进程之间交换。一个进程写入日志,而另一个进程读取日志。
  • 两个进程都需要通过localhost或本地套接字相互通信。
  • 容器和微服务都需要频繁的RPC调用。这可以提高性能。
  • 两个容器或应用程序需要共享一些Linux名称空间。例如,将一个容器添加到另一个容器的网络名称空间中,以查看网络设备和该容器的网络信息。

4.2 Pod级别

在Pod YAML中,可以做出如下的分类:

凡是调度、网络、存储,以及安全相关的属性,基本上是 Pod 级别的。
凡是跟容器的 Linux Namespace 相关的属性,也一定是 Pod 级别的。Pod 的设计,就是要让它里面的容器尽可能多地共享 Linux Namespace,仅保留必要的隔离和限制能力。
凡是 Pod 中的容器要共享宿主机的 Namespace,也一定是 Pod 级别的定义。

4.3 Pod参数

案例Pod YAML如下:

apiVersion: v1
kind: Pod
metadata:
  name: demo-pod
spec:
  nodeSelector:
    gpu: nvida
  containers:
    - name: demo-pod-container
      image: nginx
      imagePullPolicy: Always
      lifecycle:
        postStart:
          exec:
            command:
              ["/bin/sh", "-c", "echo Hello PostStart > /usr/share/message"]
        preStop:
          exec:
            command: ["/usr/sbin/nginx", "-s", "quit"]

关于Pod参数的具体含义:

  • NodeSelector

帮助供用户将 Pod 与 Node 进行绑定的字段,意味着这个 Pod 永远只能运行在携带了“gpu: nvida”标签(Label)的节点上;否则,它将调度失败。

  • NodeName(隐式):

一旦 Pod 的这个字段被赋值,Kubernetes 项目就会被认为这个 Pod 已经经过了调度,调度的结果就是赋值的节点名字。所以,这个字段一般由调度器负责设置。

  • HostAliases

定义了 Pod 的 hosts 文件(比如 /etc/hosts)里的内容。

  • Init Containers(optional)

Init Containers的生命周期,会先于所有的 Containers,并且严格按照定义的顺序执行。

关于Container参数的具体含义

  • ImagePullPolicy

这定义了镜像拉取的策略,类型选择有个有三种Always, Never, IfNotPresent
ImagePullPolicy 的值默认是 Always,即每次创建 Pod 都重新拉取一次镜像。另外,当容器的镜像是类似于 nginx 或者 nginx:latest 这样的名字时,ImagePullPolicy 也会被认为 Always。
Never 或者 IfNotPresent,则意味着 Pod 永远不会主动拉取这个镜像,或者只在宿主机上不存在这个镜像时才拉取。

  • Lifecycle

Lifecycle定义的是Container Lifecycle Hooks。作用是在容器状态发生变化时触发一系列“钩子”,例如:postStart 和 preStop

postStart
在容器启动后,立刻执行一个指定的操作。需要明确的是,postStart 定义的操作,虽然是在 Docker 容器 ENTRYPOINT 执行之后,但它并不严格保证顺序。也就是说,在 postStart 启动时,ENTRYPOINT 有可能还没有结束。
preStop
事件在容器被杀死之前(比如,收到了 SIGKILL 信号)。而需要明确的是,preStop 操作的执行,是同步的。所以,它会阻塞当前的容器杀死流程,直到这个 Hook 定义操作完成之后,才允许容器被杀死,这跟 postStart 不一样。

4.3 Pod状态

Pod 生命周期的变化,主要体现在 Pod API 对象的 Status 部分,pod.status.phase就是 Pod 的当前状态.
它有如下几种可能的情况:

Pending
这个状态意味着,Pod 的 YAML 文件已经提交给了 Kubernetes,API 对象已经被创建并保存在 Etcd 当中。但是,这个 Pod 里有些容器因为某种原因而不能被顺利创建。比如,调度不成功。
Running
这个状态下,Pod 已经调度成功,跟一个具体的节点绑定。它包含的容器都已经创建成功,并且至少有一个正在运行中。
Succeeded
这个状态意味着,Pod 里的所有容器都正常运行完毕,并且已经退出了。一般发生在运行Job时。
Failed
这个状态下,Pod 里至少有一个容器以不正常的状态(非 0 的返回码)退出。我们需要查看 Pod 的 Events 和日志。
Unknown
这是一个异常状态,意味着 Pod 的状态不能持续地被 kubelet 汇报给 kube-apiserver,这很有可能是主从节点(Master 和 Kubelet)间的通信出现了问题。
Pod 对象的 Status 字段,还可以再细分出一组 Conditions。这些细分状态的值包括

  • PodScheduled
  • Ready
  • Initialized
  • Unschedulable

正常情况下,我们都是这样的running状态:

四. 总结

Pod,实际上是在扮演传统基础设施里“虚拟机”的角色。而容器,则是这个虚拟机里运行的用户程序。而且,Pod的设计完全脱离Docker这样的容器,对于Kubernetes这样的操作系统来说,达到普适性的目标非常有帮助。
Pod是一个最基本也是颗粒度最小的调度单位,学会这个是迈出Kubernetes的第一步。

顺便说一句,VS Code的对Kubernetes的YAMl编写提示还是非常友好的,对新手很合适。

Reference

https://alibaba-cloud.medium.com/getting-started-with-kubernetes-pods-and-container-design-modes-c76ade940ea0
https://time.geekbang.org/column/article/40366?utm_campaign=guanwang&utm_source=baidu-ad&utm_medium=ppzq-pc&utm_content=title&utm_term=baidu-ad-ppzq-title

转自:https://blog.wyatt.plus/archives/148