kubernetes学习笔记三( pod控制器、服务发现、网络 )

kubernetes学习笔记三( pod控制器、服务发现、网络 )

Scroll Down

一、Pod是什么?

    pod用于解决应用间的紧密交互问题,它只是一个逻辑概念,我们可以将pod和虚拟机做一个类比。每个虚拟机里边可以运行多个进程,每个pod里边也可以运行多个容器,容器的本质其实就是进程,多个容器之间可以通过localhost进行通信( 容器之间都是共享的pod的网络 ),容器间也可以共享数据卷。

Pod是Kubernetes创建或部署的最小/最简单的基本单位,一个Pod代表集群上正在运行的一个进程。

一个Pod封装一个应用容器(也可以有多个容器),存储资源、一个独立的网络IP以及管理控制容器运行方式的策略选项。Pod代表部署的一个单位:Kubernetes中单个应用的实例,它可能由单个容器或多个容器共享组成的资源。

Pods提供两种共享资源:网络和存储。

网络

每个Pod被分配一个独立的IP地址,Pod中的每个容器共享网络命名空间,包括IP地址和网络端口。Pod内的容器可以使用localhost相互通信。当Pod中的容器与Pod 外部通信时,他们必须协调如何使用共享网络资源(如端口)。

存储

Pod可以指定一组共享存储volumes。Pod中的所有容器都可以访问共享volumes,允许这些容器共享数据。volumes 还用于Pod中的数据持久化,以防其中一个容器需要重新启动而丢失数据。
image.png
Pod生命周期:

Pod 的 status 定义在 PodStatus 对象中,其中有一个 phase 字段。

下面是 phase 可能的值:

挂起(Pending):Pod 已被 Kubernetes 系统接受,但有一个或者多个容器镜像尚未创建。等待时间包括调度 Pod 的时间和通过网络下载镜像的时间,这可能需要花点时间。

运行中(Running):该 Pod 已经绑定到了一个节点上,Pod 中所有的容器都已被创建。至少有一个容器正在运行,或者正处于启动或重启状态。

成功(Succeeded):Pod 中的所有容器都被成功终止,并且不会再重启。

失败(Failed):Pod 中的所有容器都已终止了,并且至少有一个容器是因为失败终止。也就是说,容器以非0状态退出或者被系统终止。

未知(Unknown):因为某些原因无法取得 Pod 的状态,通常是因为与 Pod 所在主机通信失败。

    那么这一切是如何做到的呢?

    其实pod里边最先启动的并不是应用容器,而是一个辅助容器Infra( 即下图中的Infra Container )。Infra是pod里边最先启动的容器,它负责为pod创建网络和共享存储,而我们的应用容器只是连接到了Infra创建的网络和数据卷上,这样就达到了共享网络空间和数据卷的目的( 同一个pod中端口不能冲突,否则将会无法启动pod )

kubernetesPod.png

    有些配置并不会被打包到容器里,因为它们变动频繁,我们通常启动容器时将它们动态挂载到容器里,为了应付这种需求,就有了ConfigMap资源。有些配置信息中有敏感信息需要加密,为了应对这种需求,就有了Secret这种资源

创建ConfigMap的步骤如下:

  1. 首先我们编写这个ConfigMap,然后通过Kubctl客户端提交给Kubernetes集群,Kubernetes API Server收到请求后将配置存入etcd存储中
  2. 容器启动后,kublet发送请求给API Server,将ConfigMap挂载到容器中

ConfigMapSecret.png

那我们为什么要启动多个pod副本?

    答:当某一个挂掉了,其他仍然可以正常访问,而且当访问量过大时,也可以通过多启用几个Pod来达到分流的目的。

那我们要怎么启用多个Pod呢?

    那就是ReplicaSet(Replication Controller已过时)这种资源,ReplicaSet就是为了提供多个Pod副本而设计的。

ReplicaSet

image.png

    ReplicaSet( 副本控制器 )保证集群的资源处于期望状态( 也就是维护用户定义的副本数目 ),举个例子,想要运行几个副本,就是其进行控制的,一旦副本数目没有达到期望值,其就要负责将副本改写到期望值( 也就是如果有容器异常退出,会自动创建新的Pod来替代;而如果异常多出来的容器也会自动回收 )

    新版本Kubernetes中,建议使用ReplicaSet来取代ReclicationController:

    ReplicaSetReclicationController没有本质的不同,只是名字不一样,但是ReplicaSet支持集合式的selector

    集合式的selector意思就是:集群创建pod的时候,会为其添加许多的标签,当需要对pod进行操作(删除、修改等)的时候,rs(ReplicaSet)就支持通过其标签来操作的集合方案,而rc就不支持。所以在大型项目管理中,rs比rc更简单、更有效。

    selector:标签选择器。不加标签,就会在所有PV找最佳匹配

二、如何部署多个应用副本?

    当我们部署的应用有了新版本,我们想升级它但是又不想影响线上用户的访问,我们应该怎么做呢?滚动升级是一个比较好的方案。

deployment

那么kubernetes是如何实现这种滚动升级的呢?

    答:就是通过Deployment这种资源,实际上Deployment与ReplicaSet与Pod是一种层层控制的关系。(Deployment控制ReplicaSet,ReplicaSet控制pod) ReplicaSet负责通过控制器模式保证系统中的Pod的个数永远等于指定的个数。这也正是Deployment只允许容器的restart = Always的原因,只有在容器能保证自己始终是running状态的前提下,ReplicaSet调整Pod的个数才有意义。在这个基础上,Deployment同样通过控制器模式来操作ReplicaSet的个数和属性,从而实现水平扩展,收缩he滚动升级这两个动作。

    deployment被创建出来之后,它会去创建一个rs( 也就意味着这个rs不是用户定义的,而是deployment定义的,而rs就负责去创建pod。当需要deployment将镜像升级的时候,deployment会新创建一个rs )

三、如何对应用进行滚动升级?

    起初一个Deployment控制一个副本集,这个副本集的版本是唯一的版本。流程是:deployment会新建一个新版本v2的ReplicaSet,V2版本的ReplicaSet则会启动一个v2版本的pod,同时会退出一个v1版本的pod,新老版本的pod交替启动和停止,直到所有v1版本的pod都已更新为v2版本,此次滚动更新便完成了。

image.png

image.png

image.png

image.png

image.png

四、Pod控制器类型

① HPA(Horizontal Pod AutoScale )

    HPA就是平滑扩展,在早期的版本中仅支持根据cpu利用率所扩容,新版本中,支持根据内存和用户自定义的metric进行扩缩容

CPU Utilization Percentage(cpu利用百分率) 是一个计算平均值,即目标 Pod 所有副本的 CPU 利用率的平均值。一个 Pod 自身的 CPU 利用率是该Pod当前的 CPU 的使用量除以它的 Pod Request 的值,比如定义一个 Pod 的 Pod Request 为0.4,当前的CPU 使用量为 0.2,则它的 CPU 使用率为 50% 。如此一来,就可以算出来一个 RC 控制的所有 Pod 副本的 CPU 利用率的算术平均值了。如果某一时刻 cpu利用率 的值超过 80%,则意味着当前的 Pod 副本数很可能不足以支撑接来下更多的请求,需要进行动态扩容;而当前请求高峰时段过去后,Pod 的 CPU 利用率又会降下来,此时对应的 Pod 副本数应该自动减少到一个合理的水平。这就是根据cpu利用率自动进行扩缩容

  HPA基于rs定义,当rs的pod的cpu利用率大于80%的时候,进行扩展的最大值是10个pod,当cpu利用率降下来时,最小值是2个pod。

② StatefulSet

  StatefulSet是为了解决有状态服务的问题(对应Deploymenets和ReplicaSets是为无状态服务而设计),其应用场景包括:

    * 稳定的持久化存储,即Pod重新调度后还是能访问到相同的持久化数据,基于PVC来实现(当一个pod死亡之后被调度回来取代之前的老的pod时,用到的存储还是之前的存储,并且里面的数据也不会丢失 )

    * 稳定的网络标志,即Pod重新调度后其PodName和Hostname不变,基于Headless Service(即没有Cluster IP的Service)来实现(重新调度回来的pod的主机名不会变,不需要重新写入)

    * 有序部署,有序扩展,即Pod是有顺序的,在部署或者扩展的时候要根据定义的顺序依次进行(从0到N-1,当前一个Pod处于Running 和Ready 状态时,第二个pod才会被创建。原因是:当我们要创建一个集群时,比如LNMP,他们的启动顺序应该是MySQL -> PHP -> Nginx,因为有依赖关系,所以在大型环境中是由一定启动关系的 ,关闭时也需要有序。即为有序部署与有序回收 ),基于init containers来实现

    * 有序回收,有序删除

③ DaemonSet

  DaemonSet确保全部(或者一些)Node上运行一个指定Pod的副本。当有Node加入集群时,指定的Pod副本也将添加到该Node节点上。当有Node从集群移除时,其节点上的Pod也会被回收。删除某一个DaemonSet的话,将会删除它创建的所有Pod。

  (“一些” 是因为可以在Node上打一些污点,这些污点就是可以不被调度的,所以在DaemonSet创建的时候,这些打了污点的Node就不会运行这个Pod,但是默认情况下所有的Node都会运行一个Pod副本,有且只有一个

  当然:当你在一个Node上运行了好几个不同的Pod,这时候我们就可以把这几个Pod中的主要进程提取出来,放在一个单独的Pod中的不同容器上,这样也就可以通过DaemonSet来设置。或者也可以设置多个DaemonSet

使用DaemonSet的一些典型用法:

    * 运行集群存储daemon,例如在每个Node上运行glusterd、ceph

    * 在每个Node上运行日志收集daemon,例如fluented、logstash

    * 在每个Node上运行监控daemon,例如Prometheus Node Exporter

④ Job、Cronjob

  Job负责批处理任务,即仅执行一次的任务,它保证批处理任务的一个或多个Pod成功结束

  ( 使用Job与在linux直接执行脚本的区别在于:① job如果判断这个脚本不是正常退出,它就会重新执行一遍,直到正常退出为止,并且还可以设置正常退出的次数,比如正常退出3次,才允许此脚本执行成功 )

  Cron Job管理基于时间的Job,即:

( 总的来说就是可以在特定的时间执行 )

    * 在给定的时间点只运行一次

    * 周期性地再给定时间点运行

五、服务发现

① Service(SVC)概述

为什么需要Service?
k8s中的Pod是随时可以消亡的(节点故障、容器内应用程序错误等原因)。如果使用 Deployment 运行了应用程序,Deployment 将会在 Pod 消亡后再创建一个新的 Pod 以维持所需要的副本数。每一个 Pod 有自己的 IP 地址,然而,对于 Deployment 而言,对应的 Pod 集合是动态变化的。

动态变化就导致了如下结果:

如果某些 Pod(假设是 'backends')为另外一些 Pod(假设是 'frontends')提供接口,在 'backends' 中的 Pod 集合不断变化(IP 地址也跟着变化)的情况下,'frontends' 中的 Pod 如何才能知道应该将请求发送到哪个 IP 地址?
Service 存在的意义,就是为了解决这个问题。

Service 是 Kubernetes 中的一种服务发现机制:

• Pod 有自己的 IP 地址
• Service 被赋予一个唯一的 dns name
• Service 通过 label selector 选定一组 Pod
• Service 实现负载均衡,可将请求均衡分发到选定这一组 Pod 中

image.png

图中,Service 先连线到 Controller,Controller 在连线到容器组,这种表示方式只是概念上的,期望用户在使用 Kubernetes 的时候总是通过 Controller 创建 Pod,然后再通过 Service 暴露为网络服务,通过 Ingress 对集群外提供互联网访问。

事实上,Service 与 Controller 并没有直接联系,Service 通过 label selector 选择符合条件的 Pod,并将选中的 Pod 作为网络服务的提供者。从这个意义上来讲,可以有很多种方式去定义 Service 的 label selector,然而,最佳的实践是,在 Service 中使用与 Controller 中相同的 label selector。例如上图所示。

image.png

客户端需要访问一组Pod,如果这些Pod无相干性是不能够通过service进行代理的,这些Pod要具有相关性,比如通过同一个rc或者deployment创建的,或者拥有同一组标签。也就是说service是通过标签进行选择到这些Pod的。

选择到Pod之后,这个service就会有自己的IP+端口,client就通过访问service的IP+端口间接“轮询->RR算法”访问到Pod

六、网络通讯方式

  kubernetes的网络模型假定所有的Pod都在一个可以直接连通的扁平的网络空间,这在GCE( Google Compute Engine )里面是现成的网络模型,kubernetes假定这个网络已经存在。而在私有云里面搭建kubernetes集群,就不能假定这个网络已经存在了。我们需要自己实现这个网络假设,将不同节点上的docker容器之间的互相访问先打通,然后运行Kubernetes

下图中的“集群网络”、“节点网络”详见k8s学习笔记二中1.2.2 service的网络服务模式
qq_pic_merged_1574482425857.jpg

  在k8s中,扁平化的概念就是所有的pod都可以通过对方的IP( 这个IP是私网地址 ) “ 直接到达 ”( 带引号的意思是:pod认为自己是直接到达,其实底层还有一堆的转换机制存在 )

     1、同一个pod内的多个容器:lo

image.png

     2、不同pod之间的通讯:Overlay Network( 覆盖网络,通过Flannel来建立 )

      Flannel是CoreOS团队针对Kubernetes设计的一个网络规划服务,其功能是让集群中的不同节点主机所创建的Docker容器都具有全集群唯一的虚拟IP地址。而且他还能在这些IP地址之间建立一个覆盖网络(Overlay Network),通过这个覆盖网络,将数据包原封不动地传递到目标容器。

qq_pic_merged_1574478186792.jpg

qq_pic_merged_1574482406700.jpg

那么Flannel是如何解决同主机甚至跨主机的通讯问题的呢?

①  同主机间不同pod的通讯过程:

    首先,真实的Node服务器上会安装一个Flanneld的守护进程,Flanneld会监听一个端口,这个端口就是后期进行转发数据包以及接收数据包的一个服务端口,flanneld进程启动之后会开启一个网桥叫flannel 0,这个flannel 0专门接收docker 0 转发出来的数据报文。docker 0 本身会分配自己的IP到Pod上,如果是同一台主机上的不同Pod的话,它的数据报文走的其实是docker 0这个网桥,因为它们都是在同一个网桥下面的子IP。这很简单就能实现,难就难在跨主机之间的通信,还得通过对方的IP到达( 私网地址 )。

Screenshot_20191123113712.jpg

----------非常重要分割线----------

②  不同主机间的pod通讯过程( 对照上图进行分析 )

  我们以Web app2Backend进行数据通信来说明跨主机间的Pod通信的流程:

    首先web app2的数据包源地址要写自己的10.1.15.2/24,目的IP地址要写10.1.20.3/24,因为不是同一个网段,所以要将这个数据包发送到网关docker 0(10.1.15.1/24),flannel 0就通过特殊方法( 构置函数,我不太懂 )将数据包从docker 0抓取过来,然后在从etcd中获取到路由表,判断路由到哪台机器。 因为flannel 0 flanneld开启的一个网桥,当数据包到达flanneld的时候,它会对数据包进行封装。( 数据包结构如右上角所示,第三层outerIP封装的是这两个物理主机的源IP和目的IP,数据传输层则使用udp封装,原因是更快。在往上一层是InnerIP,这一层封装的就是对应Pod的源IP和目的IP,封装到这一层之后,里面封装了一个数据包实体 )然后就转发到目的物理主机192.168.66.12,根据三层outerIP地址所转发,这个数据包到达目的主机之后,因为端口肯定是flanneld的服务端口,所以报文肯定会被flanneld所截获,然后进行拆封,拆封之后就会送到flannel 0,随后flannel 0会将其转发到docker 0,然后机会被送到Pod Backend。这样就实现了跨主机的扁平化网络,其实这个过程消耗的资源还是比较高的!

----------非常重要分割线----------

Flannel与Etcd的调用关系:

     Etcd会存储flannel可分配的IP地址段资源( 也就意味着,当flannel启动之后向etcd插入其可以被分配的网段,并且要记录上哪个网段分配给了哪个pod,防止这个已分配的网段再次被flannel利用分配给其他的Node节点,)

    * 监控Etcd中每个Pod的实际地址,并在内存中建立维护Pod节点路由表( 也就是映射Pod的IP地址到物理主机的IP地址,这样在转发数据包时才知道要将去往其他物理主机上的某一个Pod的数据包转发去哪 )

     3、pod与Service之间的通讯:各节点的Iptables规则( 最新已启用lvs )

     4、Pod到外网:

      Pod向外网发送请求,查找路由表,转发数据包到宿主机的网卡,宿主网卡完成路由选择后,iptables执行Masquerade,把源IP更改为宿主网卡的IP,然后向外网服务器发送请求

     5、Service( 虽然是一个扁平型网络,但是其始终是一个私有网络 )

    

小结:

1、deploment并不负责pod的创建,它通过与ReplicaSet的机制来达到滚动升级,甚至还可以进行回滚操作,即当发现新更新的版本有问题之后,还可以通过rs回退到老版本( 原因是,当进行完滚动升级之后,老旧版本的rs并不会被删除,而是被停用,当需要回滚的时候,老旧版本的rs则会被启用 )

image.png

2、从kubernetes的最小单位Pod开始讲起,为了解决服务编排时两个服务的亲密关系,kubernetes引入了Pod这个概念,定义处在一个Pod内的容器共享网络空间、存储卷,并且一起被调度。

3、我们解决了服务间的亲密关系以后,需要为服务运行多个副本同时对外提供服务,于是引入了ReplicaSet这个控制器。

    为了解决服务滚动升级的问题,又引入了Deployment这个控制器来控制ReplicaSet控制器,通过Deployment这个控制器就能轻松的将我们的应用部署到kubernetes集群上并且对其进行滚动升级。

image.png

4、pod控制器

5、pod的五种通讯方式:( 非常重要 )

学习笔记部分内容来源:
1、尚硅谷汪洋老师学习视频
2、https://www.cnblogs.com/xinfang520/p/11819924.html
3、https://kuboard.cn/learning/