13. 为什么我们会需要 Pod?

13.1 docker容器的本质

"""
docker容器的本质  是进程.
主要通过
Namespace 做隔离,Cgroups 做限制,rootfs 做文件系统
"""

疑问: 既然有容器为什么 Kubernetes 项目又突然搞出一个 Pod 来呢?

要回答这个问题,我们还是要一起回忆一下我曾经反复强调的一个问题:容器的本质到底是什么?

你现在应该可以不假思索地回答出来:容器的本质是进程。

没错。容器,就是未来云计算系统中的进程;容器镜像就是这个系统里的“.exe”安装包。那么 Kubernetes 呢?

你应该也能立刻回答上来:Kubernetes 就是操作系统!

非常正确。

13.2 进程组

现在,就让我们登录到一台 Linux 机器里,执行一条如下所示的命令

pstree -gsystemd(1)-+-accounts-daemon(1984)-+-{gdbus}(1984)| `-{gmain}(1984)|-acpid(2044)...      |-lxcfs(1936)-+-{lxcfs}(1936)| `-{lxcfs}(1936)|-mdadm(2135)|-ntpd(2358)|-polkitd(2128)-+-{gdbus}(2128)| `-{gmain}(2128)|-rsyslogd(1632)-+-{in:imklog}(1632)|  |-{in:imuxsock) S 1(1632)| `-{rs:main Q:Reg}(1632)|-snapd(1942)-+-{snapd}(1942)|  |-{snapd}(1942)|  |-{snapd}(1942)|  |-{snapd}(1942)|  |-{snapd}(1942)# 小插曲: 当只知道一个命令,如果查询这个命令属于哪个rpm包中?
1. 命令存在时,使用rpm -qf `which 命令`
[root@node02 tools]# rpm -qf `which yum`
yum-3.4.3-161.el7.centos.noarch2. 命令不存在时,使用yum whatprovides 命令
[root@node02 tools]# yum whatprovides pstree
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile* base: mirrors.aliyun.com* extras: mirrors.aliyun.com* updates: mirrors.aliyun.com
docker-ce-stable/x86_64/filelists_db                             |  16 kB  00:00:00
kubernetes/filelists                                             |  18 kB  00:00:00
psmisc-22.20-15.el7.x86_64 : Utilities for managing processes on your system
Repo        : base
Matched from:
Filename    : /usr/bin/pstree

不难发现,在一个真正的操作系统里,进程并不是“孤苦伶仃”地独自运行的,而是以进程组的方式,“有原则的”组织在一起。在这个进程的树状图中,每一个进程后面括号里的数字,就是它的进程组 ID(Process Group ID, PGID

对于操作系统来说,这样的进程组更方便管理。举个例子,Linux 操作系统只需要将信号,比如,SIGKILL 信号,发送给一个进程组,那么该进程组中的所有进程就都会收到这个信号而终止运行。

Kubernetes 项目所做的,其实就是将“进程组”的概念映射到了容器技术中,并使其成为了这个云计算“操作系统”里的“一等公民”。

13.3 容器设计模式

Pod 在 Kubernetes 项目里还有更重要的意义,那就是:容器设计模式

为了理解这一层含义,我就必须先给你介绍一下Pod 的实现原理。

首先,关于 Pod 最重要的一个事实是:它只是一个逻辑概念。

也就是说,Kubernetes 真正处理的,还是宿主机操作系统上 Linux 容器的 Namespace 和 Cgroups,而并不存在一个所谓的 Pod 的边界或者隔离环境。

那么,Pod 又是怎么被“创建”出来的呢?

答案是:Pod,其实是一组共享了某些资源的容器

具体的说:Pod 里的所有容器,共享的是同一个 Network Namespace,并且可以声明共享同一个 Volume。

那这么来看的话,一个有 A、B 两个容器的 Pod,不就是等同于一个容器(容器 A)共享另外一个容器(容器 B)的网络和 Volume 的玩儿法么?

这好像通过 docker run --net --volumes-from 这样的命令就能实现嘛,比如:

$ docker run --net=B --volumes-from=B --name=A image-A ...

但是,你有没有考虑过,如果真这样做的话,容器 B 就必须比容器 A 先启动,这样一个 Pod 里的多个容器就不是对等关系,而是拓扑关系了。

所以,在 Kubernetes 项目里,Pod 的实现需要使用一个中间容器,这个容器叫作 Infra 容器。在这个 Pod 中,Infra 容器永远都是第一个被创建的容器,而其他用户定义的容器,则通过 Join Network Namespace 的方式,与 Infra 容器关联在一起。这样的组织关系,可以用下面这样一个示意图来表达:

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

而在 Infra 容器“Hold 住”Network Namespace 后,用户容器就可以加入到 Infra 容器的 Network Namespace 当中了。所以,如果你查看这些容器在宿主机上的 Namespace 文件(这个 Namespace 文件的路径,我已经在前面的内容中介绍过),它们指向的值一定是完全一样的。

这也就意味着,对于 Pod 里的容器 A 和容器 B 来说:

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

而对于同一个 Pod 里面的所有用户容器来说,它们的进出流量,也可以认为都是通过 Infra 容器完成的。这一点很重要,因为将来如果你要为 Kubernetes 开发一个网络插件时,应该重点考虑的是如何配置这个 Pod 的 Network Namespace,而不是每一个用户容器如何使用你的网络配置,这是没有意义的。

这就意味着,如果你的网络插件需要在容器里安装某些包或者配置才能完成的话,是不可取的:Infra 容器镜像的 rootfs 里几乎什么都没有,没有你随意发挥的空间。当然,这同时也意味着你的网络插件完全不必关心用户容器的启动与否,而只需要关注如何配置 Pod,也就是 Infra 容器的 Network Namespace 即可。

有了这个设计之后,共享 Volume 就简单多了:Kubernetes 项目只要把所有 Volume 的定义都设计在 Pod 层级即可。

这样,一个 Volume 对应的宿主机目录对于 Pod 来说就只有一个,Pod 里的容器只要声明挂载这个 Volume,就一定可以共享这个 Volume 对应的宿主机目录。比如下面这个例子:

apiVersion: v1
kind: Pod
metadata:name: two-containers
spec:restartPolicy: Nevervolumes:- name: shared-datahostPath:      path: /datacontainers:- name: nginx-containerimage: nginxvolumeMounts:- name: shared-datamountPath: /usr/share/nginx/html- name: debian-containerimage: debianvolumeMounts:- name: shared-datamountPath: /pod-datacommand: ["/bin/sh"]args: ["-c", "echo Hello from the debian container > /pod-data/index.html"]

在这个例子中,debian-container 和 nginx-container 都声明挂载了 shared-data 这个 Volume。而 shared-data 是 hostPath 类型。所以,它对应在宿主机上的目录就是:/data。而这个目录,其实就被同时绑定挂载进了上述两个容器当中。

这就是为什么,nginx-container 可以从它的 /usr/share/nginx/html 目录中,读取到 debian-container 生成的 index.html 文件的原因。

明白了 Pod 的实现原理后,我们再来讨论“容器设计模式”,就容易多了。

Pod 这种“超亲密关系”容器的设计思想,实际上就是希望,当用户想在一个容器里跑多个功能并不相关的应用时,应该优先考虑它们是不是更应该被描述成一个 Pod 里的多个容器。

为了能够掌握这种思考方式,你就应该尽量尝试使用它来描述一些用单个容器难以解决的问题。

第一个最典型的例子是:WAR 包与 Web 服务器。

我们现在有一个 Java Web 应用的 WAR 包,它需要被放在 Tomcat 的 webapps 目录下运行起来。

假如,你现在只能用 Docker 来做这件事情,那该如何处理这个组合关系呢?

  • 一种方法是,把 WAR 包直接放在 Tomcat 镜像的 webapps 目录下,做成一个新的镜像运行起来。可是,这时候,如果你要更新 WAR 包的内容,或者要升级 Tomcat 镜像,就要重新制作一个新的发布镜像,非常麻烦。
  • 另一种方法是,你压根儿不管 WAR 包,永远只发布一个 Tomcat 容器。不过,这个容器的 webapps 目录,就必须声明一个 hostPath 类型的 Volume,从而把宿主机上的 WAR 包挂载进 Tomcat 容器当中运行起来。不过,这样你就必须要解决一个问题,即:如何让每一台宿主机,都预先准备好这个存储有 WAR 包的目录呢?这样来看,你只能独立维护一套分布式存储系统了。

实际上,有了 Pod 之后,这样的问题就很容易解决了。我们可以把 WAR 包和 Tomcat 分别做成镜像,然后把它们作为一个 Pod 里的两个容器“组合”在一起。这个 Pod 的配置文件如下所示:

apiVersion: v1
kind: Pod
metadata:name: javaweb-2
spec:initContainers:- image: geektime/sample:v2name: warcommand: ["cp", "/sample.war", "/app"]volumeMounts:- mountPath: /appname: app-volumecontainers:- image: geektime/tomcat:7.0name: tomcatcommand: ["sh","-c","/root/apache-tomcat-7.0.42-v2/bin/start.sh"]volumeMounts:- mountPath: /root/apache-tomcat-7.0.42-v2/webappsname: app-volumeports:- containerPort: 8080hostPort: 8001 volumes:- name: app-volumeemptyDir: {}

在这个 Pod 中,我们定义了两个容器,第一个容器使用的镜像是 geektime/sample:v2,这个镜像里只有一个 WAR 包(sample.war)放在根目录下。而第二个容器则使用的是一个标准的 Tomcat 镜像。

不过,你可能已经注意到,WAR 包容器的类型不再是一个普通容器,而是一个 Init Container 类型的容器。

在 Pod 中,所有 Init Container 定义的容器,都会比 spec.containers 定义的用户容器先启动。并且,Init Container 容器会按顺序逐一启动,而直到它们都启动并且退出了,用户容器才会启动。

所以,这个 Init Container 类型的 WAR 包容器启动后,我执行了一句 "cp /sample.war /app",把应用的 WAR 包拷贝到 /app 目录下,然后退出。

而后这个 /app 目录,就挂载了一个名叫 app-volume 的 Volume。

接下来就很关键了。Tomcat 容器,同样声明了挂载 app-volume 到自己的 webapps 目录下。

所以,等 Tomcat 容器启动时,它的 webapps 目录下就一定会存在 sample.war 文件:这个文件正是 WAR 包容器启动时拷贝到这个 Volume 里面的,而这个 Volume 是被这两个容器共享的。

像这样,我们就用一种“组合”方式,解决了 WAR 包与 Tomcat 容器之间耦合关系的问题。

实际上,这个所谓的“组合”操作,正是容器设计模式里最常用的一种模式,它的名字叫:sidecar。

顾名思义,sidecar 指的就是我们可以在一个 Pod 中,启动一个辅助容器,来完成一些独立于主进程(主容器)之外的工作。

比如,在我们的这个应用 Pod 中,Tomcat 容器是我们要使用的主容器,而 WAR 包容器的存在,只是为了给它提供一个 WAR 包而已。所以,我们用 Init Container 的方式优先运行 WAR 包容器,扮演了一个 sidecar 的角色。

第二个例子,则是容器的日志收集。

比如,我现在有一个应用,需要不断地把日志文件输出到容器的 /var/log 目录中。

这时,我就可以把一个 Pod 里的 Volume 挂载到应用容器的 /var/log 目录上。

然后,我在这个 Pod 里同时运行一个 sidecar 容器,它也声明挂载同一个 Volume 到自己的 /var/log 目录上。

这样,接下来 sidecar 容器就只需要做一件事儿,那就是不断地从自己的 /var/log 目录里读取日志文件,转发到 MongoDB 或者 Elasticsearch 中存储起来。这样,一个最基本的日志收集工作就完成了。

跟第一个例子一样,这个例子中的 sidecar 的主要工作也是使用共享的 Volume 来完成对文件的操作。

但不要忘记,Pod 的另一个重要特性是,它的所有容器都共享同一个 Network Namespace。这就使得很多与 Pod 网络相关的配置和管理,也都可以交给 sidecar 完成,而完全无须干涉用户容器。这里最典型的例子莫过于 Istio 这个微服务治理项目了。

Istio 项目使用 sidecar 容器完成微服务治理的原理,我在后面很快会讲解到。

备注:Kubernetes 社区曾经把“容器设计模式”这个理论,整理成了一篇小论文,你可以点击链接浏览。

转载于:https://www.cnblogs.com/plf-Jack/p/11300009.html

13. 为什么我们会需要 Pod?相关推荐

  1. Kubernetes(k8s)常用资源的使用、Pod的常用操作

    1.K8s是如何运行容器的. 答:k8s是通过定义一个Pod的资源,然后在Pod里面运行容器的.K8s最小的资源单位Pod. 2.如何创建一个Pod资源呢? 答:在K8s中,所有的资源单位都可以使用一 ...

  2. k8s pod 详解

    https://www.cnblogs.com/kevingrace/p/11309409.html 一.什么是Pod kubernetes中的一切都可以理解为是一种资源对象,pod,rc,servi ...

  3. (0044) iOS 开发之SDWebImage 深度学习其源码和原理

    闲着没事看了SDWebImage的源码.清晰了它的原理. SDWebImage 深度学习 1.它是iOS图片加载框架 它支持从网络中下载且缓存图片,并设置图片到对应的UIImageView控件或者UI ...

  4. Kubernetes Events介绍(下)

    https://www.kubernetes.org.cn/1195.html 经过前两回的"踏血寻妖",一个完整的Events原形逐渐浮出水面.我们已经摸清了它的由来和身世,本回 ...

  5. Bupt桌游馆--共享资源清单

    线下 [诡影寻踪]2-5人(最佳4人) 标签:| 轻策 | 推理 | 持有者:chenbiteng [赌命大赛]2-6人(最佳5人) 标签:| 毛线 | 嘴炮 | 哄骗 | 运气 | 计分 | 持有者 ...

  6. k8s学习笔记——ceph pv rbd动态挂载

    //参考https://github.com/kubernetes-retired/external-storage/tree/master/ceph/rbd //参考https://www.wenj ...

  7. k8s介绍及与docker搭建集群

    一.Kubernetes系列之介绍篇 •Kubernetes介绍 1.背景介绍 云计算飞速发展 - IaaS - PaaS - SaaS Docker技术突飞猛进 - 一次构建,到处运行 - 容器的快 ...

  8. 【kubernetes/k8s源码分析】 kubelet cgroup 资源预留源码分析

    kubernetes 1.13 WHY 默认情况下 pod 能够使用节点全部可用资源.用户 pod 中的应用疯狂占用内存,pod 将与 node 上的系统守护进程和 kubernetes 组件争夺资源 ...

  9. 还在手动部署 Kubernetes 集群吗,是时候使用 Kubespray 完成自动化部署了!

    公众号关注 「奇妙的 Linux 世界」 设为「星标」,每天带你玩转 Linux ! 前言 部署 Kubernetes 除了手动方式外,还有诸如 Kubeadm.Kubespray.Breeze.Ra ...

最新文章

  1. php编程题试题和答案,比较基础的php面试题及答案-编程题部分
  2. 将cocos2dx项目从Visual Studio 迁移到 xcode
  3. jspxcms bug表
  4. Jzoj3625 旅行(travel)
  5. Linux inotify功能及实现原理【转】
  6. Matlab:图像数据保存
  7. oracle 给表空间改名,Oracle重命名表空间和删除表空间
  8. pyltp在ubuntu20.04下面的安装办法
  9. Linux shell重复执行某命令n次
  10. 网络爬虫--8.编码趣闻
  11. 给你的网页添加随机BGM背景音乐
  12. notifyDataSetInvalidated()和notifyDataSetChanged()有什么区别? (转载)
  13. java 财付通支付_工商变更:马化腾卸任财付通支付科技有限公司法定代表人
  14. JMeter中使用“用户自定义变量”实现参数化
  15. [转]使用Python MrJob的MapReduce实现电影推荐系统
  16. 400多个开源项目以及43个优秀的Swift开源项目-Swift编程语言资料大合集
  17. 几种损失函数比较--代价函数,损失函数,目标函数区别
  18. U盘量产U盘扩容和U盘芯片检测
  19. 无源贴片晶振四角引脚_四脚贴片晶振有方向之分吗
  20. 个体工商户营业执照在网上如何年检?

热门文章

  1. linux so文件支持系统,让linux支持xfs jfs reiserfs 文件系统
  2. java 正则高级应用_JAVA正则表白式高级用法(分组与捉拿).
  3. php fpm子进程数配置,php-fpm进程管理方式以及子进程数量配置原则详解
  4. redis 缓存数据格式
  5. postgres 密码更改
  6. clang 反汇编
  7. 2.3 快速搭建你的第一个系统,并进行迭代
  8. scrapy.spider
  9. Pandas Apply函数
  10. c语言程序设计行列式,新手作品:行列式计算C语言版