需求背景

面对大量用户访问,多任务执行,系统负载会持续升高,目前共有三类解决方案

  • 硬件升级:CPU升级,多核心,内存扩充,这一类纯粹靠资本,升级成本极高
  • 软件层面:采用高效率开发语言,比如C/Go/Rust等底层开发语言,可以编译为操作系统直接执行的二进制文件,不像Java/Python等解释型语言需要安装一个语言解释器,导致内存占用较大(尤其是Java吃内存很严重),同时速度也不快
  • 业务拆分与分布式:负载均衡,解决高并发访问与多任务执行问题,技术难度与硬件成本角度权衡较为均衡

负载均衡概述

将负载(前端的访问请求,后台执行的任务)进行平衡,通过负载均衡算法分摊到多个服务器上进行执行,是解决高性能,单点故障高可用,水平伸缩的终极解决方案

效果

  • 增加吞吐量,解决高并发压力(高性能)
  • 提供故障转移,解决单节点故障问题(高可用)
  • 通过添加或减少服务器数量,提供网站水平伸缩性(扩展性)
  • 网络入口安全防护,在负载均衡设备上做一些过滤,黑白名单等处理,最大程度上保证集群系统的安全性

负载均衡实现技术

负载均衡在OSI网络层次中,层级越低,性能越好,但是技术复杂度也越高

DNS负载均衡(七层负载均衡)

最早的负载均衡技术,利用域名解析实现负载均衡,在DNS服务器,配置多个DNS记录,这些记录对应的服务器构成集群,大型网站总是部分使用DNS解析,作为第一级负载均衡

比如在不同网络环境下,ping baidu.com命令执行获取到目标设备的IP是不一样的

优点

  • 简单:负载均衡工作交给DNS服务器处理,不需要专门的服务器维护
  • 性能高:可以支持基于地址的域名解析,解析成距离用户最近的服务器地址,避免长链路网络传输,加快访问速度

缺点

  • 可用性差:新增/修改DNS后,解析时间较长,域名DNS服务器发生变更后,需要等待本地DNS中域名DNS服务器的TTL缓存失效,本地DNS才会重新发起递归查询,然后全国各地DNS才能同步到最新的域名DNS服务器名称,该过程需要一到两天的时间
  • 扩展性低:DNS负载均衡的控制权在域名商,扩展性有限

Nginx HTTP负载均衡(七层负载均衡)

可以使用配置实现

根据URL、浏览器类别、语言来决定是否要进行负载均衡

优点

  • 对网络稳定性的依赖非常小
  • 承担高负载压力且稳定,在硬件不差的情况下一般能支撑几万次的并发量

缺点

  • 仅能支持httphttpsEmail协议,在适用范围上面较小

LVS负载均衡(四层负载均衡)

Linux内核集群实现的一个高性能、高可用的负载均衡服务器,具有很好的可伸缩性,可靠性和可管理性

四层负载均衡服务器在接受到客户端请求后,通过修改数据包的地址信息(IP+端口号)将流量转发到应用服务器

优点

  • 抗负载能力强,在负载均衡软件里的性能最强的,对内存和cpu资源消耗比较低
  • 工作稳定,自身有完整的双机热备方案,如LVS+Keepalived,但是项目实施中用得最多的还是LVS/DR+Keepalived
  • 无流量,LVS只分发请求,而流量并不从它本身出去,这点保证了均衡器IO的性能不会收到大流量的影响
  • 应用范围比较广,工作在OSI4层,所以它几乎可以对所有应用做负载均衡,包括http、数据库、在线聊天室等等

缺点

  • 软件本身不支持正则表达式处理,不能做动静分离
  • 实施操作复杂,技术难度高

HAProxy负载均衡(七层/四层负载均衡)

免费的负载均衡软件,可以运行于大部分主流的Linux操作系统上,HAProxy提供了四层(TCP)七层(HTTP)负载均衡能力,具备丰富的功能

优点

  • 支持TCP协议的负载均衡转发
  • 支持Session的保持,Cookie的引导,同时支持通过获取指定的url来检测后端服务器的状态
  • 支持虚拟主机的
  • 支持负载均衡策略非常多

缺点

  • 不支持HTTP cache功能
  • 重载配置的功能需要重启进程
  • 多进程模式支持不够好

IP负载均衡(三层负载均衡)

负载均衡服务器对外依然提供一个浮动IP,但集群中不同的机器采用不同的IP地址,当负载均衡服务器接受到请求之后,根据不同的负载均衡算法,通过IP将请求转发至不同的真实服务器,在网络层通过修改请求目标IP地址进行负载

优点

  • 在内核进程完成数据分发,比在应用层分发性能更好

缺点

  • 所有请求响应都需要经过负载均衡服务器,集群最大吞吐量受限于负载均衡服务器网卡带宽

链路层负载均衡(二层负载均衡)

负载均衡服务器对外依然提供一个浮动IP,配置真实物理服务器集群所有机器虚拟IP和负载均衡服务器IP地址一致,集群中不同的机器采用相同IP地址,但机器的MAC地址不一样,当负载均衡服务器接受到请求之后,通过改写报文的目标MAC地址的方式将请求转发到目标机器实现负载均衡,达到不修改数据包的源地址和目标地址,进行数据分发的目的

优点

  • 性能好

缺点

  • 配置复杂

负载均衡算法

负载均衡器通过负载均衡算法决定请求转发到哪台设备

  • 轮询Round Robin

    顺序循环将请求一次顺序循环地连接每个服务器,以轮询的方式依次请求调度不同的服务器;实现时,一般为服务器带上权重

​ 优点:服务器请求数目相同;实现简单、高效;易水平扩展

​ 缺点:服务器压力不一样,不适合服务器配置不同的情况;请求到目的结点的不确定,造成其无法适用于有写操作的场景

​ 应用场景:数据库或应用服务层中只有读的场景

  • 比率Ratio

​ 给每个服务器分配一个加权值为比例,根椐这个比例,把用户的请求分配到每个服务器

  • 优先权Priority

​ 给所有服务器分组,给每个组定义优先权,当最高优先级中所有服务器出现故障,将请求送给次优先级的服务器组,这种方式,实际为用户提供一种热备份的方式

  • 最少连接

​ 将请求分配到连接数最少的服务器

​ 优点:根据服务器当前的请求处理情况,动态分配

​ 缺点:算法实现相对复杂,需要监控服务器请求连接数

  • 最快模式Fastest

​ 传递连接给那些响应最快的服务器

  • 观察模式Observed

    连接数目和响应时间这两项的最佳平衡为依据为新的请求选择服务器

  • 预测模式Predictive

​ 利用收集到的服务器当前的性能指标,进行预测分析,选择一台服务器在下一个时间片内,其性能将达到最佳的服务器相应用户的请求

  • 动态性能分配Dynamic Ratio-APM

​ 根据收集到的应用程序和应用服务器的各项性能参数,动态调整流量分配

  • 动态服务器补充Dynamic Server Act

​ 当主服务器群中因故障导致数量减少时,动态地将备份服务器补充至主服务器群

  • 服务质量QoS

​ 按不同的优先级对数据流进行分配

  • 服务类型ToS

​ 按不同的服务类型(在 Type of Field 中标识)负载均衡对数据流进行分配

基于主机资源的系统负载均衡

前述所有负载均衡和算法策略,都是针对负载均衡器的,应用于短期执行任务(普遍是10秒或者1分钟这样短期内做出应答的服务),对于长期执行的任务,同时会消耗大量资源的负载均衡不是很适用于上面的负载均衡技术,最佳的解决方案是参考k8s pod调度策略的基于主机资源的系统负载均衡(全球最强的超大规模集群编排解决方案,有着谷歌数十年的超大规模集群运维系统经验与验证,是集群编排技术的大一统)

k8s调度器

Kubernetes 中,调度是指将Pod放置到合适的节点上,以便对应节点上的 Kubelet能够运行这些Pod

调度器通过 Kubernetes 的监测Watch机制来发现集群中新创建且尚未被调度到节点上的Pod, 调度器会将所发现的每一个未调度的Pod 调度到一个合适的节点上来运行

pod调度步骤

  1. 过滤阶段:过滤阶段会将所有满足 Pod 调度需求的Node 选出来,例如,PodFitsResources 过滤函数会检查候选Node的可用资源能否满足Pod 的资源请求,在过滤之后,得出一个Node 列表,里面包含了所有可调度节点;通常情况下,这个Node 列表包含不止一个Node,如果这个列表是空的,代表这个Pod不可调度

    执行的过滤规则

    • PodFitsHostPorts:检查Node上是否不存在当前被调度Pod的端口,如果被调度Pod用的端口已被占用,则此NodePass
    • PodFitsResources:检查Node是否有空闲资源(如CPU和内存)以满足Pod的需求
    • CheckNodeMemoryPressure:对于内存有压力的Node,则不会被调度Pod
    • CheckNodePIDPressure:对于进程ID不足的Node,则不会调度Pod
    • CheckNodeDiskPressure:对于磁盘存储已满或者接近满的Node,则不会调度Pod
  2. 打分阶段:在过滤阶段后调度器会为Pod 从所有可调度节点中选取一个最合适的Node,根据当前启用的打分规则,调度器会给每一个可调度节点进行打分,最后,kube-scheduler 会将Pod 调度到得分最高的Node 上,如果存在多个得分最高的Nodekube-scheduler 会从中随机选取一个

    打分规则

    • SelectorSpreadPriority:优先减少节点上属于同一个 ServiceReplication ControllerPod 数量
    • InterPodAffinityPriority:优先将 Pod 调度到相同的拓扑上(如同一个节点、RackZone 等)
    • LeastRequestedPriority:节点上放置的Pod越多,这些Pod使用的资源越多,这个Node给出的打分就越低,所以优先调度到Pod少及资源使用少的节点上
    • MostRequestedPriority:尽量调度到已经使用过的 Node 上,将把计划的Pods放到运行整个工作负载所需的最小节点数量上
    • BalancedResourceAllocation:优先平衡各节点的资源使用

参考阅读

负载均衡算法及方案-知乎

Nginx、HAProxy、LVS三者的优缺点-博客园

解决k8s调度不均衡问题

这应该是最全的K8s-Pod调度策略了-腾讯云

Kubernetes 调度器-k8s官方文档


龚正阳
29 声望5 粉丝

粗犷型程序员