k8s作为现在最火的容器编排调度平台,好用我也就不必多说了。当我们初识k8s的时候一个新的概念就到了我们眼前,那就是pod。我们在使用了之后也就渐渐的接受了pod这个东西,但是你有没有想过,为什么是pod?k8s为什么会有这样的设计?今天我们就来细细说说这个pod

架构图

首先我们来回忆看看k8s的架构图是什么样子的

这个是来源于 https://www.kubernetes.org.cn/ 中文官网的一个架构图

从架构图中我们可以看到,整个k8s的设计架构有以下几个要点:

  • master、node架构;master就是大脑充当着管理者的角色
  • master中提供了api-server、controller-manager、scheduler、etcd用于管理node并向外提供服务
  • node就是实际干活的,提供了kubelet、proxy用于向master汇报情况,并管理pod的网络
  • 而pod是k8s中最小的调度单位,Pod就是最小的,管理,创建,计划的最小单元.

当然其他组件都非常重要,这个我们以后再说,我们今天就来看看主角“pod”

为什么是pod?

一开始用的时候我就好奇为什么k8s要弄出一个pod,因为我们一开始使用的是docker,操作的是docker容器,构建的也是docker镜像,为什么不直接调度docker容器就好了,这样粒度不是更加细致,调度也会更加方便吗?
我们在使用k8s之前也使用过docker-compose,从另一个角度说,这也是一种容器的管理,看起来也挺好的。
下面我们就来说说pod

pod扮演的是什么角色

  1. 从一开始我们的服务往往是运行在服务器上的,一个应用就占用了一个服务器,但是一个大的服务器上往往不会只有一个应用。
  2. 后来技术的发展出现了虚拟化,将一个大的服务器,虚拟化成几个小的服务器,来使用,从
  3. 节省资源也做到了服务与服务之间的隔离,从而避免一个服务的问题导致整个服务器宕机。
    后来出现了docker,于是我们在一个服务器上面运行多个容器。
  4. k8s的pod…

从上面的图你大概可以感受到pod在k8s中其实是一个什么样的角色。
我们如果使用虚拟机,那么上面就会有一系列的服务这些服务可能会有一些依赖,而这样的依赖就好像在服务器中运行的一个个进程组,往往其中也有着相关的依赖,而pod中的容器也是一样的道理,其中也会有类似这样的依赖,为了更好的描述和管理这样的依赖,于是就有了pod。
其实这样的理念往往可以类比出很多这样的设计。

Pod中容器的关系

一定会有这样的关系吗?我的感觉是,在现代技术服务的开发的过程中,这样的关系是不可避免的。我下面来举几个例子。

运行时容器和项目包

我们知道 java 的 web 应用往往需要部署在tomcat这样的容器之中,在 springboot 还没有出现以前,需要自己启动一个 tomcat 容器,然后将需要部署的应用打包部署到容器中去。

在以前你可能会想着,将 javaweb 打包一个war,然后编写一个 dockerfile 将 war 包cp到 tomcat 中的 webapp 目录中。但是这样带来的就是每次更新发布的时候,镜像会很大,因为每次构建都会有一个基础的tomcat镜像。

而在k8s使用的时候,会有的设计的是,将tomcat作为一个不变的镜像(它也不应该改变)而把 war 包作为另外一个容器,而这样个容器同时挂载同一个目录,将 webapp 挂载的目录与 war包挂载的目录相同来达到目的,然后将他们放到同一个 pod 中。

类似的操作还有: https://kubernetes.io/zh/docs/tasks/access-application-cluster/communicate-containers-same-pod-shared-volume/ 里面说的就是前端静态页面和 nginx 的关系。

下面引用官网设计理念中的一句话:“比如你运行一个操作系统发行版的软件仓库,一个Nginx容器用来发布软件,另一个容器专门用来从源仓库做同步,这两个容器的镜像不太可能是一个团队开发的,但是他们一块儿工作才能提供一个微服务;这种情况下,不同的团队各自开发构建自己的容器镜像,在部署的时候组合成一个微服务对外提供服务。

日志架构

在使用 docker 部署项目的时候会遇到一个问题就是日志持久化的问题,因为 docker 容器如果被删除的话,其中的文件也会被删除,那么我们的日志文件同时也会被删除,也就是说我们必须要将日志持久化。

最常见的方式是,将日志存储的目录挂载到宿主机上,这样容器被删除的时候日志不会被删除。
而在k8s中常见的日志处理架构是怎么样的呢?

https://kubernetes.io/zh/docs/concepts/cluster-administration/logging/

使用的是 sidecar,这个是什么呢?其实就是一个辅助性质的容器,同时与主容器放在同一个 pod 中,读取主容器挂载出来的日志目录。其实后续还可以做更多的操作,比如日志发送es等等。

总之

总之我们可以看到,在一个pod中的容器关系是非常密切的,他们可以拥有同一个目录,甚至可以拥有同一个网络,可以拥有相互的服务,这样的关系我听过的名词叫做“超亲密关系”。就类似一对夫妻之间的关系了。

因为在现在的多说应用中,已经几乎做不到一个人顶天立地了,总是会有各种各样的依赖,依赖一些组件,依赖一些工具,依赖一些网络服务等等,一个进程组有很多的进程互相帮助来最终实现功能一样。

这样的关系太过常见,于是k8s就将它设计为了pod。

pod的实现原理

如果你已经对docker的实现比较熟悉,其实pod的实现并不复杂。(如果对docker实现不熟悉可以翻看之前的博客)
其实pod是一个逻辑上的概念,其实pod做的事情很简单:

  • pod中的所有容器共享一个Network Namespace
  • pod中的同期可以声明互相共享Volume

如何实现的

-w587
其实k8s做的就是初始化一个infra的容器(这是一个很小的容器),利用这个容器去抢先占用需要使用的 Namespace ,然后在将用户指定的容器加载进来,同时使用的就是相同的 Namespace 了。(如果有 InitContainer 会优先按顺序初始化它,图上就不做说明了)

这样共享网络应该是没有问题了,那么要共享Volume也很简单。pod 只需要将 Volume 目录挂载到宿主机,让内部的容器挂载这个目录就可以了。

还有哪些共享

  • PID 命名空间(同一个Pod中应用可以看到其它进程)
  • 网络 命名空间(同一个Pod的中的应用对相同的IP地址和端口有权限)
  • IPC 命名空间(同一个Pod中的应用可以通过VPC或者POSIX进行通信)
  • UTS 命名空间(同一个Pod中的应用共享一个主机名称)

pod还有还有哪些功能

再来说说pod还有哪些功能,这些功能也是k8s为什么设计pod的原因之一

健康检查

通过Probe:LivenessProbe或者ReadinessProbe,可以探测应用是否处于健康状态,如果不健康做出相关的反应。
这就好比k8s可以定期的帮你监控、维持一整个应用的健康。
其实在我们看来,很多时候服务挂了,需要重启,需要做高可用,那么nginx呢?tomcat呢?也是一样的。所以pod的健康能保证整个服务的全部健康使用。

限制网络带宽

我们可以通过给Pod增加kubernetes.io/ingress-bandwidth和kubernetes.io/egress-bandwidth这两个annotation来限制Pod的网络带宽。
为什么我提到了这个功能呢?因为在实际的业务开发过程中经常会使用一些网络插件,这些网络插件在流量的控制上非常有用,有的时候我们会根据网络流量来做一系列的操作,用户的突然增长导致的流量剧增是否要扩容等等…而这样的监控和限制对于pod来说无疑会更加方便,而不需要管pod内部的容器的流量。

RestartPoliy

重启的策略,这个也算是一个功能吧

  • Always:只要退出就重启
  • OnFailure:失败退出(exit code不等于0)时重启
  • Never:只要退出就不再重启
    说明 k8s 管理 pod 的生命周期更加容易,还有调度等等这里就不再展开了。

总结

官网:一个Pod(就像一群鲸鱼,或者一个豌豆夹)。

我觉得 Pod 更证明了一种设计模式“组合”,在有的时候会,组合的合理,就会方便很多东西,比如设计了一堆组件,组合在一起;ps画图的时候方块更圆组合在一起,就可以一起多复制几个组合。

当然我们在认识到为什么要这样设计 Pod 的同时需要意识到,我们应该将什么样的容器组合放置在同一个 Pod 之中才比较合适。遵循一定的“容器设计模式”进行编排,调度的时候才会更加得心应手。