Kubernetes 介绍
-
容器:以docker为代表的容器运行技术。
-
服务网格: 比如Service Mesh等。
-
微服务:在微服务体系结构中,一个项目是由多个松耦合且可独立部署的较小组件或服务组成。
-
不可变基础设施:不可变基础设施可以理解为一个应用运行所需要的基本运行需求,不可变最基本的就是指运行服务的服务器在完成部署后,就不在进行更改,比如镜像等。
-
声明式 API:描述应用程序的运行状态,并且由系统来决定如何来创建这个环境,例如声明一个 pod,会有 k8s 执行创建并维持副本。
云原生特征:
-
符合 12 因素应用,12要素应用程序是一种构建应用程序的方法.
-
面向微服务架构
-
自服务敏捷架构
-
基于 API 的协作
-
抗脆弱性
12 因素应用:
1、基准代码:一份基准代码,多份部署(用同一个代码库进行版本控制,并可进行多次部署)。
2、依赖:显式地声明和隔离相互之间的依赖。
3、配置:在环境中存储配置。
4、后端服务:把后端服务当作一种附加资源。
5、构建,发布,运行:对程序执行构建或打包,并严格分离构建和运行。
6、进程:以一个或多个无状态进程运行应用。
7、端口绑定:通过端口绑定提供服务。
8、并发:通过进程模型进行扩展。
9、易处理:快速地启动,优雅地终止,最大程度上保持健壮性。
10、开发环境与线上环境等价:尽可能的保持开发,预发布,线上环境相同。
11、日志:将所有运行中进程和后端服务的输出流按照时间顺序统一收集存储和展示。
12、管理进程:一次性管理进程(数据备份等)应该和正常的常驻进程使用同样的运行环境。
云原生项目分类:
1.1 borg系统原理说明(K8s前生)
BorgMaster:可以理解为这个borg系统的大脑,就是专门负责请求分发的,真正的工作节点其实是Borglet,也就是有对应的容器要运行的话就是通过Borglet来提供的,所以这里为了防止我的Borg master单节点故障所以这里就有很多副本,并且这个副本数是有讲究的,并且这里是有5个Borg master副本的,之所以是5个Borg master是为了满足高可用集群的1、3、5、7、9,之所以这样选择是为了防止出现主节点选票机制,所以对于我们的高可用来说他的调度器最好保持在三个以上的奇数
并且我们可以通过web浏览器来进行访问、命令行、以及我的一些文件的读取,而且在我们的 kubernetes 中也会有这几种方法是进行所谓的整个调度集群的管理。
这三种方式会被接入到我们的 Borg master,然后由 Borg master 理解请求以后去做分发,所谓的分发就是把接收到的请求交给谁来进行处理。
Scheduler:调度器,所有的任务到了scheduler之后会被分发至不同的节点(borglet)去运行,当然这里并不是我们的 scheduler 调度器去和我们的 borglet 进行交互。而是 scheduler 把数据写入至 paxos(谷歌的键值对数据库)进行存储,并且 borglet 会实时的在 paxos 数据库里进行监听,如果发现有 borglet 自己的请求了,borglet 会将自己的请求取出来进行消费处理这个任务达到这么一个流程目的。
以上就是整个Borg系统的原理
1.2 K8S架构图
Master 节点:
K8S 使用者通过命令行或者是通过 paas 平台对 API server 进行交互的
如果使用者是查看操作的话,API server 就会到 ETCD 数据库中进行查看数据
如果我们的操作是需要创建容器调度,然后 API server 就会去找 scheduler 来进行调度
当我们创建好容器之后,需要启动多少个容器的副本数,这个时候就会找到 Controlers 来进行维护,这四个组件就会存在于我们的 master 节点上
Node 节点:
然后每个 node 节点上会启两个组件,Kubelet 和 Kube-proxy
Kubelet :
接收 master 下发的指令,并在本机上管理容器的生命周期,包括创建和删除容器、初始化容器、探针检测容器,然后每个 pod 中有至少一个容器或者多个容器,然后创建 pod 的时候 Kubelet 调用 Runc ,runc 可以分为 docker runc 或者说是其他的容器 runc
Kube-proxy:
用于维护当前 node 节点上的网络规则,包括 iptables 或者 ipvs ,维护这些网络规则是,负责将规则写入至 iptables 、或者是 ipvs 实现服务映射,访问至 pod
1.2.1 master 节点组件说明
scheduler组件:
在master服务器中有 scheduler 组件是一个调度器,任务过来以后我们仍然需要scheduler把这些任务分散至不同的node里,但这里我们的 scheduler 将任务交给了 api server ,api server 在负责把这个请求写入到 etcd 也就意味着在 K8S 中我们的scheduler并不会与我们的etcd直接交互,
Replication控制器(RC):
就是负责维护我们的副本数目比如我们想让这个容器运行几个副本就是由 replication 来负责控制,一旦 replication 的副本数不满足我们的期望值了他就会自动将副本数改写到我们所期望的pod数量\也就是对应的删除或创建我们的 pod。
api server:
-
身份认证
-
验证权限
-
验证指令
-
执行操作
-
返回结果
https://kubernetes.io/zh/docs/reference/command-line-tools-reference/kube-apiserver/
Kubernetes API server 提供了k8s各类资源对象的增删改查及watch等HTTP Rest接口,这些对象包括pods、services、replicationcontrollers等,API Server为REST操作提供服务,并为集群的共享状态提供前端,所有其他组件都通过该前端进行交互。
是我们一切服务的访问入口,我们会发现 scheduler 和 replicateion 都需要和我们的 api server 进行交互。Kubectl 我是我们的命令行管理工具也需要和我们的 api server 进行交互。Web UI 也需要和我们的 api server 进行交互。Etcd 也需要和我们的 api server 进行交互。所以我们会发现在 K8S 中 api server 是非常繁忙的。所以为了减轻 api server 的压力,我们每个组件还可以在本地生成一定量的缓存并不需要每件事情都用到api server去进行申请。当我们的 api server 接收到请求以后呢会去操作etcd(键值对数据库),etcd在我们的K8S集群中呢就比较类似于在我们的 Borg 中的 paxos 这么一个键值对的数据库。Etcd 在整个 K8S 集群中起到了一个持久化的方案,并且 etcd 的官方将它定位成一个可信赖的分布式键值存储服务,Etcd 天生支持集群化,并不需要其他的组件参与进来就能够实现自己的集群化方案,所谓分布式键值存储服务也就是扩容非常方便。它能够为整个分布式集群存储一些关键数据,协助分布式集群的正常运转。
Etcd数据库:
一个可信赖的分布式键值存储服务、它能够为整个分布式集群存储一些关键数据,协助分布集群正常运转
K8S集群中ETCD内部架构图
Etcd的架构中会发现它采用的是 http server 的形式,也就意味着这里依然是采用 http 协议进行 C/S 的构建服务,除此以为 K8S 也是采用我们的 http 协议进行我们的 C/S 结构的开发,因为我们的 http 协议天生支持很多种操作方式。
Raft:
存放一些读写的信息,并且为了防止这些信息出现损坏还有一个 WAL ,所谓的 WAL 是一个预写日志,也就是说如果我们想对这里面的数据进行更改,WAL 就得先生产一个日志先存放一下,并且会对这些日志进行一个定时的完整加临时的备份。比如我先备份一个大版本,备份完成之后我还有一些新的修改,就会有多个子版本,然后最后在进行一次完整备份,将他转换为一个大版本。这样的好处是,首先我们不可能随时进行完整备份,因为消耗的数据量太大,那为什么还要在一定的时间内还要备份呢,防止这里的增量备份太多,我们在还原的时候太费事费时,所以采用了这么一种方案
并且还会实时的将这些数据和日志写入到我们的 store(本地磁盘存 储)进行一个持久化的设置,以上就是 etcd 整个架构的说明。
Etcd 我们可以直接理解为整个 K8S 集群中一个重要的存储,存储我们 K8S 集群中的一些参数信息,等于说离开了ETCD 整个 K8S 就瘫痪了。所以 etcd 是一个必须组件。
1.2.2 Node节点原理
在node节点中会有kube proxy、kubelet、container,也就意味着在我们的 node 节点需要安装三个软件**kube proxy、kubelet、以及 docker**(当然我们在node节点可以选者docker也可以选择一些其他的容器引擎)
Kubelet组件:
会和我们的 C R I(容器运行环境接口)这里就是我们的 docker 进行交互,kubelet 会操作 docker 来进行创建对应的容器。也就是说 kubelet 会维持我们的 pod 生命周期。
Kube proxy:
之前我们说过我们的 SVC 就是完成负载的操作,负载的组件就是通过我们的 kube proxy 去完成的。也就意味着怎么去实现我们的 pod 与 pod 之间的访问包括负载均衡就需要借助到我们的 kube proxy ,kube proxy的默认操作对象是操作我们的防火墙去实现这里的pod的映射。当然在 K8S 新版本中还支持我们的 IPVS 也就是我们 LVS组件。
以上就是我们K8S架构的一个标准的组件。
1.3 各个组件的意义
1.3.1 Master节点的组件介绍:
API server:
所有服务访问的统一入口,这里包括我们的 scheduler、RC、etcd、kubelet 以及 kube proxy 等等所有组件都需要访问 api server,所以这里就可想而知 api server 的压力还是挺大的。
-
该端口默认值为6443,可通过启动参数
--secure-port
的值来修改默认值。 -
默认IP地址为非本地(Non-Localhost)网络端口,通过启动参数
--bind-address
设置该值。 -
该端口用于接收客户端、dashboard等外部HTTPS请求。
-
用于基于Tocken文件或客户端证书及HTTP Base的认证。
-
用于基于策略的授权。
Crontrollermanager(控制器):
维持 pod 副本的期望数,是我们 K8S 中的重要组件
Scheduler(调度器):
负责接收任务,选择合适的节点进行分配任务,所谓的合适呢就是保障这个节点有足够的资源,可以供给给我的pod 去运行,就是我们想这个节点有那些带宽有哪些 CPU 的使用资源都是由我们的 scheduler 去调度完成的。
-
Kubernetes 调度器是一个控制面进程,负责将 Pods 指派到节点上。
-
通过调度算法为待调度Pod列表的每个Pod从可用Node列表中选择一个最适合的Node,并将信息写入etcd中。
-
node节点上的kubelet通过API Server监听到kubernetes Scheduler产生的Pod绑定信息,然后获取对应的Pod清单,下载Image,并启动容器。
策略:
-
Least Requested Priority
优先从备选节点列表中选择资源消耗最小的节点(CPU+内存)
-
Calculate NodeLabel Priority
优先选择含有指定Label的节点。
-
Balanced Resource Allocation
优先从备选节点列表中选择各项资源使用率最均衡的节点。
Etcd(键值对数据库):
存储 K8S 集群的所有重要信息(持久化)因为有些数据虽然很重要但是并不需要持久化,也就是说需要持久化的数据都需要写入到我们的 ETCD 中,也就意味者我们有一天想要恢复我们的 K8S 集群的话,只需要对我们的etcd进行还原即可,这是我们 master 节点上有的组件
1.3.2 Node节点的组件介绍:
Kubelet:
直接跟 docker 或者容器引擎交互实现容器的生命周期管理,可以这样理解 kubelet 接收到指令以后,先把我们K8S 发送过来的指令进行理解,理解完成以后再去把对应的指令直接转化我们的 container(容器)能够听懂的命令,并且让他去创建达到这么一个 pod 创建的流程。
Kube proxy:
负责写入我们的规则至 iptables 、或者是 ipvs 实现服务映射访问的
1.4 其他插件说明
Coredns:
主要实现的功能可以为集群中的 SVC 创建一个 A 记录或者是一个域名 IP 的对应关系解析,也就是意味者以后我们再集群中访问我们的一些其他的 pod 的时候完全不需要这个 pod 的 IP 地址而是通过 coredns 给他生成的这么一个域名去实现访问,coredns 也是我们 K8S 集群中重要的组件。也是实现我们负载均衡得其中一项功能。
Dashboard:
给 K8S 集群提供一个 B/S 结构的访问体系
B/S : 浏览器和服务器得访问体系
Ingress controller:
因为 K8S 只能够实现一个四层代理,那 ingress controller 可以帮组 k8s 实现七成代理
Federation:
提供一个可以跨集群中心多 K8S 的统一管理的功能。
Prometheus(普罗米修斯):
提供一个 K8S 集群的监控能力
ELK:
提供 K8S 的集群日志统一分析接入平台。
高可用集群副本数据最好是大于或等于三个的奇数
2 kubernetes – 基础概念
2.1 pod 概念
自主式POD:不是被控制器管理的pod。一旦死亡就不会再重生
控制器管理的POD:就是被控制器所管理的POD。
2.1.1 自主式POD的基础概念
容器会共用pause的网络栈,也就是说这两个容器就没有他的独立地址了他们都是共同使用pause的地址、共用他的存储卷
Pause 网络栈共享:
首先我们要定义一个 POD,就会先启动第一个容器,只要运行一个POD这个容器就会被启动、这个容器叫 PAUSE(只要是有POD这个容器就会被启动)、这个容器启动成功之后我假设在这个POD里启动了两个容器,也就是说一个POD里会被封装多个容器。然后这两个容器会共用pause的网络栈,也就是说这两个容器就没有他的独立地址了,他们都是共同使用pause的地址、共用他的存储卷、但是这两个容器之间进程不隔离(也就意味着、这里一个容器运行的是php-fpm一个容器运行的是nginx,如果这里nginx想要反向代理fpm的话只需要写locahost9000(这个是php服务的9000端口)即可,原因是这两个容器都是共享的pause的网络栈、也就是在同一个pod里容器之间的端口不能冲突的。)
Pause 存储卷共享:
假如这个pod会挂在一个存储上,同理这两个容器(php-fpm、nginx)都会共享pause的存储。因为他们两个分别都需要共享html的内容。
也就意味着在同一个pod里及共享网络又共享存储卷。
2.1.2 控制器管理的POD的基础概念
Replication Controller(简称RC)& ReplicaSet(简称RS) & Deployment 这是三种管理器类型、因为这三种有一定的重合性所以放在一起讲。
Replication Controller: 用来确保容器应用的副本数始终保持在用户定义的副本数,即如果有容器异常退出,会自动创建新的 Pod 来替代;而如果异常多出来的容器也会自动回收。在新版本的 Kubernetes中建议使用 ReplicaSet(在K8s新版本中官方抛弃RC采用的都是RS) 来取代Replication Controlle(RC)
ReplicaSet 跟 ReplicationController 没有本质的不同,只是名字不一样,并且 ReplicaSet 支持集合式的selector(选择器)
虽然 ReplicaSet 可以独立使用,但一般还是建议使用 Deployment(调度) 来自动管理 ReplicaSet ,这样就无需担心跟其他机制的不兼容问题(比如 ReplicaSet不支持 rolling-update(滚动更新) 但 Deployment (调度) 支持)
2.1.2.1 Deployment(调度)原理
deployment 为 pod 和 replicaset 提供一个声明式定义(declarative)方法,用来代替以前的 replication controller 来方便管理应用。典型的应用场景包括:
-
定义 deployment 来创建 pod 和 replicaset
-
滚动升级和回滚应用
-
扩容和缩容
-
暂停和继续 deployment
Deployment在创建出来以后呢会去创建一个RS,也就意味这RS不是我们自己定义得,而是deployment定义得,deployment再去负责创建我们对应的pod。比如上图创建了三个pod,如果有一天我们让deployment把镜像的V1版更新成V2版本。他会新建一个RS(这里比如是RS-1)然后我们就将RS的V1滚动更新到RS-1的V2版已到达滚动更新的状态,并且还可以回滚,所谓的回滚就是当发现更新到V2版本有一定的小BUG我们可以支持回滚,只见我们的rolling-update(滚动更新)就会回到RS新建一个老旧版本的V1然后把RS-1的V2删了以此类推,原因是deployment再滚动更新之后这个RS并不会被删除而是被停用,当我们开始回滚的时候就会将老旧版本的RS开始启用,然后逐渐达到deployment(调度器)想要达到的预期状态。以上是deployment的原理,deployment需要去创建RS达到创建pod的能力
2.1.2.2 HPA(水平制度扩展)
Horizontal Pod Autoscaling 仅适用于仅适用于 Deployment 和 ReplicaSet,在 V1 版本中仅支持根据Pod的 CPU 利用率扩缩容,在 vlalpha版本中,支持根据内存和用户自定义的 metric(公制的)扩缩容。
我现在运行了一个RS-1,RS-1下面管理两个pod,然后我再定义一个HPA,这个HPA定义是基于这个RS-1来定义的,当我们的CPU大于80的时候就进行扩展,扩展的最大值是10个最小值是两个,也就以为这HPA会去监控这些POD的资源利用率,当CPU达到80的时候HPA就会去新建出来新的pod直到达到他HPA的最大值10个,也就是cpu只有在大于或等于80的时候HPA才会去创建pod,如果不满足这个条件就不会再进行创建。一旦利用率变低之后这里的pod就会被回收。但是最小只能删到2因为这里定义了最小个数为2,所以HPA就会达成一个水平制度扩展的目的。
2.1.2.3 StatefullSet 解决有状态服务的问题
StatefulSet 是为了解决有状态服务的问题(对应 Deployments 和 ReplicaSets 是为无状态服务而设),其应用场景包括:
-
稳定的持久化存储(就是pod在死亡以后再去调度一个新的pod回来的时候里面的数据也不能丢失),即 Pod重新调度后还是能访问到相同的持久化数据,基于 PVC来实现来实现。
-
稳定的网络标志,即 Pod 重新调度后其 PodName(pod名称)和 HostName(主机名称)不变,基于 Headless Service(即没有 Cluster IP 的 Service)来实现。这是防止在集群里面定义了一个pod名称去调用,结果出现了一个新的pod顶替以后名字变了需要重新写入这是不需要的,因为他会实时的稳定这个网络表示
-
有序部署,有序扩展,有序部署会分为扩展和回收阶段,所以称为有序部署和有序扩展。即 Pod 是有顺序的,在部署或者扩展的时候要依据定义的顺序依次进行(即从 0 到 N-1,在下一个 Pod 运行之前所有Pod 必须都是Running(运行)和 Ready(准备)状态),基于 init containers 来实现
-
有序收缩,有序删除(即从 N-1 到 0)
2.1.2.4 daemonset(守护进程)
DaemonSet 确保全部(或者一些) Node 上运行一个 Pod 的副本。当有 Node 加入集群时,也会为他们新增一个 Pod 。当有 Node 从集群移除时,这些 Pod 也会被回收。删除 DaemonSet 将会删除它创建的所有 Pod
使用 DaemonSet 的一些典型用法:
-
运行集群存储 daemon ,例如在每个 Node 上运行 glusterd 、ceph 。
-
在每个 Node 上运行日志收集 daemon ,例如 fluentd 、logstash 。
-
在每个 Node 上运行监控 daemon ,例如 Prometheus Node Exporter
2.1.2.5 Job (工作),Cronjob(定时任务)
Job 负责批处理任务,即仅执行一次的任务,它保证批处理任务的一个或多个 Pod 成功结束
Cron Job 管理基于时间的 Job ,即:
-
在给定时间点只运行一次
-
周期性地在给定时间点运行
Cron job:简单来说就是可以在特定的时间重复执行。
2.2 服务发现
所谓的服务发现就是我们的客户端想要去访问我们的一组pod,并且被访问的pod必须要有相关性,因为只有是有相关性才会被我的service收集到。并且service去收集我们的pod是通过我们的标签去选择到的。选择到以后这个service会有自己的ip和端口,这时候我们的客户端(client)就可以去访问service的IP加端口。而间接的访问到所对应的pod,并且这里是有一个RR(轮询)的算法存在的。
2.2.1 pod 与 pod 之间的通讯方案
上图解析:
就是我们的 apache 加我们的 fpm 模块。然后前面是一些缓存服务器 squid(缓存服务)。最后面就是我们的MySQL,而 squid(缓存服务)前面肯定是需要一个负载服务器的这里我就用 LVS。
也就意味这首先我们要构建一个 apache 加入到 fpm 的结构集群,然后在构建 mysql 集群正在构建 squid 集群。假如我们想把这个结构放在我们的 k8s 集群中运行的话。
MySQL 需要运行成一个 pod ,mysql 封装成一个 pod 之后呢我们还有 apache+fpm ,并且 apache+fpm 这三个都是类似的所以我们可以在 deployment(调度器)上面去创建。Deployment 会指定 apache+fpm 的副本数目为三个副本。这样就会有三个不同的 apache+fpm 的 pod 存在了。
然后在往上我们会发现有三个 squid(缓存服务),这三个 squid(缓存服务)我们也可以交个控制器去控制。然后 LVS 就可以靠他本身的功能将这个集群负载了。
而我们在 php-fpm 和 squid 的中间再加一层 service – php-fpm。这个 service – php-fpm 会绑定 php-fpm 的标签选择进行绑定,那 squid 想要进行反向代理的设置的话就不需要写这三个 php-fpm pod 所对应的IP地址了。而是写的 service – php-fpm 的IP地址,因为 php-fpm 这三个 pod 如果死掉的话就会从新生成ip地址,而间接都就要修改 squid 反向代理的配置,以避免不必要的问题。这样的话 squid 只要将 IP 指定在我们的 service – php-fpm上面即可并且 php-fpm 是三个 pod ,MySQL 也是 pod 他们之间会出现一定的关联,比如我们将mysql部署在StatefullSet 里面这时 mysql pod 的名称就不会变,那我们可以通过他的名称去固定在我们对应 pod 上,因为K8S 内部是一个扁平化的网络,所以容器之间是可以互相访问的所以我们的 php-fpm 里面写 mysql 的信息是没有问题的,对于这三台 squid 来说用户想要访问,我可以在创一个 service – squid 与我们的 squid 进行绑定,通过去判断我们的 squid 的相关一些标签进行确定。
这样的话我们只需要将我们的 service-squid 暴露在外面即可,service 其中有一种暴露模式叫做我们的nodeport 。这样我们就可以将这个集群部署在K8S集群中了。这个集群也是我们的pod与pod之间的通讯方案。
2.3 网络的通讯方式
2.3.1 网络通讯模式
Kubernetes 的网络模型假定了所有 Pod 都在一个可以直接连通的扁平的网络空间中(在 K8S 中所谓的扁平化网络空间就是所有的 pod 都可以通过对方的ip“直接达到”就是 pod 认为他自己是直接到达的,其实底层有一堆的转换机制存在),这在 GCE (Google Compute Engine 谷歌的云服务)里面是现成的网络模型,在 Kubernetes中假定这个网络已经存在(也就意味了如果我们想在自己的集群中去构建K8S的话就先要去解决扁平化的网络空间)。
而在私有云里搭建 Kubernetes 集群,就不能假定这个网络已经存在了。我们需要自己实现这个网络假设,将不同节点上的 Docker 容器之间的互相访问先打通,然后运行 Kubernetes
2.3.2 同一个 pod 内的容器之间通讯
同一个 Pod 内的多个容器之间: lo (回环网卡)
各 Pod 之间的通讯: Overlay Network(重叠网络)
Pod 与 Service 之间的通讯:通过各节点的 Iptables(防火墙)规则的转换去实现。在K8S新版已经加入了LVS的转发机制并且转发上线会更高。
2.3.3 网络解决方案 Kubernetes + Flannel
Flannel 是 CoreOS 团队针对 Kubernetes 设计的一个网络规划服务,简单来说,它的功能是让集群中的不同节点主机创建的 Docker 容器都具有全集群唯一的虚拟 IP 地址。而且它还能在这些 IP 地址之间建立一个覆盖网络( Overlay Network ),通过这个覆盖网络,将数据包原封不动地传递到目标容器内
上面加粗的这句话提取出来的概念就是,不同机器这里指的是物理机,上面运行的docker。这里面的容器他们之间的IP不能一致。
2.3.4 Flanneld的网络的通讯方案原理(同主机和不同主机通讯)
这里画了两台大主机、一个是192.168.66.11/24一个是192.168.66.12/24然后在这两台主机中运行了4个pod,然后在192.168.66.11这个主机中运行的是web app2和web app1的两个pod。然后在192.168.66.12这个主机中运行的是web app3和backend(前端组件)。然后所有的浏览访问到backend上,backend经过自己的网关去处理,把什么样的请求分配在什么样的服务上。
那也就意味着当backend想要去跟web app2或者是web app2想要跟backend通讯的时候就需要涉及到跨主机了,以及web app3跟本机的backend两个不同pod之间通讯的话他们是同主机的,是怎么来解决通讯的问题的。那我们下面来详细解析一下。
首先在我们的node服务器上我们会去安装一个叫flanneld的守护进程,这个flanneld的进程呢会监听一个端口,这个端口就是用于后期转发数据包以及接收数据包的这么一个服务端口。这个flanneld进程一旦启动之后会开启一个网桥叫 flannel0 这个网桥 flannel0 就是专门去收集 docker0 转发出来的数据报文。然后这个 docker0 会分配自己的 IP 到对应的 POD 上,如果是我们同一台主机上的两个 pod 之间的访问的话走的其实是 docker0 的网桥,因为大家都是在同一个网桥下面的两个不同的子网而已。也就是在真实服务器的主机内核已经完成了这次数据报文的转换。
怎么跨主机还能通过对方的 IP 直接到达:
到达流程:首先我们的 web app2 pod 想要把数据包发送到 backend ,这时候 web app2 的数据包源地址写自己的 10.1.15.2/24 目标要写 backend 的 10.1.20.3/24 。由于不是在同一台主机所以 web app2 先发送到他的网关也就是 docker0 的 IP 10.1.15.1/24,而 docker0 上会有对应的函数把这个数据包抓到我们的 flannel0 并且在flannel0 上会有一堆的路由表记录。这个路由表记录是从我们的 etcd 里面获取到的,写入到我们当前的主机判断路由到底到那台机器。到了 flannel0 之后因为 flannel0 是 flanneld 开启这么一个网桥所以这个数据包会到flanneld,到了 flanneld 以后会对这个数据报文进行封装也就是上图的封装部分。有这么一个结构mac是mac部分,然后到我们的下一层也就是第三层,第三层内容写的是源地址为192.168.66.11目标写的是192.168.66.12 。接着下一层封装的是UDP的数据报文,也就意味着 flanneld 使用的是UDP的数据报文去转发这些数据包因为更快,毕竟在同一个局域网内部。在下一层有封装了一层新的三层信息源是10.1.15.2目标是10.1.20.3封装到这一层以后外面就封装了一个 payload 实体。在被转发到192.168.66.12的这个主机上。原因是我们的三层写了目标地址是192.168.66.12所以这个数据报文是能够到这里的,并且也是对应的这里的flanneld的端口所以这个数据包会被192.168.66.12主机的 flanneld 所截获。Flanneld截获以后会进行拆封,因为192.168.66.12主机的 flanneld 他知道这是干嘛的,拆封完了之后会转发到192.168.66.12主机的 flannel0(10.1.20.0)然后再转发到docker0(10.1.20.1)。然后在转发给 192.168.66.12主机上的 backend pod。这样的话就能是实现我们的跨主机的扁平化网络。
以上就是整个flanneld的网络的通讯方案
2.3.5 Flannel与ETCD之间有哪些关联
ETCD 之 Flannel 提供说明:
1、存储管理 Flannel 可分配的 IP 地址段资源,也就意味着 flannel 在启动之后会向 etcd 去插入可以被分配的网段,并且把那个网段分配到那台机器 etcd 都会记录着,防止以增的网段在被 flannel 利用分配给其他的 node 节点。以避免IP冲突
2、监控 ETCD 中每个 Pod 的实际地址,并在内存中建立维护 Pod 节点路由表。
也就是说 ETCD 在我们的K8S集群中,是非常重要的,所以想要进行高可用的话 ETCD 一定是我们最先高可用的一个组件。
2.3.6 不同情况下网络通信方式
同一个 Pod 内部通讯:同一个 Pod 共享同一个网络命名空间,共享同一个 Linux协议栈所以都是使用的 lo 的回环网卡进行通讯的
Pod1 至 Pod2
> Pod1 与 Pod2 不在同一台主机, Pod 的地址是与 docker0 在同一个网段的,但 docker0 网段与宿主机网卡是两个完全不同的 IP 网段,并且不同 Node 之间的通信只能通过宿主机的物理网卡进行。将 Pod 的 IP 和所在 Node 的 IP 关联起来,通过这个关联让 Pod 可以互相访问
> Pod1 与 Pod2 在同一台机器,由 Docker0 网桥直接转发请求至 Pod2 ,不需要经过 Flannel Pod 至 Service 的网络:目前基于性能考虑,全部为 iptables或LVS 维护和转发
Pod 到外网: Pod 向外网发送请求,查找路由表 , 转发数据包到宿主机的网卡,宿主网卡完成路由选择后, iptables 执行 Masquerade 动作 ,把源 IP 更改为宿主网卡的 IP ,然后向外网服务器发送请求,也就意味着如果 pod 里面的容器想上网也就是通过 masquerade 的动态转换去完成所谓的上网行为
外网访问 Pod :Service ,外网想要访问pod就要借助到我们service的node pod方式才能进行所谓的访问的K8S集群内部,因为她虽然是一个扁平化的网络,但可别忘了这个扁平化的网络是一个私有网络并不能在跟物理机相邻的网络内访问到他所以要借助到我们的node pod进行映射。
以上就是我们的不同网络的通讯机制。
2.3.7 组件通讯示意图
ldy前来打开,加油
ldy前来打卡,加油
加油