k8s 前世

k8s 的前世是google 内部的Borg 系统,是一个作业调度平台,调度的对象是一个个进程。
在这里插入图片描述
Borg 本身也利用了容器化技术比如 Cgroups, Namespace 实现应用的隔离。

运行在线上的业务主要分为在线业务(prod)和离线业务(non-prod, Batch),比如上边的 Gmail,Google Docs 和 Web Search 就是离线业务,中间的 MapReduce 等业务就是离线业务,大数据的批处理业务一般都是离线业务。

在线业务就要求服务的稳定性,高可用,对资源的消耗一般不高,离线业务对资源的要求比较高,比如说AI 模型训练等,但是离线业务对可用性要求相对于在线业务不高,比如说一个批处理业务跑失败了,可以重试。

Borg 就是整合了整个数据中心的离线业务和在线业务,来进一步提高资源的利用率。

Borg 简介
特性:

  • 物力资源利用率高。
  • 服务器共享,在进程级别做隔离。
  • 应用高可用,故障恢复时间短。
  • 调度策略灵活。
  • 应用接入和使用方便,提供了完备的Job 描述语言,服务发现,实时状态监控和诊断工具。

优势:

  • 对外隐藏底层资源管理和调度、故障处理等。
  • 实现应用的高可靠和高可用。
  • 足够弹性,支持应用跑在成千上万的机器。

Borg 基本概念

Workload

  • prod: 在线任务
  • non-prod: 离线任务

Cell

  • 一个Cell 就是一个集群
  • Cell 对服务器资源进行了抽象,用户在使用Borg 的时候无需关心资源分配,程序安装,依赖管理、健康检查及故障恢复。

Job 和 Task

  • Job 是作业的的描述对象,用户可以通过Job 定义属性、元信息和优先级,优先级涉及到抢占式调度过程。
  • 一个Job 可以包含多个相同的Task, 每一个Task 运行相同的应用程序,Task 数量就是应用的副本数。

Naming

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

Borg 架构

在这里插入图片描述
Borg 的架构是比较经典的Master-Worker 加 Scheduler 调度的简洁架构。

Borgmaster 主进程

  • 处理客户端RPC请求,比如创建Job, 查询Job 等。
  • 维护系统组件和服务的状态,比如服务器、Task等。
  • 负责与Borglet 通信。

Scheduler 进程

  • 调度策略

    • Worst Fit,将任务调度到集群资源利用率最低的节点运行,目的就是使提高整个集群各个节点的资源利用率
    • Best Fit,根据任务指定的资源情况,分配到剩余资源最合适(最接近指定资源量)的节点,比如说一个任务需要2个CPU,刚好有个机器只剩下2个多CPU资源,那么这个任务就会被分配到该机器运行。
      这样就会导致很多任务都运行在一个节点上,那么就会出现有的节点很忙,有的节点很闲,这样就意味着可以将空闲的节点移除,降低资源成本
    • Hybrid,混合模式。
  • 调度优化

    • Score caching: 当服务器或者任务的状态未发生变更或者变更很少时,直接采用数据缓存,避免重复计算。
    • Equivalencecalsses: 调度同一个Job 下多个相同的Task 只需要计算一次。
    • Relaxed randomization: 引入一些随机性,即每次随机选择一些机器,只要符合需求的服务器数量达到一定值时,就可以停止计算,无需每次对Cell 中所有服务器进行feasibility checking.

Borglet

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

应用高可用

  • 被抢占的离线任务放回pending queue ,等待重新调度。
  • 多副本应用跨故障域部署
  • 对于类似服务器或操作系统升级的维护操作,避免大量服务器同时进行。
  • 支持幂等性,支持客户端重复操作。
  • 当服务器状态变为不可用时,要控制重新调度任务的速率。因为Borg 无法区分是节点故障还是出现了短暂的网络分区,如果是后者,等待网络恢复更利于保障服务可用性。
  • 当某种“任务 @ 服务器”的组合出现故障时,下次重新调度时需避免这种组合再次出现,因为极大可能会再次出现相同故障。
  • 记录详细的内部信息,便于故障排查和分析
  • 保障应用高可用的关键性设计原则:无论何种原因,即使Borg 进程挂掉、失联,都不能杀死正在运行的服务(Task)。

Borg 系统自身高可用

  • Borgmaster 组件多副本设计。
  • 采用一些简单的和底层的工具来部署Borg 实例,避免引入过多的外部依赖。
  • 每个Cell的Borg 独立部署,避免不同Borg 系统相互影响。

资源利用率

  • 通过将在线任务和离线任务混合部署,空闲时,离线任务可以充分利用计算资源;繁忙时,在线任务通过抢占的方式保证优先得到执行,合理地利用资源
  • 夜间用户使用在线任务比较少,资源空闲多,所以一般将离线任务在夜间运行。

隔离性

  • 安全性隔离
    • 早期使用Chroot jail, 后期版本基于 Namespace
  • 性能隔离
    • 采用基于Cgroup 的容器技术实现。
    • 在线任务是延时敏感型的,优先级高,而离线任务优先级低。
    • 通过不同优先级之间的抢占式调度来优先保障在线业务任务的性能,牺牲离线任务。
    • Borg 将资源分为两类:
      可压榨资源,CPU是可压榨资源,资源耗尽不会终止进程。
      不可压榨资源,内存是不可压榨资源,资源耗尽进程会被终止。

Borg 调度原理

作为开发人员除了编写健壮的代码逻辑,还希望自己的服务能够在资源足够的环境下运行,这个足够资源就难以判断,一般是通过压测来决定:

比如说我的服务目标是实现qps 最少为10,为了保证峰值资源足够,那么就以100作为基准进行压测,在申请服务器资源的时候就以10倍申请,这样在大多数空闲的时候,是极度浪费的。

针对这种情况,Borg做了优化,Borg 的调度在Task 申请资源的时候,并不会直接按照用户所说的资源大小去申请,而是会计算出实际使用的资源,然后再预留一点资源防止峰值,最后将剩下的多的资源回收。
在这里插入图片描述
这样在使用Borg 的时候,Task的资源就是动态变化的,而无需担心资源的浪费。

什么是kubernetes(K8S)

k8s 是google 基于 Borg 开源的容器集群管理系统,主要功能包括:

  • 基于容器的应用部署、维护和滚动升级
  • 负载均衡和服务发现
  • 跨机器和跨地区的集群调度
  • 自动伸缩
  • 无状态服务和有状态服务
  • 插件机制保证扩展性

系统一般分为声明式系统和命令式系统,命令式系统关注如何做声明式系统关注做什么

比如说使用电视遥控器切换上下台,调节音量大小就是命令式系统,你要告诉电视如何做。
使用空调遥控器将温度设置为25度,就是声明式系统,你不用关心空调该如何做,而是告诉空调做什么。

  • 声明式包括直接声明与间接声明,直接声明就是告诉你我需要什么,间接声明就是不直接告诉你我的需求,我会把我的需求放在特定的地方,请在方便的时候拿出来处理。
  • 声明式系统的好处就是幂等性,状态固定,每次我要你做事,请给我返回相同结果。
  • 声明式系统是面向对象的,把一切抽象成对象。

k8s 中的声明式
k8s 的所有管理能力构建在对象抽象的基础上,核心对象包括:

  • Node: 计算节点的抽象,用来描述计算节点的资源抽象、健康状态等。
  • Namespace:资源隔离的基本单位,可以简单理解为文件系统中的目录结构。
  • Pod: 用来描述应用实例,包括镜像地址,资源请求等。k8s 中最核心的对象,也是打通应用和基础架构的秘密武器。
  • Service:服务如何将应用发布成服务,本质上是负载均衡和域名服务的声明。

k8s 架构

在这里插入图片描述
k8s 采用与Borg 类似的架构,都是主从结构,每个worker 上都有一个 Kubelet用来管理worker 节点,master 也只能通过kubelet 与 worker 交互。

用户与K8s的交互以及k8s 间各组件间的交互都需要通过API Server, master 与 worker 节点都不能直接与etcd 交互,也只能通过API Server。

包括的主要组件如下:
在这里插入图片描述master 节点包括认证鉴权模块, REST API模块,scheduler 模块,controller 模块, 分布式存储模块etcd。

  • 用户的所有请求都只能通过该服务的REST API与k8s 交互。
  • k8s 的元数据存储使用的是分布式存储服务etcd, 这是一个强大的、稳定的、高可用的键值存储。
  • kube-controller manager 控制管理器,运行着所有处理集群日常任务的控制器。包括了节点控制器、副本控制器、端点控制器以及服务账户等。
  • 调度器Scheduler,会监控新建的pods(一组或一个容器)并将其分配给节点。

node 节点包括kubelet 模块,Proxy 模块,以及核心Pod 模块和运行在Pod 中的容器。

  • kebelet 负责调度到对应节点的Pod 的声明周期管理,执行任务并将 Pod 状态报告给主节点的渠道,通过容器运行时(拉取镜像、启动和停止容器)来运行这些容器。还会定期执行被请求的容器的健康探测程序。
  • Kube-proxy 负责节点的网络,在主机上维护网络规则并执行连接转发。还负责对正在服务的 pods 进行负载均衡。

etcd

在这里插入图片描述
etcd 是基于 Raft 协议开发的分布式k-v 存储,可用于服务发现、共享配置以及一致性保障(如数据库选主、分布式锁等)。

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

关于etcd 的操作,官方文档讲解的很清楚:https://etcd.io/docs/v3.3/demo/

API Server

在这里插入图片描述
kube-APIServer 是k8s 最重要的核心组件之一,主要提供以下功能:

  • 提供集群管理的REST API 接口,包括:
    认证:Authentication,校验身份是否合法
    授权:Authorization,校验是否有权限
    准入:Admission(Mutating & Valiating),校验是否运行执行该动作, 鉴权是层层递进的,当执行一个动作时,有权限不代表该动作在当时是合法的。
  • 提供其他模块之间的数据交互和通信的枢纽(其他模块通过APIServer 查询或修改数据,只有APIServer 才可以直接操作etcd)
  • APIServer 提供etcd 数据缓存以减少集群对etcd 的访问。

API Server 展开:
在这里插入图片描述
API Server 没有太多的逻辑,主要就是对各个请求进行权限验证:

  • APIHandler: 每一个请求都会对应一个handler 处理
  • AuthN: 认证,这里的认证可以使用外部认证服务(AuthService),这样就可以和企业的一些自己的认证相结合。
  • Rate Limit: 限流
  • Auditing: 审计,会对请求进行log 记录,为了日后的安全审查
  • AuthZ: 这里使用的是RBAC进行权限校验,可以使用k8s 自身的也可以使用外部的鉴权服务。
  • Aggregator: APIServer 接收的是REST API,它可以像Nginx 一样将不同的请求路由的不同的服务,这里支持用户自定义一些APIServer验证逻辑,当走到 Aggregator 时,如果你的请求需要走自定义的那么 Aggregator 就会将请求转发到 Aggregated APIServer
    然后最终请求到达 etcd。
  • Mutating: 对请求参数进行变形封装,方便后边的校验
  • Validating: 对请求参数进行校验

Controller Manager

Controller Manager 是集群的大脑,是确保整个集群动起来的关键,会监控APIServer。

  • 确保k8s 遵循声明式系统规范,确保系统的真实状态与用户的期望状态一致。
  • Controller Manager 是多个控制器的组合,每个 Controller 都是一个loop, 负责监听其管控的对象,当对象发生变更时完成配置。
  • Controller 配置失败通常会触发自动重试,整个集群会在控制器不断重试的机制下确保最终一致性(Eventual Consistency)

比如说我启动一个deployment 期望启动两个Pod, 但是当时集群资源不足,只能够启动一个Pod, 那么另一个Pod 就会处于未调度状态, Controller 会不断的重试,当集群资源够的时候,吊起另一个Pod。

控制器的工作流程
k8s 的任何对象是允许用户自己去做监听的,针对这些对象的访问,k8s 提供了CodeGenerator 可以帮助用户生成访问这些对象的代码框架,这个代码框架就叫Controller Interface:
在这里插入图片描述

  • Informer: 可以通过Informer 监听任何对象,当对象的状态进行变化,比如说增删改查,都会生成一个event 事件,任何的controller 都是一个生产者消费者模型,会将事件对应的key, 放在一个队列中,然后worker 去消费这些key, 进行下一步动作。
  • Lister: 会对对象的状态进行一些缓存,并且对外提供接口,这样就可以通过Lister 直接获取cache 中的对象状态,无需走APIServer

Informer 的工作机制
在这里插入图片描述

  • List & Watch 会将所有对象作为一个List 返回过来,然后保持一个长连接去监听对象的状态。
  • APIServer 传递到Informer的对象是序列化的,在Informer 中就需要用到反射,将对象反序列化为一个个对象放到队列中。
  • 每个对象状态的变更,都会分配到一个Event 进行处理,当处理完毕后,会将处理过的状态也同步更新至 Thread Safe Store, 这里的 Thread Safe Store 就是Controller Interface 中的Lister 中缓存的对象状态。

控制器的协同工作原理
在这里插入图片描述
APIServer 中有各种各样的控制器,每个控制器的分工是不同的,比如上边创建一个Deployment 就先经过多个控制器:
Deployment Controller:监听Deployment, 创建副本集。

ReplicaSet Controller: 监听副本集,创建Pod, 这个时候创建出来的Pod 是未调度状态的,需要经过调度器发现可用节点,然后再根据调度策略,将Pod 绑定至对应节点。

然后节点上的kubelet 就会加载对应Pod, 通过CRI(Container Runtime Interface) 将容器进程拉起来,通过CNI(Container Network Interface)加载对应容器网络,通过CSI(Container Storage Interface) 挂载对用容器存储,这个时候这个Pod 就处于已调度状态了。

Scheduler

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

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

调度阶段分为:
在这里插入图片描述

  • Predict: 过滤不能满足业务需求的节点,如资源不足、端口冲突等。
  • Priority:按既定要素将满足调度需求的节点评分,选择最佳节点。
  • Bind:将计算节点与Pod 绑定,完成调度。

Kubelet

在这里插入图片描述
k8s 的初始化系统(init system)

  • 从不同源获取Pod 清单,并按需求启停 Pod 的核心组件:
    • Pod 清单可从本地文件目录,给定的HTTPServer 或 Kube-APIServer 等源头获取
    • Kubelet 将运行时,网络和存储抽象成了 CRI, CNI, CSI
  • 负责汇报当前节点的资源信息和健康状态
  • 负责 Pod 的健康检查和状态汇报

Kube-Proxy

在这里插入图片描述

  • 控制集群中用户发布的服务,并完成负载均衡配置。
  • 每个节点的Kube-Proxy 都会配置相同的负载均衡策略,使得整个集群的服务发现建立在分布式负载均衡器之上,服务调用无需经过额外的网络跳转。
  • 负载均衡配置置于不同插件实现:
    • userspace
    • 操作系统网络协议栈不同的Hooks 点和插件:
      iptables
      ipvs

推荐的Add-ons

  • kube-dns: 负责为整个集群提供DNS 服务(现在已经作为k8s内置组件了)
  • ingress Controller:为服务提供外网入口
  • MetricsServer: 提供监控资源
  • Dashboard: 提供GUI
  • Fluentd-Elasticsearch: 提供集群日志采集、存储与查询
Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐