01-Kubernetes 架构原则和对象设计

一、Google Borg

(一)什么是云计算

  什么是云计算

        

 

 

   过去对于云管理或者叫作业管理主要有两条路,一个是以Openstack为典型的虚拟化平台,一个是以谷歌 borg 为典型的基于进程的作业调度平台。

  以Openstack为典型的虚拟化平台,是吧很多物理机装上Hypervisor,在上面启动一些虚拟机,再把这些虚拟机组成一个大的云平台,最终交付的是一个个的操作系统,在操作系统之上,可以部署应用系统。那么应用的部署、升级、管理都和云平台有比较明显的界限,所以像之前的云平台都会有比较清晰的基础架构的Infra as a service、平台的Platform as a service、SaaS Software as a service。这种云管平台虚拟机构建和业务代码部署分离,可变的基础架构使后续维护风险变大。

  以谷歌 borg 为典型的基于进程的作业调度平台,其调用的是一个个进程,Borg本身利用了一些容器技术,比如CGroup技术、早期使用的chroot jail 技术以及后续替换成的namespace技术去做应用之间的隔离。

(二)Google Borg

  K8S是Kubernetes的简称,其是基于谷歌的Borg系统开发的。Borg是谷歌用来管理公司内部所有作业的任务管理平台。

  Borg主要支撑两类业务,一类叫做生产业务(Production Workload),例如Gmail(邮箱服务)、Google Docs(文档服务)、Web Search(搜索服务);一类叫做离线作业(Non-prod Service),例如AI相关的、大数据相关的。生产业务要求高可用,其对资源的消耗可能并没有那么大,但是要求永远在线,而离线作业一般是做批处理的,对于资源的开销比较高,但是并不要求高可用。

  谷歌将两种业务类型混合部署,是的数据中心的资源利用率有一个本质的提升。

         

   1、Google Borg 简介特性

    物理资源利用率高。通过在线、离线混部的方式,提高了资源利用率;同时其没有使用虚拟化技术,因此不需要模拟操作系统,所有的资源都是用来做计算的,也提高了资源利用率。

    服务器共享,在进程级别做隔离。

    应用高可用,故障恢复时间短。

    调度策略灵活。

    应用接入和使用方便,提供了完备的Job描述语言,服务发现,实时状态监控和诊断工具。

    Google Borg 优势:

      对外隐藏底层资源管理和调度、故障处理等。

      实现应用的高可靠和高可用。

      足够弹性,支持应用跑在成千上万的机器上。

    Google Borg 基本概念:

      Workload:分为在线业务和离线业务。prod:在线任务,长期运行、对延时敏感、面向终端用户等,比如Gmail, Google Docs, Web Search服务等。non-prod∶:离线任务,也称为批处理任务(Batch),比如一些分布式计算服务等。

      Cell:集群管理的概念,一个 Cell 上跑一个集群管理系统Borg。通过定义 Cell 可以让Borg 对服务器资源进行统一抽象,作为用户就无需知道自己的应用跑在哪台机器上,也不用关心资源分配、程序安装、依赖管理、健康检查及故障恢复等。

      Job和Task:作业描述和调度的概念,用户以 Job 的形式提交应用部署请求。一个Job 包含一个或多个相同的Task,每个Task 运行相同的应用程序,Task 数量就是应用的副本数。每个 Job 可以定义属性、元信息和优先级,优先级涉及到抢占式调度过程。

      Naming:服务发现的概念,Borg 的服务发现通过BNS ( Borg Name Service)来实现。50.jfoo.ubar.cc.borg .google.com 可表示在一个名为cc的Cell 中由用户 uBar 部署的一个名为 jFoo 的 Job 下的第50个Task。

  2、Borg 架构

    首先有一个Cell的概念,即一个Borg集群,在一个集群内部有Borg Mater和Borg Slave,用户可以使用borg提供的命令或者浏览器向Borg Master提交一个任务,然后scheduler就会从Borg Matser中读取任务并按照一定的调度策略将其调度到Slave上,那么就会将Slave节点与Job绑定,在Slave中存在代理Boeglet会读取调度到当前节点任务并执行。

        

     Borgmaster主进程∶

      处理客户端RPC请求,比如创建Job,查询Job等。

      维护系统组件和服务的状态,比如服务器、Task等。

      负责与Borglet通信。

    Scheduler进程∶

      调度策略(Worst Fit(资源利用率最低的节点)、Best Fit(刚好满足需求的节点,例如需要两个cpu,那么就找刚好有两个cpu的节点)、Hybrid(混合模式))

      调度优化:

        Score caching:当服务器或者任务的状态未发生变更或者变更很少时,直接采用缓存数据,避免重复计算。

         Equivalence classes:调度同一Job下多个相同的Task只需计算一次。

        Relaxed randomization∶ 引入一些随机性,即每次随机选择一些机器,只要符合需求的服务器数量达到一定值时,就可以停止计算,无需每次对Cell中所有服务器进行feasibility checking。

    Borglet:Borglet是部署在所有服务器上的Agent,负责接收Borgmaster进程的指令。

  3、高可用与利用率

   应用高可用:

    prod 任务的要求就是高可用,如果资源不够时,会 kill non-prod 任务,被抢占的 non-prod 任务放回 pending queue,等待重新调度。

    多副本应用跨故障域部署。所谓故障域有大有小,比如相同机器、相同机架或相同电源插座等,一挂全挂。

    对于类似服务器或操作系统升级的维护操作,避免大量服务器同时进行。

    支持幂等性,支持客户端重复操作。

    当服务器状态变为不可用时,要控制重新调度任务的速率。因为 Borg 无法区分是节点故障还是出现了短暂的网络分区,如果是后者,静静地等待网络恢复更利于保障服务可用性。

    当某种“任务 @ 服务器”的组合出现故障时,下次重新调度时需避免这种组合再次出现,因为极大可能会再次出现相同故障。

    记录详细的内部信息,便于故障排查和分析。

    保障应用高可用的关键性设计原则∶无论何种原因,即使 Borgmaster 或者 Borglet 挂掉、失联,都不能杀掉正在运行的服务(Task)。

   系统自身高可用

    Borgmaster组件多副本设计。

    采用一些简单的和底层(low-level)的工具来部署Borg系统实例,避免引入过多的外部依赖。

    每个Cell的Borg均独立部署,避免不同Borg系统相互影响。

   资源利用率:

    通过将在线任务(prod)和离线任务(non-prod,Batch)混合部署,空闲时,离线任务可以充分利用计算资源;繁忙时,在线任务通过抢占的方式保证优先得到执行,合理地利用资源。

    98%的服务器实现了混部。

    90%的服务器中跑了超过25个Task和4500个线程。

    在一个中等规模的Cell里,在线任务和离线任务独立部署比混合部署所需的服务器数量多出约 20%-30%。可以简单算一笔账,Google 的服务器数量在千万级别,按 20% 算也是百万级别,大概能省下的服务器采购费用就是百亿级别了,这还不包括省下的机房等基础设施和电费等费用。

  4、Brog调度原理

        

  隔离性:

    安全性隔离:

      早期采用Chrootjail,后期版本基于Namespace。

    性能隔离:

      采用基于Cgroup的容器技术实现。

      在线任务(prod)是延时敏感(latency-sensitive)型的,优先级高,而离线任务(non-prod,Batch)优先级低。

      Borg通过不同优先级之间的抢占式调度来优先保障在线任务的性能,牺牲离线任务。

      Borg将资源类型分成两类:可压榨的(compressible),CPU是可压榨资源,资源耗尽不会终止进程;不可压榨的(non-Compressible),内存是不可压榨资源,资源耗尽进程会被终止。

二、Kubernetes 基础架构

(一)Kubernetes 概述

  1、什么是Kubernetes(K8S)

    Kubernetes是谷歌开源的容器集群管理系统,是Google多年大规模容器管理技术Borg的开源版本,主要功能包括:

      基于容器的应用部署、维护和滚动升级;

      负载均衡和服务发现;

      跨机器和跨地区的集群调度;

      自动伸缩;

      无状态服务和有状态服务;

      插件机制保证扩展性

  2、命令式(Imperative)vs 声明式(Declarativey)

    命令式系统关注“如何做”(在软件工程领域,命令式系统是写出解决某个问题、完成某个任务或者达到某个目标的明确步骤。此方法明确写出系统应该执行某指令,并且期待系统返回期望结果。); 

    声明式系统关注“做什么”(在软件工程领域,声明式系统指程序代码描述系统应该做什么而不是怎么做。仅限于描述要达到什么目的,如何达到目的交给系统。)

    声明式可以保证幂等性,因为状态固定,每次我要你做事,请给我返回相同结果;同时声明式是面向对象的,把一切抽象成对象。

  3、Kubernetes:声明式系统

    Kubernetes的所有管理能力构建在对象抽象的基础上,核心对象包括:

      Node∶计算节点的抽象,用来描述计算节点的资源抽象、健康状态等。

      Namespace∶资源隔离的基本单位,可以简单理解为文件系统中的目录结构。

      Pod∶用来描述应用实例,包括镜像地址、资源需求等。Kubernetes中最核心的对象,也是打通应用和基础架构的秘密武器。

      Service∶服务如何将应用发布成服务,本质上是负载均衡和域名服务的声明。

(二)Kubernetes架构

  1、架构简述

    Kubernetes采用与Borg类似的架构,从节点上有master 和 worker节点,从组件上有 API Server、Controller、scheduler、etcd、kubelet。

        

    如上图所示,是一个Kubernetes的Cluster,集群中分为管理节点 Mater Node 和工作节点 Worker Node,master 节点中有 API Server、scheduler、controllers,worker 节点中kubelet。

    API Server类似一个 Rest Server,即接收 rest 请求,无论是通过命令行还是通过浏览器的输入,最终都会以命令的形式被 API Server 接收,然后 API Server 经过鉴权认证后,会将命令存储到 etcd 数据库,etcd 可以在访问数据的时候添加一个 watch,一旦 watch 的数据有变化,etcd 会将其封装为一个事件通知给客户端,因此 etcd 还充当了类似与消息队列的角色,也就是说 API Server 会将请求推送给 etcd,etcd也会将对象的变更推送给 API Server。那么 API Server 可以接收客户端或者各个组件的请求外,还可以把对象变更推送出去,这样整个系统就可以联动起来了。也就是说其他的组件不互相通信,所有的组件都将请求发送给API Server,API Server 做认证、安全、校验后把数据推送到 etcd,同时 API Server 会将任何对象的变化推送到其他组件,通过消息的机制来驱动整个系统的变化。

    在 worker 节点上有 kubelet,其会定时上报 worker 节点状态到 API Server,API Server会将状态存入 etcd 数据库,同时 scheduler 就能接收到这些变化的请求,也就是说 scheduler 是知道整个集群中 worker 节点的使用情况。

    用户创建一个调度请求(创建 pod )给 API Server,那么 API Server 会将请求发送 scheduler,scheduler会根据请求的内容(创建几个 pod,占用多少资源)以及 worker 节点的资源利用率选择合适的节点就行调度,也就是将 pod 和调度的节点通知 API Server,然后 API Server 把 pod 中的 node name 信息存储到 etcd 中。

    kubelet 从 API Server 中获哪个 pod 和自己的节点有关,如果有关,就会进行任务处理,创建一个pod。

    除了上面的流程外,还有一个 Controllers,其是对其他组件的一个管理,例如配置一个 Service 的域名配置和负载均衡等。

   2、主要组件

        

    对上面的架构稍微展开一点看一下细节:

      API Server 实际上就是一个 Rest Server,在其中注册了一些对象的 handler,当要操作一个对象的时候,实际上是将其转化成了 Rest 的调用,并由 API Server 接收,其实 API Server 就类似于整个集群的网关,那么对于任何的网关来说,都有很多附加的功能,例如认证(知道你是谁,确认客户端是合法的)、鉴权(是否有操作的权限)、准入(请求是否合法)等。

      Controller Manager,其控制了一堆管理器,例如 Deployment 控制器、Replica Set 控制器、Nodelifecycle 控制器等,其是让整个集群的大脑,是让整个集群运行起来的核心。

      Kubelet:主要做两件事,上报节点的资源使用情况,维护 pod 的生命周期。

      proxy(worker node):当需要发布一个服务(Service)的时候,需要由 proxy 来配置负载均衡。

      cAdvisor:在每一个 pod 里面,在容器世界里,都是使用 Container 的 RuntimeService 去起的应用,其都是使用标准的 CNI 的接口,都有统一的 Cgroup 的配置,Namespace 的配置,那么就使得容器的配置标准化,那就会让容器的监控更简单。因此在每一个 Kubernetes 中都有一个 cAdvisor 的组件,用来收集容器进程的一些健康状况,例如资源利用量(用了多少 CPU,用了多少内存等),这样就可以使用容器技术、以很低的成本来构建一套监控体系,包括对资源使用量的监控,健康状态的监控等,这样就使得整个云平台标准化了。

  3、Kubernets主节点(Master Node)

    API服务器(API Server):这是Kubernetes控制面板中唯一带有用户可访问API以及用户可交互的组件。API服务器会暴露一个RESTful的Kubernetes API并使用JSON格式的清单文件(manifestAPIServer files)。

    集群的数据存储(ClusterDataStore):Kubernetes 使用“etcd”。这是一个强大的、稳定的、高可用的键值存储,被Kubernetes用于长久储存所有的API对象。

    控制管理器(ControllerManager):被称为“kube-controller manager”,它运行着所有处理集群日常任务的控制器。包括了节点控制器、副本控制器、端点(endpoint)控制器以及服务账户等。

    调度器(Scheduler):调度器会监控新建的pods(一组或一个容器)并将其分配给节点。

    这四个是核心组件,如果这些组件不起来,整个集群是不能工作的。

        

  4、Kubernetes工作节点(Worker Node)

    Kubelet:负责调度到对应节点的Pod的生命周期管理,执行任务并将Pod状态报告给主节点的渠道,通过容器运行时(拉取镜像、启动和停止容器等)来运行这些容器。它还会定期执行被请求的容器的健康探测程序。

     Kube-proxy:它负责节点的网络,在主机上维护网络规则并执行连接转发。它还负责对正在服务的pods进行负载平衡。

(三)Kubernetes组件概述

  1、etcd

        

    etcd是CoreOS基于Raft开发的分布式key-value存储,可用于服务发现、共享配置以及一致性保障(如数据库选主、分布式锁等)。

    etcd 是基本的key-value存储;监听机制;key的过期及续约机制,用于监控和服务发现;原子CAS和CAD,用于分布式锁和leader选举

    查看etcd中的信息:这里说明一下,在 etcd 中 etcdctl 是其命令行工具,可以使用 etcdctl 命令来查看相关信息

# 设置别名
alias ks='kubectl -n kube-system'
# 从 kube-system 下获取 pod
ks get pod
# 通过 exec 进入 pod 内部
ks exec -it etcd-node1 sh
# 通过 etcdctl 获取 / 开头的 key,如果去掉 --keys-only 就能看基础信息
etcdctl--endpoints https://localhost:2379 --cert  /etc/kubernetes/pki/etcd/server.crt--key /etc/kubernetes/pki/etcd/server.key--cacert/etc/kubernetes/pki/etcd/ca.crt get--keys-only--prefix/

     如果想监听对象变化,可以使用 watch 来进行监听,一旦命令执行,那么这个watch就会一直存在。

etcdctl--endpoints https://localhost:2379--cert /etc/kubernetes/pki/etcd/server.crt--key /etc/kubernetes/pki/etcd/server.key--cacert/etc/kubernetes/pki/etcd/ca.crt watch--prefix /registry/services/specs/default/mynginx

  2、APIServer

        

    Kube-APIServer是Kubernetes最重要的核心组件之一,主要提供以下功能:

      提供集群管理的RESTAPI接口,包括∶认证Authentication、 授权Authorization、准入Admission(Mutating&Valiating)。

      提供其他模块之间的数据交互和通信的枢纽(其他模块通过APIServer查询或修改数据,只有APIServer才直接操作etcd)。

      APIServer提供etcd数据缓存以减少集群对etcd的访问。

    APIServer展开:

      (1)APIHandler:APIServer 本质上是一个 RestServer,那么就需要注册不同对象的Handler

      (2)AuthN:认证,除了可以使用Kubernetes 自带的认证机制外,还可以使用 webhook自己定义一些认证机制。当配置了外部的认证机制后,认证请求就可以被转发到外部去,这样就可以去集成企业统一的认证平台

      (3)Rate Limit:限流

      (4)Auditing:审计,所有的操作都会生成一条日志记录

      (5)AuthZ:认证,可以使用 k8s 自带的 RBAC(role-base access control),也可以使用 webhook 自定义

      (6)Aggregator:可以像 nginx 一样做路由配置,如果 APIServer 是标准的 K8S APIServer,就会走默认的 K8S APIServer,包括Mutating、Validation等;如果是自定义的 APIServer,就会走Aggregated APIServer(自定义、独立部署的 APIServer),包括自定义的Mutating、Validation等。

      (7)Mutating:变形,可以加一些属性

      (8)Validating:验证,加完属性后再做一些验证

        

  3、Controller Manager

    Controller Manager是集群的大脑,是确保整个集群动起来的关键;

    作用是确保 Kubernetes 遵循声明式系统规范,确保系统的真实状态(Actual State)与用户定义的期望状态(Desired State)一致;

    Controller Manager是多个控制器的组合,每个Controller事实上都是一个control loop,负责侦听其管控的对象,当对象发生变更时完成配置;

    Controller 配置失败通常会触发自动重试,整个集群会在控制器不断重试的机制下确保最终一致性(Eventual Consistency)。

    稍微展开一下控制器的工作流程:

      针对任何对象,Kubernetes 有一些辅助工具 Codegenerator,可以帮助我们生成这个对象的一些访问框架、代码框架,这个框架主要就是 Controller Interface,分为 Informer 和 Lister 两部分。

      Informer 相当于去监听某个对象,任何一个对象的状态发生变化,或者对一个对象的增删改操作,Informer 都会生成一个新的 Event,控制器利用这些 frameworker 就可以去监听这些事件(AddFunc、DeleteFunc、UpdateFunc)。当监听到某个事件时,就会把事件的 key 拿出来兵放到一个队列中,模拟生产者消费者模型;消费者就是一些 worker Thread,其不停的从队列中取数据,取出来去做配置。

      Lister 相当于Kubernetes的所有对象在创建监听后在客户端创建了一个缓存,通过Lister就可以直接从缓存获取,就不需要再请求 APIServer了。

      

        

     Informer的内部机制

      SharedInformer 通过 List&Watch 去关注一个对象(List&Watch:第一,会把所有当前所有对象的List对象返回回来,第二,作为长连接,关注后面的变更)

      接口是通用的,那么对于不同的对象如何转换为统一的接口使用的,就是通过Reflector进行反射生成对象,然后把对象放入队列中

      然后就是将对象添加到 Thread Safe Store,另外如果监听到变化,informer 分配 Event 到 Handler,handler 则进行事件处理

        

    控制器的协同工作原理:

      当我们使用 kubectl apply -f nginx-deploy.yml 去更新时(可以加 -v 9 查看整体流程日志),配置文件内容:

        

 

     流程如下:

      (1)从配置文件获取 APIServer 的地址

      (2)然后会将这个请求发到 APIServer 中,APIServer 会做认证、鉴权、准入等操作,执行完之后会将请求存储到 etcd 中。

      (3)当请求存入到 etcd 后,就需要控制器进行操作了,控制器有很多种,例如 Deployment Controller,其关注的就是 Deployment对象,上面的 nginx-deploy.yml 里面是要创建一个 Deployment,因此 Deployment Controller 就监听到该事件,然后看到其是要创建一个副本集,因此就将创建 ReplicaSet 的请求发给 APIServer,APIServer 会将该请求放入 etcd 中。

      (4)ReplicaSet Controller 监听到创建副本集的任务时,就会读取请求,通过上述配置文件可以看到,需要创建一个 pod 对象,因此就会将创建 Pod 的请求发给 APIServer,APIServer 会将请求存入 etcd

      (5)调度器监听到创建 Pod 的请求后,会进行调度,选择一个合适的节点与 pod 进行绑定

      (6)节点监听到其绑定了一个新的 pod,就会进行初始化,Kubernetes 对容器的初始化已经做好了抽象,使用 CRI 抽象了初始化一个容器环境,使用 CNI 抽象了一个网络环境,使用 CSI 抽象了文件挂载。

        

  4、Scheduler:

         

    特殊的Controller,工作原理与其他控制器无差别。

    Scheduler的特殊职责在于监控当前集群所有未调度的Pod,并且获取当前集群所有节点的健康状况和资源使用情况,为待调度Pod选择最佳计算节点,完成调度。

    调度阶段分为:

      Predict∶过滤不能满足业务需求的节点,如资源不足、端口冲突等。

      Priority∶按既定要素将满足调度需求的节点评分,选择最佳节点。

      Bind∶将计算节点与Pod绑定,完成调度。

  5、Kubelet:

        

    Kubernetes的初始化系统(init system)

    从不同源获取Pod清单,并按需求启停Pod的核心组件:

      Pod 清单可从本地文件目录,给定的HTTPServer或Kube-APIServer等源头获取;

      Kubelet 将运行时,网络和存储抽象成了CRI,CNI,CSI。

    负责汇报当前节点的资源信息和健康状态;

    负责Pod的健康检查和状态汇报。

  6、Kube-Proxy

        

    监控集群中用户发布的服务,并完成负载均衡配置。每个节点的Kube-Proxy都会配置相同的负载均衡策略,使得整个集群的服务发现建立在分布式负载均衡器之上,服务调用无需经过额外的网络跳转(Network Hop)。

    负载均衡配置基于不同插件实现:userspace、操作系统网络协议栈不同的Hooks点和插件(iptables、ipvs)

  推荐的Add-ons:

    kube-dns∶负责为整个集群提供DNS服务;

    Ingress Controller∶ 为服务提供外网入口;

    MetricsServer∶提供资源监控;

    Dashboard∶提供GUI;

    Fluentd-Elasticsearch∶提供集群日志采集、存储与查询。

三、了解 kubectl

  Kubectl 命令和 kubeconfig: 

    kubectl是一个Kubernetes的命令行工具/它允许 Kubernetes用户以命令行的方式与Kubernetes交互,其默认读取配置文件〜/.kube/config。

    kubectl会将接收到的用户请求转化为 rest 调用以 rest client 的形式与 apiserver 通讯。

    apiserver的地址/用户信息等配置在kubeconfig。

apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: REDACTED
    server: https://127.0Q 1:54729
  name: kind-kind
contexts:
- context:
    cluster: kind-kind
    user: kind-kind
  name: kind-kind
current-context: kind-kind
kind: Config
users:
- name: kind-kind
  user:
    client-certificate-data: REDACTED
    client-key-data: REDACTED

  Kubectl 常用命令:

     kubectl get po -oyaml -w

      kubectl 可查看对象。

      -oyaml 输出详细信息为yaml格式。

      -w watch该对象的后续变化。

      -owide 以详细列表的格式查看对象。

  kubectl describe:

    kubectl describe展示资源的详细信息和相关Event

        

  kubectl exec: 

    kubectl exec提供进入运行容器的通道,可以进入容器进行debug操作。

        

  Kubectl logs:

    Kubectl logs可查看pod的标准输入(stdout, stderr),与tail用法类似。

        

四、深入理解Kubernetes

  云计算的传统分类:非云、IASS、PASS、SASS

        

  Kubernetes生态系统:

    Kubernetes模糊了传统云的分类,他既可以做基础架构管理(IASS),又可以做集群管理和数据管理(PASS)、同时又可以做公共服务的管理(SASS),但是如果必须要说 Kubernetes 属于哪一类传统云,实际上和 SASS 最为贴合。

        

  Kubernetes设计理念:

    Kubernetes 的设计理念主要是高可用、安全、可移植性、可扩展性。

        

  Kubernetes Master:这些组件上面已经说明,不再赘述

        

  Kubernetes 分层架构: 

    核心层:Kubernetes最核心的功能,对外提供API构建高层的应用,对内提供插件式应用执行环境。可以进行向上扩展和向下扩展向下扩展使用不同的 plugin插件让其可以运行在不同的网络、不同的存储等环境;向上扩展对不同的对象做统一处理。

    应用层:部署(无状态应用、有状态应用、批处理任务、集群应用等)和路由(服务发现、DNS解析等)。

    管理层:系统度量(如基础设施、容器和网络的度量)、自动化(如自动扩展、动态Provision等)、策略管理(RBAC、Quota、PSP、Networkpolicy 等)。

    接口层:Kubectl命令行工具、客户端SDK以及集群联邦。

    生态系统:在接口层之上的庞大容器集群管理调度的生态系统,可以划分为两个范畴:

      Kubernetes 外部:日志、监控、配置管理、Cl、CD、Workflow、FaaS、OTS 应用、 ChatOps 等;

      Kubernetes内部:CRI、CNI、CVI、镜像仓库、Cloud Provider.集群自身的配置和管理等。

          

    再细化的看一下分层架构:

      向下扩展:向下扩展主要是可以让 Kubernetse 拥有可以运行在各种环境的能力

        Container Runtimer:容器运行时插件,可以有Docker、containerd、cri-o

        Network Plugin:网络插件,早期有 kubenet,现在已经不使用,主要使用 CNI,对于 CNI 的实现有 Fannel、Calico、slim,不同的厂商、不同的需求就可以选择不同的网络插件,也可以自己去实现网络插件

        Volume Plugin:存储插件,他有 CSI,就是可以支持各种各样的存储,比如 cinder、ceph 等等

        Image Registory:镜像插件

        Cloud Provider:云提供商,比如要运行在 AWS,那么 AWS 会把很多功能跟 AWS 接口做整合,要运行再腾讯,就会和腾讯做整合

        Identity Provider:认证服务,比较早的时候有 K store 这种认证,现在可以支持 webhook 的这种认证

      向上扩展是 Kubernetes 提供的一系列扩展能力,这个扩展能力主要是接口的扩展,有了这个扩展,社区的很多能力就能被集成进来,例如 Operators、Istio等;还有为整个生态服务的,例如 Helm,其为 Kubernetes 提供了对象的抽象、模板抽象的能力,当发布一个应用的时候,需要写很多的 yml 文件,那么 Heml 会将对象中的模板进行抽取,将可变的东西抽取成一个个变量,例如版本号等,那么做版本更新的时候,只需要更改版本号这一个变量就可以了,然后他负责 rander,负责整个模板的渲染,最后创建回 Kubernetes 集群里面去。

  

   API设计原则(如何设计 API 才能让其遵循其是一个声明式系统):

    所有API都应是声明式的:

      相对于命令式操作,声明式操作对于重复操作的效果是稳定的/这对于容易出现数据丢失或重复的分布式环境来说是很重要的。

      声明式操作更易被用户使用,可以使系统向用户隐藏实现的细节,同时也保留了系统未来持续优化的可能性。

      此外,声明式的API还隐含了所有的API对象都是名词性质的/例如Service. Volume这些API都是名词,这些名词描述了用户所 期望得到的一个目标对象。

    API对象是彼此互补而且可组合的:

      这实际上鼓励API对象尽量实现面向对象设计时的要求/即〃高内聚/松耦合/对业务相关的概念有一个合适的分解,提高分解出 来的对象的可重用性。

    高层API以操作意图为基础设计:

      如何能够设计好API,跟如何能用面向对象的方法设计好应用系统有相通的地方,高层设计一定是从业务出发/而不是过早的从技术实现出发。

      因此,针对Kubernetes的高层API设计/ 一定是以Kubernetes的业务为基础出发,也就是以系统调度管理容器的操作意图为基础设计。

    低层API根据高层API的控制需要设计:

      设计实现低层API的目的,是为了被高层API使用,考虑减少冗余、提高重用性的目的/低层API 的设计也要以需求为基础,要尽量抵抗受技术实现影响的诱惑。

    尽量避免简单封装,不要有在外部API无法显式知道的内部隐藏的机制:

      简单的封装,实际没有提供新的功能,反而增加了对所封装API的依赖性。

      例如StatefulSet和ReplicaSet,本来就是两种Pod集合,那么Kubernetes就用不同API对象来定义它们,而不会说只用同一个ReplicaSet,内部通过特殊的算法再来区分这个ReplicaSet是有状态的还是无状态。

    API操作复杂度与对象数量成正比:

      API的操作复杂度不能超过。(N),否则系统就不具备水平伸缩性了。

    API对象状态不能依赖于网络连接状态:

      由于众所周知,在分布式环境下,网络连接断开是经常发生的事情,因此要保证API对象状态能应对网络的不稳定,API对象的状态就不能依赖于网络连接状态。

    尽量避免让操作机制依赖于全局状态:

      因为在分布式系统中要保证全局状态的同步是非常困难的。

  Kubernets如何通过对象的组合完成业务描述

        

  架构设计原则:

    只有APIServer可以直接访问etcd存储/其他服务必须通过Kubernetes API来访问集群状态;

    单节点故障不应该影响集群的状态;

    在没有新请求的情况下/所有组件应该在故障恢复后继续执行上次最后收到的请求(比如网络分区或服务重启等);

    所有组件都应该在内存中保持所需要的状态/ APIServer将状态写入etcd存储,而其 他组件则通过APIServer更新并监听所有的变化;

    优先使用事件监听而不是轮询。

  引导(Bootstrapping)原则:

    Self-hosting 是目标。既然 Kubernetes 的能力是保证应用的高可用,我们又希望 Kubernetes 控制平面自身的高可用,那么我们是否可以使用 Kubernetes 来管理 Kubernetes 控制平面,这个就叫做 Self-hosting,就是通过 Kubernetes 的基础组件把 Kubernetes 集群拉起来,这就是使用  kubeadmin 去加载整个集群的方式,它通过 kubelet 以静态 pod 的方式加载整个集群。(搭建集群可以使用 kubeadmin 和二进制两种方式,在初学时期,使用二进制的方式可以更好的理解 Kubernetes 中的每个服务应该怎么配置,但是在实际生产环境中不建议使用二进制,而是建议使用 kubeadmin的方式,也就是 Kubernetes on Kubernetes)

    减少依赖,特别是稳态运行的依赖。

    通过分层的原则管理依赖。

    循环依赖问题的原则:

      同时还接受其他方式的数据输入(比如本地文件等),这样在其他服务不可 用时还可以手动配置引导服务;

      状态应该是可恢复或可重新发现的;

      支持简单的启动临时实例来创建稳态运行所需要的状态/使用分布式锁或文 件锁等来协调不同状态的切换(通常称为pivoting技术);

      自动重启异常退出的服务,比如副本或者进程管理器等。

五、核心技术概念和API对象

 

  API对象是Kubernetes集群中的管理操作单元。

  Kubernetes集群系统每支持一项新功能,引入一项新技术/ 一定会新引入对应的API对象/支持对该功能的管理操作。

  每个API对象都有四大类属性:TypeMeta、MetaData、Spec、Status

  1、TypeMeta

    Kubernetes对象的最基本定义,它通过引入GKV (Group, Kind, Version)模型定义了一个对象的类型。

      Group:Kubernetes定义了非常多的对象,如何将这些对象进行归类是一门学问,将对象依据其功能范围归入不同的分组,比如把支撑最基本功能的对象归入core组,把与应用部署有关的对象归入apps组,会使这些对象的可维护性和可理解性更高。

      Kind:定义一个对象的基本类型,比如Node、Pod、Deployment等。

      Version:社区每个季度会推出一个Kubernetes版本,随着Kubernetes版本的演进,对象从创建之初到能够完全生产化就绪的版本是不断变化的。与软件版本类似,通常社区提出一个模型定义以后,随着该对象不断成熟,其版本可能会从vlalphal到v1alpha2y或者到vibetai,最终变成生产就绪版本v1。

  2、Metadata

    Metadata中有两个最重要的属性:Namespace和Name,分别定义了对象的 Namespace归属及名字,这两个属性唯一定义了某个对象实例。

    Label:顾名思义就是给对象打标签,一个对象可以有任意对标签,其存在形式是键值对。 Label定义了对象的可识别属性, Kubernetes API支持以Label作为过滤条件查询对象。

    Annotation:Annotation与Label —样用键值对来定义,但Annotation是作为属性扩展,更多面向于系统管理员和开发人员,因此需要像其他属性一样做合理归类。

    Finalizer:Finalizer本质上是一个资源锁,Kubernetes在接收某对象的删除请求时,会检查Finalizer是否为空,如果不为空则只对其做逻辑删除,即只会更新对象中的 metadata.deletionTimestamp 字段。

    Resourceversion:Resourceversion可以被看作一种乐观锁,每个对象在任意时刻都有其 Resourceversion,当 Kubernetes 对象被客户端读取以后,Resourceversion 信息也被一并读取。此机制确保了分布式系统中任意多线程能够无锁并发访问对象,极大提升了系统的整体效率

  (1)Lable:

    Label 是识别 Kubernetes 对象的标签,以 key/value 的方式附加到对象上。

    key 最长不能超过63字节,value可以为空,也可以是不超过253字节的字符串。

    Label 不提供唯一性,并且实际上经常是很多对象(如Pods) 都使用相同的 label 来标志具体的应用。

    Label 定义好后其他对象可以使用 Label Selector 来选择一组 相同Label的对象

    Label Selector 支持以下几种方式:

      等式,如 app=nginx 和 env != production;

      集合,如 env in (production, qa);

      多个label (它们之间是AND关系),如 app=nginx,env=test

        

  (2)Annotations:

    Annotations 是 key/value 形式附加于对象的注解。

    不同于 Labels 用于标志和选择对象,Annotations 则是用来记录一些附加信息,用来辅助应用部署、安全策略以及调度策略等。

    比如 deployment 使用 annotations 来记录 rolling update 的状态。

  3、Spec 和 Status:

    Spec和Status才是对象的核心。

    Spec 就是规范(Specification),是用户的期望状态,由创建对象的用户端来定义。

    Status是对象的实际状态,由对应的控制器收集实际状态并更新。

    与TypeMeta和Metadata等通用属性不同/ Spec和Status是每个对象独有的。

  常用Kubernetes对象及其分组:

        

六、核心对象概览

  1、Node

    Node是Pod真正运行的主机,可以是物理机,也可以是虚拟机。

    为了管理Pod,每个Node节点上至少要运行container runtime(比如 Docker 或者 Rkt)、Kubelet 和 Kube、proxy 服务。

  2、Namespace:

    Namespace是对一组资源和对象的抽象集合,比如可以用来将系统内部的对象划分为不同的项目组或用户组。

    常见的 pods、services、replication controllers 和 deployments 等都是属于某一个 Namespace 的(默认是 default),而 Node、persistentVolumes 等则不属于任何Namespace。

  3、Pod:

    Pod是一组紧密关联的容器集合/它们共享 PID、IPC、Network W UTS namespace,是 Kubernetes 调度的基本单位。

    Pod的设计理念是支持多个容器在一个Pod中共享网络和文件系统,可以通过进程间通信和文件共享这种简单高效的方式组合完成服务。

    同一个Pod中的不同容器可共享资源:

      共享网络Namespace;

      可通过挂载存储卷共享存储;

      共享 Security Context。

apiVersion: v1
kind: Pod
metadata:
  name: hello
spec:
containers:
  - image: nginx:1.15
  name: nginx

    如何通过Pod对象定义支撑应用运行:

      环境变量:直接设置值、读取Pod Spec的某些属性、从ConfigMap读取某个值、从Secret读取某个值

        

    存储卷:

      通过存储卷可以将外挂存储挂载到Pod内部使用。

      存储卷定义包括两个部分:Volume和VolumeMounts

        Volume:定义Pod可以使用的存储卷来源

        VolumeMounts:定义存储卷如何Mount到容器内部。

apiVersion: v1
kind: Pod
metadata:
  name: hello-volume
spec:
  containers:
  - image: nginx:1.15
    name: nginx
    volumeMounts:
    - name: data
      mountPath: /data
  volumes:
  - name: data
    emptyDir: {}

    Pod 网络:Pod的多个容器是共享网络Namespace的,这意味着:

      同一个Pod中的不同容器可以彼此通过Loopback地址访问:

        在第一个容器中起了一个服务 http://127.0.0.1。

        在第二个容器内,是可以通过httpGet http:172.0.0.1 访问到该地址的。

      这种方法常用于不同容器的互相协作。

    资源限制:

      Kubernetes通过Cgroups提供容器资源管理的功能,可以限制每个容器的CPU和内存使用,比如对于刚才创建的deployment,可以通过下面的命令限制 nginx容器最多只用50%的CPU和128MB的内存:

$ kubectl set resources deployment nginx-app -c=nginx -- limits=cpu=500m7memory=128Mi
deployment "nginx" resource requirements updated

    等同于在每个Pod中设置resources limits

apiVersion: v1
kind: Pod
metadata:
  labels:
    app: nginx
  name: nginx
spec:
  containers:
    - image: nginx
      name: nginx
      resources:
        limits:
          cpu: "500m"
          memory: "128MiH

  4、健康检查:

    Kubernetes作为一个面向应用的集群管理工具/需要确保容器在部署后确实处在正常的运行状态。

    探针类型:

      LivenessProbe:探测应用是否处于健康状态/如果不健康则删除并重新创建容器。

      ReadinessProbe:探测应用是否就绪并且处于正常服务状态/如果不正常则不会接收来自Kubernetes Service的流量。

      StartupProbe:探测应用是否启动完成,如果在failureThreshold*periodSeconds周期内未就绪,则会应用进程会被重启。

    探活方式:Exec(使用命令查看文件是否存在)、TCP socket(查看端口)、HTTP(使用健康检查 API)

    健康检查 spec

        

  5、ConfigMap:

    ConfigMap用来将非机密性的数据保存到键值对中。

    使用时,Pods可以将其用作环境变量、命令行参数或者存储卷中的配置文件。

    ConfigMap将环境配置信息和容器镜像解耦,便于应用配置的修改。

  6、密钥对象(Secret):

    Secret是用来保存和传递密码、密钥、认证凭证这些敏感信息的对象。

    使用Secret的好处是可以避免把敏感信息明文写在配置文件里。

    Kubernetes集群中配置和使用服务不可避免的要用到各种敏感信息实现登录、认证等功能,例如访问AWS存储的用户名密码。

    为了避免将类似的敏感信息明文写在所有需要使用的配置文件中,可以将这些信息存入一个Secret对象,而在配置文件中通过Secret对象引用这些敏感信息。

    这种方式的好处包括:意图明确、避免重复、减少暴漏机会。

    用户(User Account) &服务帐户(Service Account) :

      顾名思义,用户帐户为人提供账户标识,而服务账户为计算机进程和Kubernetes集群中运行的Pod提供账户标识。

      用户帐户和服务帐户的一个区别是作用范围:

        用户帐户对应的是人的身份,人的身份与服务的Namespace无关,所以用户账户是跨 Namespace 的;

        而服务帐户对应的是一个运行中程序的身份,与特定Namespace是相关的。

      当 pod 启动的时候,会默认的给 pod 一个 service account,同时会将service account 对应的 secret amount 到 pod中,在 pod 中和 apiserver 通讯的时候,pod就可以读取 service account 所对应的 secret 中的 token 和 ca,并且以该 token 去和 apiserver 进行通信,而 apiserver 就可以知道这个请求是来自于哪个 namesapce 的 serviceaccount,就可以进行权限管理。

  7、Service:

    Service是应用服务的抽象,通过labels为应用提供负载均衡和服务发现。匹配 labels 的 Pod IP 和端口列表组成 endpoints,由 Kube-proxy 负责将服务 IP 负载均衡到这些 endpoints 上。

    每个 Service 都会自动分配一个 cluster IP (仅在集群内部可访问的虚拟地址)和 DNS 名,其他容器可以通过该地址或 DNS 来访问服务,而不需要了解后端 容器的运行。

        

  8、副本集(Replica Set):

    Pod只是单个应用实例的抽象,要构建高可用应用,通常需要构建多个同样的副本,提供同一个服务。

    Kubernetes为此抽象出副本集ReplicaSet,其允许用户定义Pod的副本数,每一个Pod都会被当作一个无状态的成员进行管理,Kubernetes保证总是有用户期望的数量的Pod正常运行。

    当某个副本宕机以后/控制器将会创建一个新的副本。

    当因业务负载发生变更而需要调整扩缩容时,可以方便地调整副本数量。

        

    Deloyment 部署:

      部署表示用户对 Kubernetes 集群的一次更新操作。

      部署是一个比 RS 应用模式更广的 API 对象,可以是创建一个新的服务、更新一个新的服务、也可以是滚动升级一个服务。

      滚动升级一个服务,实际是创建一个新的 RS,然后逐渐将新 RS 中副本数增加到理想状态,将旧 RS 中的副本数减小到 0 的复合操作。

      这样一个复合操作用一个 RS 是不太好描述的,所以用一个更通用的 Deployment 来描述。

      以 Kubernetes 的发展方向,未来对所有长期伺服型的的业务的管理都会通过 Deployment 来管理。

        

  9、有状态服务集(StatefulSet):

    对于 StatefulSet 中的 Pod,每个 Pod 挂载自己独立的存储,如果一个 Pod 出现故障,从其他节点启动一个同样名字的 Pod,要挂载上原来 Pod 的存储继续以它的状态提供服务。

    适合于StatefulSet的业务包括数据库服务MySQL和PostgreSQL,集群化管理服务ZooKeeper, etcd等有状态服务。

    使用StatefulSet,Pod仍然可以通过漂移到不同节点提供高可用,而存储也可以通过外挂的存储来提供高可靠性,StatefulSet 做的只是将确定的 Pod与 确定的存储关联起来保证状态的连续性。

        

    Statefulset 与 Deployment 的差异:

      身份标识:StatefulSet Controller为每个Pod编号,序号从0开始。

      数据存储:Statefulset 允许用户定义 volumeClaimTemplates,Pod 被创建的同时,Kubernetes 会以 volumeClaimTemplates中定义的模板创建存储卷,并挂载给Pod。

      Statefulset 的升级策略不同:onDelete(因为设计数据的移动,这种方式需要手动的将数据移动后再做升级)、滚动升级、分片升级

  10、任务(Job):

    Job是Kubernetes用来控制批处理型任务的API对象。

    Job管理的Pod根据用户的设置把任务成功完成后就自动退出

    成功完成的标志根据不同的spec.completions策略而不同:单Pod型任务有一个Pod成功就标志完成、定数成功型任务保证有N个任务全部成功;

        

  11、后台支撑服务集(DaemonSet):

    长期伺服型和批处理型服务的核心在业务应用,可能有些节点运行多个同类业务的Pod,有些节点上又没有这类Pod运行;

    而后台支撑型服务的核心关注点在Kubernetes集群中的节点(物理机或虚拟机),要保证每个节点上都有一个此类Pod运行。

    节点可能是所有集群节点也可能是通过nodeselector选定的一些特定节点。

    典型的后台支撑型服务包括存储、日志和监控等在每个节点上支撑Kubernetes集群运行的服务。

  12、存储PV和PVC:

    PersistentVolume (PV)是集群中的一块存储卷,可以由管理员手动设置,或当用户创建 PersistentVolumeClaim (PVC) 时根据 Storageclass 动态设置。

    PV和PVC与Pod生命周期无关。也就是说,当Pod中的容器重新启动、 Pod重新调度或者删除时,PV和PVC不会受到影响,Pod存储于PV里的数据得以保留。

    对于不同的使用场景,用户通常需要不同属性(例如性能、访问模式等) 的PV

  13、CustomResourceDefinition:

     CRD就像数据库的开放式表结构,允许用户自定义Schema。

    有了这种开放式设计,用户可以基于CRD定义一切需要的模型,满足不同业务的需求。

    社区鼓励基于CRD的业务抽象,众多主流的扩展应用都是基于CRD构建的,比如 Istio、Knative

    甚至基于CRD推出了 Operator Mode和Operator SDK,可以以极低的开发成本定义新对象,并构建新对象的控制器。

 

posted @ 2022-11-24 01:46  李聪龙  阅读(256)  评论(0编辑  收藏  举报