提交 51447457 编写于 作者: RunAtWorld's avatar RunAtWorld

k8s架构

上级 efb0387b
......@@ -21,6 +21,10 @@
* [架构解析](arch/README.md)
* [scheduler](arch/scheduler/README.md)
* [controller](arch/k8s_controller.md)
* [pod](arch/k8s_pod.md)
* [cri](arch/k8s_cri.md)
* [istio](arch/istio.md)
## 应用
......
......@@ -2,97 +2,165 @@
> Kubernetes API Server , Controller Manager , Scheduler , kubelet , kube-proxy
![](../images/k8s_layer.png)
Kubernetes是一个轻便的和可扩展的开源平台,用于管理容器化应用和服务。通过Kubernetes能够进行应用的自动化部署和扩缩容。在Kubernetes中,会将组成应用的容器组合成一个逻辑单元以更易管理和发现。Kubernetes积累了作为Google生产环境运行工作负载15年的经验,并吸收了来自于社区的最佳想法和实践。Kubernetes经过这几年的快速发展,形成了一个大的生态环境,Google在2014年将Kubernetes作为开源项目。Kubernetes的关键特性包括:
![](../images/k8s_arch.png)
* 自动化装箱:在不牺牲可用性的条件下,基于容器对资源的要求和约束自动部署容器。同时,为了提高利用率和节省更多资源,将关键和最佳工作量结合在一起。
* 自愈能力:当容器失败时,会对容器进行重启;当所部署的Node节点有问题时,会对容器进行重新部署和重新调度;当容器未通过监控检查时,会关闭此容器;直到容器正常运行时,才会对外提供服务。
* 水平扩容:通过简单的命令、用户界面或基于CPU的使用情况,能够对应用进行扩容和缩容。
* 服务发现和负载均衡:开发者不需要使用额外的服务发现机制,就能够基于Kubernetes进行服务发现和负载均衡。
* 自动发布和回滚:Kubernetes能够程序化的发布应用和相关的配置。如果发布有问题,Kubernetes将能够回归发生的变更。
* 保密和配置管理:在不需要重新构建镜像的情况下,可以部署和更新保密和应用配置。
* 存储编排:自动挂接存储系统,这些存储系统可以来自于本地、公共云提供商(例如:GCP和AWS)、网络存储(例如:NFS、iSCSI、Gluster、Ceph、Cinder和Floker等)。
## 整体架构
![](images/arch.png)
Kubernetes属于主从分布式架构,主要由Master Node和Worker Node组成,以及包括客户端命令行工具kubectl和其它附加项。
* Master Node:作为控制节点,对集群进行调度管理;Master Node由API Server、Scheduler、Cluster State Store和Controller-Manger Server所组成;
* Worker Node:作为真正的工作节点,运行业务应用的容器;Worker Node包含kubelet、kube proxy和Container Runtime;
* kubectl:用于通过命令行与API Server进行交互,而对Kubernetes进行操作,实现在集群中进行各种资源的增删改查等操作;
* Add-on:是对Kubernetes核心功能的扩展,例如增加网络和网络策略等能力。
* repliceation 用于伸缩副本数量
* endpoint 用于管理网络请求
* scheduler 调度器
### 用户通过一个Yaml控制k8s产生相应Pod的过程
![](images/yaml2pod.png)
1. 准备好一个包含应用程序的Deployment的yml文件,然后通过kubectl客户端工具发送给ApiServer。
2. ApiServer接收到客户端的请求并将资源内容存储到数据库(etcd)中。
3. Controller组件(包括scheduler、replication、endpoint)监控资源变化并作出反应。
4. ReplicaSet检查数据库变化,创建期望数量的pod实例。
5. Scheduler再次检查数据库变化,发现尚未被分配到具体执行节点(node)的Pod,然后根据一组相关规则将pod分配到可以运行它们的节点上,并更新数据库,记录pod分配情况。
6. Kubelet监控数据库变化,管理后续pod的生命周期,发现被分配到它所在的节点上运行的那些pod。如果找到新pod,则会在该节点上运行这个新pod。
7. kuberproxy运行在集群各个主机上,管理网络通信,如服务发现、负载均衡。例如当有数据发送到主机时,将其路由到正确的pod或容器。对于从主机上发出的数据,它可以基于请求地址发现远程服务器,并将数据正确路由,在某些情况下会使用轮训调度算法(Round-robin)将请求发送到集群中的多个实例。
### k8s生态
![](images/layer.png)
### 创建一个Pod的过程
创建Pod的过程,时序图如下:
![](images/create_pod.png)
1. 用户提交创建Pod的请求,可以通过API Server的REST API ,也可用Kubectl命令行工具,支持Json和Yaml两种格式;
2. API Server 处理用户请求,存储Pod数据到Etcd;
3. Schedule通过和 API Server的watch机制,查看到新的pod,尝试为Pod绑定Node;
4. 过滤主机:调度器用一组规则过滤掉不符合要求的主机,比如Pod指定了所需要的资源,那么就要过滤掉资源不够的主机;
5. 主机打分:对第一步筛选出的符合要求的主机进行打分,在主机打分阶段,调度器会考虑一些整体优化策略,比如把一个Replication Controller的副本分布到不同的主机上,使用最低负载的主机等;
6. 选择主机:选择打分最高的主机,进行binding操作,结果存储到Etcd中;
7. kubelet根据调度结果执行Pod创建操作:绑定成功后,会启动container, docker run, scheduler会调用API Server的API在etcd中创建一个bound pod对象,描述在一个工作节点上绑定运行的所有pod信息。运行在每个工作节点上的kubelet也会定期与etcd同步bound pod信息,一旦发现应该在该工作节点上运行的bound pod对象没有更新,则调用Docker API创建并启动pod内的容器。
## Kubernetes API Server
Kubernetes API Server 的核心功能提供了 Kubernetes 各类资源对象(Pod、RC、Service)的增删查改及Watch等 HTTP Rest 接口,成为集群内各个功能模块之间数据交互和通信的中心枢纽,是整个系统的数据总线和数据中心,是集群管理的API入口,资源配额控制的入口,提供完备的集群安全机制。
API Server是所有REST命令的入口,它的相关结果状态将被保存在etcd(或其他存储)中。API Server的基本功能包括:
* REST语义,监控,持久化和一致性保证,API 版本控制,放弃和生效
* 内置准入控制语义,同步准入控制钩子,以及异步资源初始化
* API注册和发现
另外,API Server也作为集群的网关。默认情况,客户端通过API Server对集群进行访问,客户端需要通过认证,并使用API Server作为访问Node和Pod(以及service)的堡垒和代理/通道。
## etcd
Kubernetes默认使用etcd作为集群整体存储,当然也可以使用其它的技术。etcd是一个简单的、分布式的、一致的key-value存储,主要被用来共享配置和服务发现。etcd提供了一个CRUD操作的REST API,以及提供了作为注册的接口,以监控指定的Node。集群的所有状态都存储在etcd实例中,并具有监控的能力,因此当etcd中的信息发生变化时,就能够快速的通知集群中相关的组件。
## Controller Manager
Controller Manager 是集群内部的管理控制中心,负责集群中的Node、Pod副本、服务端点(Endpoint)、命名空间(Namespace)、服务账号(Service Account)、资源配额(ResourceQuota)等的管理,当某个Node意外宕机时,Controller Manager会及时发现此故障并执行自动化修复流程,确保集群始终处于预期的工作状态。
![](./controlerManager.jpg)
Controller-Manager Serve用于执行大部分的集群层次的功能,它既执行生命周期功能(例如:命名空间创建和生命周期、事件垃圾收集、已终止垃圾收集、级联删除垃圾收集、node垃圾收集),也执行API业务逻辑(例如:pod的弹性扩容)。控制管理提供自愈能力、扩容、应用生命周期管理、服务发现、路由、服务绑定和提供。
Kubernetes默认提供Replication Controller、Node Controller、Namespace Controller、Service Controller、Endpoints Controller、Persistent Controller、DaemonSet Controller等控制器。
## Scheduler
Kubernetes Scheduler 是负责Pod调度的重要功能模块再整个系统中有“承上启下”的作用。它负责接收 Controller Manager 创建新Pod的请求,为新Pod找到一个目标Node,新Pod创建后,目标Node上的kubelet服务进程负责Pod的剩余生命周期。
scheduler组件为容器自动选择运行的主机。依据请求资源的可用性,服务请求的质量等约束条件,scheduler监控未绑定的pod,并将其绑定至特定的node节点。Kubernetes也支持用户自己提供的调度器,Scheduler负责根据调度策略自动将Pod部署到合适Node中,调度策略分为预选策略和优选策略,Pod的整个调度过程分为两步:
具体来说,Kubernetes Scheduler 的作用是将待调度的Pod(包括Kubernetes API 新创建的Pod、Controller Manager为补足副本而创建的Pod)按照特定的调度算法和调度策略绑定到集群中某个合适的Node上,并将绑定信息写入etcd。整个调度过程涉及到三个对象:待调度Pod列表、可用Node列表以及调度算法和策略。
1. 预选Node:遍历集群中所有的Node,按照具体的预选策略筛选出符合要求的Node列表。如没有Node符合预选策略规则,该Pod就会被挂起,直到集群中出现符合要求的Node。
2. 优选Node:预选Node列表的基础上,按照优选策略为待选的Node进行打分和排序,从中获取最优Node。
简单来讲,Kubernetes Scheduler 的核心工作就是通过调度算法为待调度Pod列表的每个 Pod 从 Node 列表中选择一个最适合的Node。随后,目标节点上的 kubelet 通过 API Server 监听到 Kubernetes Scheduler 产生的 Pod 绑定事件,然后获取对应的Pod清单,下载Image镜像,并启动容器。
## kubelet
Kubelet是Kubernetes中最主要的控制器,它是Pod和Node API的主要实现者,Kubelet负责驱动容器执行层。在Kubernetes中,应用容器彼此是隔离的,并且与运行其的主机也是隔离的,这是对应用进行独立解耦管理的关键点。
在Kubernets中,Pod作为基本的执行单元,它可以拥有多个容器和存储数据卷,能够方便在每个容器中打包一个单一的应用,从而解耦了应用构建时和部署时的所关心的事项,已经能够方便在物理机/虚拟机之间进行迁移。API准入控制可以拒绝或者Pod,或者为Pod添加额外的调度约束,但是Kubelet才是Pod是否能够运行在特定Node上的最终裁决者,而不是scheduler或者DaemonSet。kubelet默认情况使用cAdvisor进行资源监控。负责管理Pod、容器、镜像、数据卷等,实现集群对节点的管理,并将容器的运行状态汇报给Kubernetes API Server。
### Container Runtime
每一个Node都会运行一个Container Runtime,其负责下载镜像和运行容器。Kubernetes本身并不停容器运行时环境,但提供了接口,可以插入所选择的容器运行时环境。kubelet使用Unix socket之上的gRPC框架与容器运行时进行通信,kubelet作为客户端,而CRI shim作为服务器。
![](images/cri.png)
![](../scheduler/Scheduler.jpg)
protocol buffers API提供两个gRPC服务,ImageService和RuntimeService。ImageService提供拉取、查看、和移除镜像的RPC。RuntimeSerivce则提供管理Pods和容器生命周期管理的RPC,以及与容器进行交互(exec/attach/port-forward)。容器运行时能够同时管理镜像和容器(例如:Docker和Rkt),并且可以通过同一个套接字提供这两种服务。在Kubelet中,这个套接字通过–container-runtime-endpoint和–image-service-endpoint字段进行设置。
在 kube-scheduler 的启动参数中 ,`--algorithm-provider="DefaultProvider"` 用于设置调度算法,默认为 DefaultProvider 。默认调度过程如下[[1]]():
1. 预选调度过程,即遍历所有目标 Node ,选出符合要求的候选节点。为此,Kubernetes内置了多种预选策略(xxx Predicates) 供用户选择
2. 确定最优节点。在第1步的基础上,采取优选策略(xxx Prioritt)计算出每个候选节点的积分,积分高者胜出。
Kubernetes CRI支持的容器运行时包括docker、rkt、cri-o、frankti、kata-containers和clear-containers等。
Kubernetes Scheduler的调度流程是通过插件方式加载的“调度算法提供者”(AlgorithmProvider)具体实现的。一个AlgorithmProvider就是包括了一组预选调度策略与一组优先选择策略的结构体,注册 RegisterAlgorithmProvider 的函数在包 `\kubernetes\cmd\kube-scheduler\app\server.go`,如下:
## kube-proxy
基于一种公共访问策略(例如:负载均衡),服务提供了一种访问一群pod的途径。此方式通过创建一个虚拟的IP来实现,客户端能够访问此IP,并能够将服务透明的代理至Pod。每一个Node都会运行一个kube-proxy,kube proxy通过iptables规则引导访问至服务IP,并将重定向至正确的后端应用,通过这种方式kube-proxy提供了一个高可用的负载均衡解决方案。服务发现主要通过DNS实现。
在Kubernetes中,kube proxy负责为Pod创建代理服务;引到访问至服务;并实现服务到Pod的路由和转发,以及通过应用的负载均衡。
## kubectl
kubectl是Kubernetes集群的命令行接口。运行kubectl命令的语法如下所示:
```
func RegisterAlgorithmProvider(name string, predicateKeys, priorityKeys sets.String) string {
schedulerFactoryMutex.Lock()
defer schedulerFactoryMutex.Unlock()
validateAlgorithmNameOrDie(name)
algorithmProviderMap[name] = AlgorithmProviderConfig{
FitPredicateKeys: predicateKeys,
PriorityFunctionKeys: priorityKeys,
}
return name
}
$ kubectl [command] [TYPE] [NAME] [flags]
```
这3个参数: "name string" 为算法名,"predicateKeys" 为算法用到的预选策略集合,
"priorityKeys sets.String" 为算法用到的优选策略集合。
这里的command,TYPE、NAME和flags为:
- comand:指定要对资源执行的操作,例如create、get、describe和delete
- TYPE:指定资源类型,资源类型是大小学敏感的,开发者能够以单数、复数和缩略的形式。例如:
其中 Scheduler 可用的预选策略有:
```
CheckNodeCondition:#检查节点是否正常(如ip,磁盘等)
GeneralPredicates
HostName:#检查Pod对象是否定义了pod.spec.hostname
PodFitsHostPorts:#pod要能适配node的端口 pods.spec.containers.ports.hostPort(指定绑定在节点的端口上)
MatchNodeSelector:#检查节点的NodeSelector的标签 pods.spec.nodeSelector
PodFitsResources:#检查Pod的资源需求是否能被节点所满足
NoDiskConflict: #检查Pod依赖的存储卷是否能满足需求(默认未使用)
PodToleratesNodeTaints:#检查Pod上的spec.tolerations可容忍的污点是否完全包含节点上的污点;
PodToleratesNodeNoExecuteTaints:#不能执行(NoExecute)的污点(默认未使用)
CheckNodeLabelPresence:#检查指定的标签再上节点是否存在
CheckServiceAffinity:#将相同services相同的pod尽量放在一起(默认未使用)
MaxEBSVolumeCount: #检查EBS(AWS存储)存储卷的最大数量
MaxGCEPDVolumeCount #GCE存储最大数
MaxAzureDiskVolumeCount: #AzureDisk 存储最大数
CheckVolumeBinding: #检查节点上已绑定或未绑定的pvc
NoVolumeZoneConflict: #检查存储卷对象与pod是否存在冲突
CheckNodeMemoryPressure:#检查节点内存是否存在压力过大
CheckNodePIDPressure: #检查节点上的PID数量是否过大
CheckNodeDiskPressure: #检查内存、磁盘IO是否过大
MatchInterPodAffinity: #检查节点是否能满足pod的亲和性或反亲和性
$ kubectl get pod pod1
$ kubectl get pods pod1
$ kubectl get po pod1
```
其中默认的预选策略为:
>
Scheduler 可用的优选策略有:
NAME:指定资源的名称,名称也大小写敏感的。如果省略名称,则会显示所有的资源,例如:
```
LeastRequested:#空闲量越高得分越高
(cpu((capacity-sum(requested))*10/capacity)+memory((capacity-sum(requested))*10/capacity))/2
BalancedResourceAllocation:#CPU和内存资源被占用率相近的胜出;
NodePreferAvoidPods: #节点注解信息“scheduler.alpha.kubernetes.io/preferAvoidPods”
TaintToleration:#将Pod对象的spec.tolerations列表项与节点的taints列表项进行匹配度检查,匹配条目越,得分越低;
SeletorSpreading:#标签选择器分散度,(与当前pod对象通选的标签,所选其它pod越多的得分越低)
InterPodAffinity:#遍历pod对象的亲和性匹配项目,项目越多得分越高
NodeAffinity: #节点亲和性
MostRequested: #空闲量越小得分越高,和LeastRequested相反 (默认未启用)
NodeLabel: #节点是否存在对应的标签 (默认未启用)
ImageLocality:#根据满足当前Pod对象需求的已有镜像的体积大小之和(默认未启用)
$kubectl get pods
```
## kubelet
- flags:指定可选的参数。例如,可以使用-s或者–server参数指定Kubernetes API server的地址和端口。
另外,可以通过运行kubectl help命令获取更多的信息。
## 附加项和其他依赖
在Kunbernetes中可以以附加项的方式扩展Kubernetes的功能,目前主要有网络、服务发现和可视化这三大类的附加项,下面是可用的一些附加项:
### 网络和网络策略
* ACI 通过与Cisco ACI集成的容器网络和网络安全。
* Calico 是一个安全的3层网络和网络策略提供者。
* Canal 联合Fannel和Calico。
* Cilium 是一个3层网络和网络侧插件,它能够透明的加强HTTP/API/L7 策略。其即支持路由,也支持overlay/encapsultion模式。
* Flannel 是一个overlay的网络提供者。
* istio 是 通过Pod代理的方式,实现k8s平台的 连接、安全加固、控制和观察服务。
### 服务发现
* CoreDNS 是一个灵活的,可扩展的DNS服务器,它能够作为Pod集群内的DNS进行安装。
* Ingress 提供基于Http协议的路由转发机制。
### 可视化&控制
* Dashboard 是Kubernetes的web用户界面。
## kube-proxy涉及
# 参考
1. 龚正,吴治辉等 . Kubernetes权威指南:从Docker到Kubernetes全接触[M] . 北京:电子工业出版社,2016:177-194
2. k8s调度器、预选策略及调度方式 . https://www.cnblogs.com/zhangb8042/p/10203266.html
3. k8s scheduler pod调度分析 . https://blog.csdn.net/weixin_39961559/article/details/81704461
4. Pod Priority and Preemption . https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/
2. [k8s-整体概述和架构](https://www.cnblogs.com/wwchihiro/p/9261607.html)
# Istio
官方对 Istio 的介绍浓缩成了一句话:
```
An open platform to connect, secure, control and observe services.
```
翻译过来,就是”连接、安全加固、控制和观察服务的开放平台“。开放平台就是指它本身是开源的,服务对应的是微服务,也可以粗略地理解为单个应用。
中间的四个动词就是 Istio 的主要功能,官方也各有一句话的说明。这里再阐释一下:
* 连接(Connect):智能控制服务之间的调用流量,能够实现灰度升级、AB 测试和红黑部署等功能
* 安全加固(Secure):自动为服务之间的调用提供认证、授权和加密。
* 控制(Control):应用用户定义的 policy,保证资源在消费者中公平分配。
* 观察(Observe):查看服务运行期间的各种数据,比如日志、监控和 tracing,了解服务的运行情况。
虽然听起来非常高级,功能非常强大,但是一股脑出现这么多名词,还都是非常虚的概念,说了跟没说一样。要想理解上面这几句话的含义,我们还是从头说起,先聊聊 Service Mesh。
## Service Mesh
一般介绍 Service Mesh 的文章都会从网络层的又一个抽象说起,把 Service Mesh 看做建立在 TCP 层之上的微服务层。我这次换个思路,从 Service Mesh 的技术根基——网络代理来分析。
说起网络代理,我们会想到翻墙,如果对软件架构比较熟悉的会想到 Nginx 等反向代理软件。
其实网络代理的范围比较广,可以肯定的说,有网络访问的地方就会有代理的存在。
Wikipedia 对代理的定义如下:
In computer networks, a proxy server is a server (a computer system or an application) that acts as an intermediary for requests from clients seeking resources from other servers.
NOTE:代理可以是嵌套的,也就是说通信双方 A、B 中间可以多多层代理,而这些代理的存在有可能对 A、B 是透明的。
![img](istio/2019090632.jpg)
简单来说,网络代理可以简单类比成现实生活中的中介,本来需要通信的双方因为各种原因在中间再加上一道关卡。本来双方就能完成的通信,为何非要多此一举呢?
那是因为代理可以为整个通信带来更多的功能,比如:
拦截:代理可以选择性拦截传输的网络流量,比如一些公司限制员工在上班的时候不能访问某些游戏或者电商网站,再比如把我们和世界隔离开来的 GFW,还有在数据中心中拒绝恶意访问的网关。
统计:既然所有的流量都经过代理,那么代理也可以用来统计网络中的数据信息,比如了解哪些人在访问哪些网站,通信的应答延迟等。
缓存:如果通信双方比较”远“,访问比较慢,那么代理可以把最近访问的数据缓存在本地,后面的访问不用访问后端来做到加速。CDN 就是这个功能的典型场景。
分发:如果某个通信方有多个服务器后端,代理可以根据某些规则来选择如何把流量发送给多个服务器,也就是我们常说的负载均衡功能,比如著名的 Nginx 软件。
跳板:如果 A、B 双方因为某些原因不能直接访问,而代理可以和双方通信,那么通过代理,双方可以绕过原来的限制进行通信。这应该是广大中国网民比较熟悉的场景。
注入:既然代理可以看到流量,那么它也可以修改网络流量,可以自动在收到的流量中添加一些数据,比如有些宽带提供商的弹窗广告。
……
不是要讲 Service Mesh 吗?为什么扯了一堆代理的事情?因为 Service Mesh 可以看做是传统代理的升级版,用来解决现在微服务框架中出现的问题,可以把 Service Mesh 看做是分布式的微服务代理。
在传统模式下,代理一般是集中式的单独的服务器,所有的请求都要先通过代理,然后再流入转发到实际的后端。
而在 Service Mesh 中,代理变成了分布式的,它常驻在了应用的身边(最常见的就是 Kubernetes Sidecar 模式,每一个应用的 Pod 中都运行着一个代理,负责流量相关的事情)。
这样的话,应用所有的流量都被代理接管,那么这个代理就能做到上面提到的所有可能的事情,从而带来无限的想象力。
![img](istio/2019090633.jpg)
此外,原来的代理都是基于网络流量的,一般都是工作在 IP 或者 TCP 层,很少关心具体的应用逻辑。
但是 Service Mesh 中,代理会知道整个集群的所有应用信息,并且额外添加了热更新、注入服务发现、降级熔断、认证授权、超时重试、日志监控等功能,让这些通用的功能不必每个应用都自己实现,放在代理中即可。
换句话说,Service Mesh 中的代理对微服务中的应用做了定制化的改进!
![img](istio/2019090634.jpg)
就这样,借着微服务和容器化的东风,传统的代理摇身一变,成了如今炙手可热的 Service Mesh。
应用微服务之后,每个单独的微服务都会有很多副本,而且可能会有多个版本,这么多微服务之间的相互调用和管理非常复杂,但是有了 Service Mesh,我们可以把这块内容统一在代理层。
![img](istio/2019090635.jpg)
有了看起来四通八达的分布式代理,我们还需要对这些代理进行统一的管理。
手动更新每个代理的配置,对代理进行升级或者维护是个不可持续的事情,在前面的基础上,在加上一个控制中心,一个完整的 Service Mesh 就成了。
管理员只需要根据控制中心的 API 来配置整个集群的应用流量、安全规则即可,代理会自动和控制中心打交道根据用户的期望改变自己的行为。
![img](istio/2019090636.jpg)
NOTE:所以你也可以理解 Service Mesh 中的代理会抢了 Nginx 的生意,这也是为了 Nginx 也要开始做 NginMesh 的原因。
再来看 Istio
了解了 Service Mesh 的概念,我们再来看 Istio ,也许就会清楚很多。首先来看 Istio 官方给出的架构图:
![img](istio/2019090637.jpg)
可以看到,Istio 就是我们上述提到的 Service Mesh 架构的一种实现,服务之间的通信(比如这里的 Service A 访问 Service B)会通过代理(默认是 Envoy)来进行。
而且中间的网络协议支持 HTTP/1.1,HTTP/2,gRPC 或者 TCP,可以说覆盖了主流的通信协议。
控制中心做了进一步的细分,分成了 Pilot、Mixer 和 Citadel,它们的各自功能如下:
Pilot:为 Envoy 提供了服务发现,流量管理和智能路由(AB 测试、金丝雀发布等),以及错误处理(超时、重试、熔断)功能。 用户通过 Pilot 的 API 管理网络相关的资源对象,Pilot 会根据用户的配置和服务的信息把网络流量管理变成 Envoy 能识别的格式分发到各个 Sidecar 代理中。
Mixer:为整个集群执行访问控制(哪些用户可以访问哪些服务)和 Policy 管理(Rate Limit,Quota 等),并且收集代理观察到的服务之间的流量统计数据。
Citadel:为服务之间提供认证和证书管理,可以让服务自动升级成 TLS 协议。
代理会和控制中心通信,一方面可以获取需要的服务之间的信息,另一方面也可以汇报服务调用的 Metrics 数据。
知道了 Istio 的核心架构,再来看看它的功能描述就非常容易理解了:
连接:控制中心可以从集群中获取所有服务的信息,并分发给代理,这样代理就能根据用户的期望来完成服务之间的通信(自动地服务发现、负载均衡、流量控制等)。
安全加固:因为所有的流量都是通过代理的,那么代理接收到不加密的网络流量之后,可以自动做一次封装,把它升级成安全的加密流量。
控制:用户可以配置各种规则(比如 RBAC 授权、白名单、Rate Limit 或者 Quota 等),当代理发现服务之间的访问不符合这些规则,就直接拒绝掉。
观察:所有的流量都经过代理,因此代理对整个集群的访问情况知道得一清二楚,它把这些数据上报到控制中心,那么管理员就能观察到整个集群的流量情况了
## Istio 解决什么问题
虽然看起来非常炫酷,功能也很强大,但是一个架构和产品出来都是要解决具体的问题。所以这部分我们来看看微服务架构中的难题以及 Istio 给出的答案。
首先,原来的单个应用拆分成了许多分散的微服务,它们之间相互调用才能完成一个任务,而一旦某个过程出错(组件越多,出错的概率也就越大),就非常难以排查。
用户请求出现问题无外乎两个问题:错误和响应慢。如果请求错误,那么我们需要知道那个步骤出错了,这么多的微服务之间的调用怎么确定哪个有调用成功?哪个没有调用成功呢?
如果是请求响应太慢,我们就需要知道到底哪些地方比较慢?整个链路的调用各阶段耗时是多少?哪些调用是并发执行的,哪些是串行的?这些问题需要我们能非常清楚整个集群的调用以及流量情况。
![img](istio/2019090638.jpg)
此外,微服务拆分成这么多组件,如果单个组件出错的概率不变,那么整体有地方出错的概率就会增大。服务调用的时候如果没有错误处理机制,那么会导致非常多的问题。
比如如果应用没有配置超时参数,或者配置的超时参数不对,则会导致请求的调用链超时叠加,对于用户来说就是请求卡住了。
如果没有重试机制,那么因为各种原因导致的偶发故障也会导致直接返回错误给用户,造成不好的用户体验。
此外,如果某些节点异常(比如网络中断,或者负载很高),也会导致应用整体的响应时间变长,集群服务应该能自动避开这些节点上的应用。
最后,应用也是会出现 Bug 的,各种 Bug 会导致某些应用不可访问。这些问题需要每个应用能及时发现问题,并做好对应的处理措施。
![img](istio/2019090639.jpg)
应用数量的增多,对于日常的应用发布来说也是个难题。应用的发布需要非常谨慎,如果应用都是一次性升级的,出现错误会导致整个线上应用不可用,影响范围太大。
而且,很多情况我们需要同时存在不同的版本,使用 AB 测试验证哪个版本更好。
如果版本升级改动了 API,并且互相有依赖,那么我们还希望能自动地控制发布期间不同版本访问不同的地址。这些问题都需要智能的流量控制机制。
![img](istio/20190906310.jpg)
为了保证整个系统的安全性,每个应用都需要实现一套相似的认证、授权、HTTPS、限流等功能。
一方面大多数的程序员都对安全相关的功能并不擅长或者感兴趣,另外这些完全相似的内容每次都要实现一遍是非常冗余的。这个问题需要一个能自动管理安全相关内容的系统。
![img](istio/20190906311.jpg)
上面提到的这些问题是不是非常熟悉?它们就是 Istio 尝试解决的问题,如果把上面的问题和 Istio 提供的功能做个映射,你会发现它们非常匹配,毕竟 Istio 就是为了解决微服务的这些问题才出现的。
## 用什么姿势接入 Istio?
虽然 Istio 能解决那么多的问题,但是引入 Istio 并不是没有代价的。最大的问题是 Istio 的复杂性,强大的功能也意味着 Istio 的概念和组件非常多,要想理解和掌握 Istio ,并成功在生产环境中部署需要非常详细的规划。
一般情况下,集群管理团队需要对 Kubernetes 非常熟悉,了解常用的使用模式,然后采用逐步演进的方式把 Istio 的功能分批掌控下来。
第一步,自然是在测试环境搭建一套 Istio 的集群,理解所有的核心概念和组件。
了解 Istio 提供的接口和资源,知道它们的用处,思考如何应用到自己的场景中,然后是熟悉 Istio 的源代码,跟进社区的 Issues,了解目前还存在的 Issues 和 Bug,思考如何规避或者修复。
这一步是基础,需要积累到 Istio 安装部署、核心概念、功能和缺陷相关的知识,为后面做好准备。
第二步,可以考虑接入 Istio 的观察性功能,包括 Logging、Tracing、Metrics 数据。
应用部署到集群中,选择性地(一般是流量比较小,影响范围不大的应用)为一些应用开启 Istio 自动注入功能,接管应用的流量,并安装 Prometheus 和 Zipkin 等监控组件,收集系统所有的监控数据。
这一步可以试探性地了解 Istio 对应用的性能影响,同时建立服务的性能测试基准,发现服务的性能瓶颈,帮助快速定位应用可能出现的问题。
此时,这些功能可以是对应用开发者透明的,只需要集群管理员感知,这样可以减少可能带来的风险。
第三步,为应用配置 Time Out 超时参数、自动重试、熔断和降级等功能,增加服务的容错性。
这样可以避免某些应用错误进行这些配置导致问题的出现,这一步完成后需要通知所有的应用开发者删除掉在应用代码中对应的处理逻辑。这一步需要开发者和集群管理员同时参与。
第四步,和 Ingress、Helm、应用上架等相关组件和流程对接,使用 Istio 接管应用的升级发布流程。
让开发者可以配置应用灰度发布升级的策略,支持应用的蓝绿发布、金丝雀发布以及 AB 测试。
第五步,接入安全功能。配置应用的 TLS 互信,添加 RBAC 授权,设置应用的流量限制,提升整个集群的安全性。
因为安全的问题配置比较繁琐,而且优先级一般会比功能性相关的特性要低,所以这里放在了最后。
当然这个步骤只是一个参考,每个公司需要根据自己的情况、人力、时间和节奏来调整,找到适合自己的方案。
## 总结
Istio 的架构在数据中心和集群管理中非常常见,每个 Agent 分布在各个节点上(可以是服务器、虚拟机、Pod、容器)负责接收指令并执行,以及汇报信息。
控制中心负责汇聚整个集群的信息,并提供 API 让用户对集群进行管理。
Kubernetes 也是类似的架构,SDN(Software Defined Network) 也是如此。
相信以后会有更多类似架构的出现,这是因为数据中心要管理的节点越来越多,我们需要把任务执行分布到各节点(Agent 负责的功能)。
同时也需要对整个集群进行管理和控制(Control Plane 的功能),完全去中心化的架构是无法满足后面这个要求的。
Istio 的出现为负责的微服务架构减轻了很多的负担,开发者不用关心服务调用的超时、重试、Rate Limit 的实现,服务之间的安全、授权也自动得到了保证。
集群管理员也能够很方便地发布应用(AB 测试和灰度发布),并且能清楚看到整个集群的运行情况。
但是这并不表明有了 Istio 就可以高枕无忧了,Istio 只是把原来分散在应用内部的复杂性统一抽象出来放到了统一的地方,并没有让原来的复杂消失不见。
因此我们需要维护 Istio 整个集群,而 Istio 的架构比较复杂,尤其是它一般还需要架在 Kubernetes 之上,这两个系统都比较复杂,而且它们的稳定性和性能会影响到整个集群。
因此再采用 Isito 之前,必须做好清楚的规划,权衡它带来的好处是否远大于额外维护它的花费,需要有相关的人才对整个网络、Kubernetes 和 Istio 都比较了解才行。
## 有益参考
1. [Istio是啥?一文带你彻底了解!](http://www.uml.org.cn/wfw/201909063.asp)
\ No newline at end of file
# Controller
Controller Manager 是集群内部的管理控制中心,负责集群中的Node、Pod副本、服务端点(Endpoint)、命名空间(Namespace)、服务账号(Service Account)、资源配额(ResourceQuota)等的管理,当某个Node意外宕机时,Controller Manager会及时发现此故障并执行自动化修复流程,确保集群始终处于预期的工作状态。
![](./controlerManager.jpg)
Controller-Manager Serve用于执行大部分的集群层次的功能,它既执行生命周期功能(例如:命名空间创建和生命周期、事件垃圾收集、已终止垃圾收集、级联删除垃圾收集、node垃圾收集),也执行API业务逻辑(例如:pod的弹性扩容)。控制管理提供自愈能力、扩容、应用生命周期管理、服务发现、路由、服务绑定和提供。
Kubernetes默认提供Replication Controller、Node Controller、Namespace Controller、Service Controller、Endpoints Controller、Persistent Controller、DaemonSet Controller等控制器。
# Container Runtime
每一个Node都会运行一个Container Runtime,其负责下载镜像和运行容器。Kubernetes本身并不停容器运行时环境,但提供了接口,可以插入所选择的容器运行时环境。kubelet使用Unix socket之上的gRPC框架与容器运行时进行通信,kubelet作为客户端,而CRI shim作为服务器。
![](images/cri.png)
protocol buffers API提供两个gRPC服务,ImageService和RuntimeService。ImageService提供拉取、查看、和移除镜像的RPC。RuntimeSerivce则提供管理Pods和容器生命周期管理的RPC,以及与容器进行交互(exec/attach/port-forward)。容器运行时能够同时管理镜像和容器(例如:Docker和Rkt),并且可以通过同一个套接字提供这两种服务。在Kubelet中,这个套接字通过–container-runtime-endpoint和–image-service-endpoint字段进行设置。
Kubernetes CRI支持的容器运行时包括docker、rkt、cri-o、frankti、kata-containers和clear-containers等。
\ No newline at end of file
# Pod
## 创建Pod
创建Pod的整个流程,时序图如下:
![](images/create_pod.png)
1. 用户提交创建Pod的请求,可以通过API Server的REST API ,也可用Kubectl命令行工具,支持Json和Yaml两种格式;
2. API Server 处理用户请求,存储Pod数据到Etcd;
3. Schedule通过和 API Server的watch机制,查看到新的pod,尝试为Pod绑定Node;
4. 过滤主机:调度器用一组规则过滤掉不符合要求的主机,比如Pod指定了所需要的资源,那么就要过滤掉资源不够的主机;
5. 主机打分:对第一步筛选出的符合要求的主机进行打分,在主机打分阶段,调度器会考虑一些整体优化策略,比如把一个Replication Controller的副本分布到不同的主机上,使用最低负载的主机等;
6. 选择主机:选择打分最高的主机,进行binding操作,结果存储到Etcd中;
7. kubelet根据调度结果执行Pod创建操作: 绑定成功后,会启动container, docker run, scheduler会调用API Server的API在etcd中创建一个bound pod对象,描述在一个工作节点上绑定运行的所有pod信息。运行在每个工作节点上的kubelet也会定期与etcd同步bound pod信息,一旦发现应该在该工作节点上运行的bound pod对象没有更新,则调用Docker API创建并启动pod内的容器。
\ No newline at end of file
# Scheduler
Kubernetes Scheduler 是负责Pod调度的重要功能模块再整个系统中有“承上启下”的作用。它负责接收 Controller Manager 创建新Pod的请求,为新Pod找到一个目标Node,新Pod创建后,目标Node上的kubelet服务进程负责Pod的剩余生命周期。
具体来说,Kubernetes Scheduler 的作用是将待调度的Pod(包括Kubernetes API 新创建的Pod、Controller Manager为补足副本而创建的Pod)按照特定的调度算法和调度策略绑定到集群中某个合适的Node上,并将绑定信息写入etcd。整个调度过程涉及到三个对象:待调度Pod列表、可用Node列表以及调度算法和策略。
简单来讲,Kubernetes Scheduler 的核心工作就是通过调度算法为待调度Pod列表的每个 Pod 从 Node 列表中选择一个最适合的Node。随后,目标节点上的 kubelet 通过 API Server 监听到 Kubernetes Scheduler 产生的 Pod 绑定事件,然后获取对应的Pod清单,下载Image镜像,并启动容器。
![](../scheduler/Scheduler.jpg)
在 kube-scheduler 的启动参数中 ,`--algorithm-provider="DefaultProvider"` 用于设置调度算法,默认为 DefaultProvider 。默认调度过程如下[[1]]():
1. 预选调度过程,即遍历所有目标 Node ,选出符合要求的候选节点。为此,Kubernetes内置了多种预选策略(xxx Predicates) 供用户选择
2. 确定最优节点。在第1步的基础上,采取优选策略(xxx Prioritt)计算出每个候选节点的积分,积分高者胜出。
Kubernetes Scheduler的调度流程是通过插件方式加载的“调度算法提供者”(AlgorithmProvider)具体实现的。一个AlgorithmProvider就是包括了一组预选调度策略与一组优先选择策略的结构体,注册 RegisterAlgorithmProvider 的函数在包 `\kubernetes\cmd\kube-scheduler\app\server.go`,如下:
```
func RegisterAlgorithmProvider(name string, predicateKeys, priorityKeys sets.String) string {
schedulerFactoryMutex.Lock()
defer schedulerFactoryMutex.Unlock()
validateAlgorithmNameOrDie(name)
algorithmProviderMap[name] = AlgorithmProviderConfig{
FitPredicateKeys: predicateKeys,
PriorityFunctionKeys: priorityKeys,
}
return name
}
```
这3个参数: "name string" 为算法名,"predicateKeys" 为算法用到的预选策略集合,
"priorityKeys sets.String" 为算法用到的优选策略集合。
其中 Scheduler 可用的预选策略有:
```
CheckNodeCondition:#检查节点是否正常(如ip,磁盘等)
GeneralPredicates
HostName:#检查Pod对象是否定义了pod.spec.hostname
PodFitsHostPorts:#pod要能适配node的端口 pods.spec.containers.ports.hostPort(指定绑定在节点的端口上)
MatchNodeSelector:#检查节点的NodeSelector的标签 pods.spec.nodeSelector
PodFitsResources:#检查Pod的资源需求是否能被节点所满足
NoDiskConflict: #检查Pod依赖的存储卷是否能满足需求(默认未使用)
PodToleratesNodeTaints:#检查Pod上的spec.tolerations可容忍的污点是否完全包含节点上的污点;
PodToleratesNodeNoExecuteTaints:#不能执行(NoExecute)的污点(默认未使用)
CheckNodeLabelPresence:#检查指定的标签再上节点是否存在
CheckServiceAffinity:#将相同services相同的pod尽量放在一起(默认未使用)
MaxEBSVolumeCount: #检查EBS(AWS存储)存储卷的最大数量
MaxGCEPDVolumeCount #GCE存储最大数
MaxAzureDiskVolumeCount: #AzureDisk 存储最大数
CheckVolumeBinding: #检查节点上已绑定或未绑定的pvc
NoVolumeZoneConflict: #检查存储卷对象与pod是否存在冲突
CheckNodeMemoryPressure:#检查节点内存是否存在压力过大
CheckNodePIDPressure: #检查节点上的PID数量是否过大
CheckNodeDiskPressure: #检查内存、磁盘IO是否过大
MatchInterPodAffinity: #检查节点是否能满足pod的亲和性或反亲和性
```
其中默认的预选策略为:
>
Scheduler 可用的优选策略有:
```
LeastRequested:#空闲量越高得分越高
(cpu((capacity-sum(requested))*10/capacity)+memory((capacity-sum(requested))*10/capacity))/2
BalancedResourceAllocation:#CPU和内存资源被占用率相近的胜出;
NodePreferAvoidPods: #节点注解信息“scheduler.alpha.kubernetes.io/preferAvoidPods”
TaintToleration:#将Pod对象的spec.tolerations列表项与节点的taints列表项进行匹配度检查,匹配条目越,得分越低;
SeletorSpreading:#标签选择器分散度,(与当前pod对象通选的标签,所选其它pod越多的得分越低)
InterPodAffinity:#遍历pod对象的亲和性匹配项目,项目越多得分越高
NodeAffinity: #节点亲和性
MostRequested: #空闲量越小得分越高,和LeastRequested相反 (默认未启用)
NodeLabel: #节点是否存在对应的标签 (默认未启用)
ImageLocality:#根据满足当前Pod对象需求的已有镜像的体积大小之和(默认未启用)
```
## 有益参考
1. [k8s调度器、预选策略及调度方式](https://www.cnblogs.com/zhangb8042/p/10203266.html)
2. [k8s scheduler pod调度分析](https://blog.csdn.net/weixin_39961559/article/details/81704461)
3. [Pod Priority and Preemption](https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/)
......@@ -211,11 +211,6 @@ service中创建endpoint资源,其中一个作用就是用于service知道包
![img](../images/1024482-20190907220918209-1727058398.png)
### 5.2.3.为外部服务创建别名
除了手动配置来访问外部服务外,还可以使用完全限定域名(FQDN)访问外部服务。
......@@ -230,7 +225,7 @@ spec:
externalName: someapi.somecompany.com // 实际服务的完全限定域名(FQDN)port: - port: 80
```
  服务创建完成后,pod可以通过external-service.default.svc.cluster.local域名(甚至是external-service)连接外部服务。
服务创建完成后,pod可以通过external-service.default.svc.cluster.local域名(甚至是external-service)连接外部服务。
## 5.3.将服务暴露给外部客户端
......@@ -1032,12 +1027,6 @@ ENTRYPOINT ["tail"]
相关yml代码如下:
```
......
......@@ -193,10 +193,6 @@ kube-scheduler在启动的时候可以通过 --policy-config-file参数可以指
type FitPredicate func(pod *v1.Pod, meta PredicateMetadata, nodeInfo *schedulercache.NodeInfo) (bool, []PredicateFailureReason, error)
```
除了上面2种方式外,Kubernetes也允许用户编写自己的调度器组件,并在创建资源的时候引用它。多个调度器可以同时运行和工作,只要名字不冲突。
调度器最核心的逻辑并不复杂。Scheduler首先监听apiserver ,获取没有被调度的Pod和全部节点列表,而后根据一定的算法和策略从节点中选择一个作为调度结果,最后向apiserver中写入binding 。比如下面就是用bash编写的简单调度器:
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册