首页 » 99链接平台 » 云原生时代从 0 到 1 构建 K8s 容器平台的 LB(Nginx)负载均衡体系(容器服务节点负载均衡业务)

云原生时代从 0 到 1 构建 K8s 容器平台的 LB(Nginx)负载均衡体系(容器服务节点负载均衡业务)

乖囧猫 2024-10-31 02:16:47 0

扫一扫用手机浏览

文章目录 [+]

所有业务(尤其是高并发业务)的访问必然要通过负载均衡 LB 代理层,服务端高并发系统离不开负载均衡,大中型公司下,负载均衡代理层都是有专人进行独立开发和建设的,云原生 Kubernetes 容器平台下的 LB 代理层,同样需要有专人来负责建设和维护。
那么 Kubernetes 容器平台基础下的的 LB(Nginx) 负载均衡代理层要怎么建设?和非容器平台下的 LB 建设有什么异同?建设的核心要点和当下最优的方案是什么?

相信看完本文,都会对 Kubernetes 容器平台的 LB(Nginx)负载均衡了然于心,并且可以快速深入建设 Kubernetes LB(Nginx)负载均衡体系。
还可以了解到,一个中大型公司,是如何从 0 到 1 来构建大规模 Kubernetes 容器平台的 LB(Nginx)负载均衡体系的一些非常宝贵的实战经验。

适应人群 :Kubernetes 开发者、LB 开发者、Kubernetes 基础运维人员、LB(Nginx)从业者、容器平台开发 or 架构设计人员。

云原生时代从 0 到 1 构建 K8s 容器平台的 LB(Nginx)负载均衡体系(容器服务节点负载均衡业务) 99链接平台
(图片来自网络侵删)
一,容器 LB 建设的背景

PS:如果对 Kubernetes 基本概念还不熟,那么需要先理解一下 Kubernetes,本文是针对对 Kubernetes 基本概念有一定理解的基础上来进行分析和设计。

1,初识负载均衡(LB)

负载均衡(Load Balancer,简称 LB)是指把客户端访问的流量通过负载均衡器,然后根据指定的一些负载均衡策略进行转发,最终可以均匀的分摊到后端上游服务器上,然后上游服务器进行响应后再返回数据给客户端。
负载均衡的最常见应用是充当反向代理,通过负载均衡,可以大大的提高服务的响应速度、提高并发请求、提高稳定性(防止单点故障)。

负载均衡的基本实现方案,从业界来看,一般分为软件和硬件两大类,软件负载均衡又可以分层如4层、7层负载均衡,如下:

• 硬件负载均衡• 如 F5,性能好,但是贵。
一般的互联网公司都没有采集硬件负载均衡• 软件负载均衡• 目前这两个都可以实现 4 层,但是更多的还是使用 Nginx 的 7 层功能。
• 4 层:典型的如 LVS• 7 层:典型的如 Nginx、HAProxy2,容器化下 LB 的异同点

在物理机时代,还没有容器化之前,典型的负载均衡的建设方案就是搭建一套 Nginx 集群,提供 7 层的代理;搭建一套 LVS 集群,提供 4 层代理方案。
并且同时,一般 7 层之上,都有一个 4 层代理,流量的基本流向就是 client -> LVS(4 层) -> Nginx(7层) -> server 。

在物理机这个时代,运维人员对 Nginx 的 upstream 的配置,基本都是手动添加修改各个 server,然后推送配置上线应用。
传统的物理机时代的维护方式,是基于后端 server 的 IP 基本是固定的,比如,你上线一个 WebServer 的服务,要部署到哪些机器上,这个是事先确定好的了,IP 会固定不变,不管你怎么升级,服务都还是固定在这些机器上,因此这个时代这样的维护方式,并没有太多问题,大家以往也都维护的挺和谐。

在容器化时代,基于 Kubernetes 的容器化平台下,LB 的建设有哪些差异呢?主要分为两大块:

• 后端服务的 IP,会由于集群的调度,IP 是可变的,每当你部署、升级等操作的时候,IP 都会改变,那么这个时候,我们显然不能够再继续采用原有写死 IP 的方式来进行 7 层代理的维护了。
由于服务 IP 的不确定性,我们必须要改变姿势,不能由人为填充 Nginx 的 upstream 的 server ip 的方式,只能通过动态的获取和变更,这个就需要 LB 能够主动发现后端服务并且动态更新• Kubernetes 的容器化平台下,集群内部的网络是虚拟的,虚拟网络的 IP 在集群外部是无法访问的,因此还需要解决好容器集群内外的网络互通问题。
二,容器 LB 负载均衡怎么建设1,Kubernetes 的负载均衡

Kubernetes 本身有内置一个集群内部的负载均衡方案,叫 kube-proxy,但是这个只能内部访问,并且功能稍显不足;而实际上,我们的容器平台,必须要提供集群外部访问的功能,因为你的用户(客户端)都是在集群外部。

Kubernetes 负载均衡相关的方案,包括:

• 集群内部负载均衡【内置】• Pod IP 在集群内部都是互通的,因此集群内部无需考虑网络互通问题• 每个 Node 节点上的 kube-proxy,就是集群内置的内部负载均衡的解决方案;但是只限于集群内部,并且功能有限• 集群外部负载均衡【额外添加】• 社区提供的 nginx-ingress-controller 方案可以满足需求• 云厂商的 Cloud provider 也可以满足需求• 参考 nginx-ingress-controller 的模式,自建 LB 方案

由此可见,如果是在自己 IDC 内部建设容器 LB 方案,那么只能采用自建方案 或者基于 nginx-ingress-controller 方案来建设;如果是上云的话,那么可以自建,也可以直接采用云厂商的方案。

下面所有的介绍,都是基于自建方案来设计,在 IDC 内部,我们要怎么从 0 到 1 来建设 K8s 容器的 LB 体系。

2,业务需求

业务功能需求就在于,业务(开发)使用容器 LB 体系的时候,他们会需要哪些需求,包括怎么使用、需要哪些功能、需要哪些策略,作为容器 LB 建设的开发人员,我们需要能够站在业务方的角度去考虑,如下图所示,有这些业务需求:

详细说明如下:

• 体验需求• LB 分组:这个业务非常核心,需要独立的 LB 集群,也就是 LB 代理层需要分组• 域名解析线路:如果是多集群、多 IDC,那么服务暴露的域名,要怎么解析,是全 IDC 都解析,还是只解析到某一个集群• 7 层代理的一些高级配置,如 uri 的 rewrite 规则、自定义一些特殊配置• 大部分用户:业务要暴露自己的服务只需要足够简单的配置和理解,他们不需要也不想关注服务暴露的细节,要的就是一个结果,我的服务部署了,我要暴露出去给 client 端调用• 小众用户:业务非常核心,有各种不确定因素存在,业务开发人员需要关注细节• 负载均衡代理层的常规功能需求• 要能够统计 SLA ,包括 QPS、慢请求、错误数 等• 要能够针对异常进行告警• 要能够支持常见的负载均衡算法,如轮询、最小连接、hash 等• 负载均衡代理层要能够支持超时、重试等基本功能• 负载均衡代理层还必须要能够支持对后端服务的健康检查• 基本的服务暴露:支持 4 层、7 层的代理方案,支持 7 层的 HTTP、HTTPS,也支持基本的 PATH 路由• 域名:服务暴露的时候,每个服务肯定需要有自己的域名,那么这个域名需要能够支持默认按照一定规则生成,还需要能够支持自定义域名;具体怎么选择就看业务自己的需求• 内外网的需求:有些业务是直接给 APP 调用的,那么必然需要暴露到外网;而有些业务只是需要集群内部访问,那么就暴露到内网即可;• upstream 上游(后端)服务的基本策略• 监控和统计• 负载均衡代理层的高级策略需求• 限流策略:高可用服务必须要有的功能,通过 LB 代理层进行限流,防止流量太大从而导致后端过载引发整体故障• 熔断保护机制:当服务发现异常,并且通过限流还不能解决的时候,需要能够直接熔断,也就是直接断开请求,防止影响到其他业务• 灰度放量:当业务新上线一个功能(版本迭代)的时候,首先需要进行灰度放量,然后观察,看是否满足预期,如果满足预期则继续灰度放量;如果有异常则需要马上回滚3,运维需求

我们建设的容器 LB 方案,最终是要交付给运维同学去使用的,运维必须要把控好整个公司的流量入口,LB 就是整个公司的流量入口;而且一般业务同学也没有权限去操作 LB 相关的配置。
那么,站在运维的角度来看,容器 LB 需要提供哪些功能呢?如下图所示,有这些运维需求:

详细说明如下:

• 负载均衡器的相关管理• 负载均衡器的自动化脚本部署,因为运维需要部署负载均衡器,那么怎么样能够实现更为智能的自动化脚本部署,而不是零散的各个命令去操作呢?这块依赖于我们提供的一些操作步骤和子命令,然后结合 ansible 来封装实现• 负载均衡器的扩缩容,部署完了之后,后续还可能有扩缩容需求,比如国庆期间、春节期间、大促期间,这是需要提前扩容的,那么怎么能够快速扩缩容?怎么更自动化?这块同样也是需要结合 ansible 来封装实现• 负载均衡器的分组,对运维而言,稳定性是首要的,那么线上的业务,有重要的服务,也有非重要的服务,一般而言,对重要核心的服务、流量非常大的服务,都需要单独的分组,用来进行物理上的隔离和管控• 权限管控和审计• 权限,一般而言,公司建设 Kubernetes 容器平台,都会有一套管理平台系统,所有人都是通过管理平台来操作,包括运维和开发。
如部署业务服务、上下线、LB 的操作和管理等等。
那么既然是这样,那么必须要控制好权限,不同角色有不同的操作权限,避免所有人都能够操作负载均衡的相关配置,只有管理员 或者 运维人员才能够操作• 审计,线上的所有变更,都需要有审计,方便回溯问题• 业务服务的配置操作• Nginx 负载均衡的基本配置检测,要能够通过管理平台来实现,包括基本检测和异常检测,检测通过才能执行变更• Nginx 负载均衡配置的灰度和回滚机制,灰度是说变更之前,需要先灰度 1 个 Nginx 节点,确保这次变更没有问题之后,才能全量变更;回滚是说如果灰度出现问题,那么需要能够快速回滚到上一个版本• Nginx 负载均衡配置的基本查看、搜索;可以全局管理所有配置;可以搜索关键字来快速定位配置• 稳定性的相关操作(流控)• 业务限流,当业务流量过大之后,根据实际情况进行限流,避免打满后端服务• 灰度放量,业务更新之前需要一个灰度逐步放量的过程• LB 系统和域名管理系统打通• 中大型公司而言,都会有内部的域名管理系统,每个服务都会有一个对外暴露的域名来访问,那么域名管理系统必须要和 LB 系统打通并且联动起来,形成一个完整的操作链。
这就需要用户暴露一个服务的时候,并不用事先申请域名,直接在 LB 系统这里进行申请即可。
4,基本方案和基本原则

Kubernetes 下,后端服务都是 Pod 的形态,Pod 要能够实现对外的负载均衡,就必须要成为 nginx 的 upstream。
而 Pod 的 IP 是随时都可能变化的,为此,就需要一个 Nginx-Controller 来动态发现 Pod,然后渲染为 nginx 的 upstream;Nginx-Controller 就是一个 Nginx 再加上一个 Controller(发现 Pod 并渲染为 upstream)。

所以,就需要我们能够自研一个 Nginx-Controller 组件来实现了,那么这个 Nginx-Controller 有些什么要求 ?

A,集群内外的网络要能互通

基本要求就是:

• 集群内,Nginx-Controller 要能够将流量分发给 Pod• 需要将 Nginx-Controller 纳入到 Kubernetes 的节点中,也就是部署 Nginx-Controller 的机器必须是 Kubernetes 的 Node 节点• 集群外,外网的请求要能够转发到 Nginx-Controller 中• 这就需要部署 Nginx-Controller 的机器能够和外部互通,一个最简单的方式就是,Nginx-Controller 采用二进制部署,使用 Node 主机的网络,这样就可以了• 因为 Node IP 是互通的,只有 Pod IP 不互通B,动态发现 Pod 并且渲染为 nginx 配置

首先,我们需要能够 watch 到 Pod、Service、 Endpoints 等资源的变化,这个就需要和 K8s API Server 交互,一般我们现在都是使用 Golang 语言来实现,因此可以基于官方的 client-go 来实现

在这,我们需要提供一套统一的模板配置,方便业务配置,然后自动渲染。
因为 Nginx-Controller 要 watch 的业务服务资源是未知的,随时可以增加或者删除,那么最好能够有一套模板机制来实现,对于 Golang,可以通过 Golang 的 template包来封装模板的实现,结合模版和当前 Service、Endpoints 的情况,渲染成对应的 nginx 配置。
比如:

upstream test-api { {{ k8sBuildUpstream "default.test-back" "port=8080" "max_fails=3" "fail_timeout=3s" }}

会渲染成相应服务的节点列表和端口:

upstream test-api { server 10.1.1.7:8080 max_fails=3 fail_timeout=3s; server 10.1.1.9:8080 max_fails=3 fail_timeout=3s; }C,实现灰度、全量、回滚的机制

Nginx-Controller 虽然可以动态渲染 nginx 配置了,但是作为线上服务,必须需要有灰度、全量、回滚的机制。

因为我们的容器 LB 是需要分组的,每一组 LB 也都会有多个 nginx 节点,灰度就是指,我们的配置要发布,首先灰度一个节点,确保这个节点 OK 之后,再灰度到下一个 nginx 节点,或者可以全量到所有 nginx 节点。
回滚则是指当我们灰度一个节点之后发现有问题,则回滚这个节点的配置。

怎么实现呢?可以通过两个 configmap 来解决灰度和全量更新的问题,configmap-canary 这个作为灰度的 configmap,并且通过 annotation 来标记哪些是要灰度的 nginx 节点的 IP,这样 nginx controller 如果识别到configmap-canary 里面的变化,则通过 annotation 的 IP 来判断是否是本节点的,如果是本节点的则渲染配置并且 reload nginx,从而生效,如果不是本节点的,那么则丢弃。
当要全量的时候,则:

• 首先,将所有的全量节点追加到 configmap-canary 的annotation["ip"]字段中,nginx-controller 读取该字段,匹配ip字段,匹配节点更新配置• 然后,如果确保已经全量成功,那么则先将 configmap-canary 的内容覆盖到 configmap-release 中,然后再清空 configmap-canary 中的 IP 列表;这样就可以完成整个灰度和全量的过程。

如果灰度的时候,发现异常了,需要回滚,那么直接清空 configmap-canary 中的 IP 列表;然后再回滚到上一个版本后,重新再走一遍发布流程来完成回滚操作

D,容器 LB 组件本身的管理和部署

上面说到容器 LB 组件本身(Nginx-Controller)需要二进制部署到 Node 主机上,那么要合理的管理这种二进制部署的需要一直运行的程序,一个较常见并且优雅的姿势就是通过 systemd 来管理。
示例配置如下:

[Unit]Description=nginx-controller daemonDocumentation=/www/nginx-controller/bin/nginx-controller -hAfter=nginx.serviceWants=nginx.service[Service]Type=simpleExecStart=/www/nginx-controller/bin/nginx-controller --slow-start=true --is_dynamic=true ${OPTIONS}ExecStop=/bin/kill -SIGTERM $MAINPIDExecReload=/bin/kill -HUP $MAINPIDKillSignal=SIGQUITRestart=on-failureRestartSec=3s[Install]WantedBy=multi-user.target

只要将这个配置放到 /usr/lib/systemd/system/ 中,systemd 就可以管理起来了。

E,各种统计和监控

Nginx-Controller 代理层所需的监控包括如下:

• 进程的监控• 进程是否存活、是否出现 panic 等• 日志监控• 日志首先要采集,然后要对错误日志进行监控,可以使用 ELK• 基本指标监控• Nginx-Controller 的一些基本指标监控,可以使用 Prometheus• 比如 reload 次数、更新次数、更新是否失败 等。



• LB 所在主机的机器性能监控• CPU:idle、system、user 等指标• 网卡软中断• 网络带宽:流入和流出带宽指标、网卡丢包指标• 内存使用、swap 使用• 磁盘 IO:读、写两方面• 剩余句柄数• LB 代理层的基本业务指标监控• SLA• 错误统计• 延迟统计• 域名维度、path 维度等三,容器 LB 体验优化(LB 架构产品设计)1,初期的架构图

我们既然是从 0 到 1 来构建 K8s 的 负载均衡体系,那么初期必然是需要从物理机转向容器,一般的选择是为了能够保证项目可以正常实施,容器 LB 这块的抉择,会结合着运维同学的一些习惯、可接受性以及更少的改动、更高的稳定性来做一些架构上的取舍。

没有容器化之前,7 层代理的架构一般是 client -> CDN -> LVS -> 物理机 Nginx -> server ;

为了满足上述诉求,在容器化之初,容器 LB 可能还不稳定,需要逐步导量过来,因此整体架构会是client -> CDN -> LVS -> 物理LB -> 容器LB(Nginx-Controller) -> POD ,如下:

LVS 和 Nginx 都需要做高可用,因此:

• LVS 就是通过 keepalive 本身来做高可用,并且 LVS 需要配置万兆网卡,因为所有流量都要经过 LVS。
• Nginx 的高可用和高并发就是建立一组 Nginx(多个 Nginx 实例),然后挂到 LVS 下面做心跳检测和流量分发• LVS 4 层代理可以对 Nginx 做检测来保证高可用• LVS 4 层代理可以基于 4 层做流量分发到 Nginx 上• 容器 LB(Nginx-Controller) 和 Pod 的网络需要能够互通,因此 容器 LB 也需要建立在 Kubernetes 集群之内,在同一个网络架构下• Kubernetes 容器平台的网络可以选择 Calico2,最优的架构图

在项目中后期,容器 LB 倾向稳定之后,那么我们要考虑的就是性能问题、成本问题、体验问题了,为此,架构需要逐步演进。

• 首先,物理机 Nginx 的存在,会导致多了一层链路• 增加响应耗时• 增加配置管理的复杂度• 增加问题排查的链路分析• 增加机器成本• 其次,Nginx-Controller 这个方案,有更优的替代方案,那就是 nginx-ingress-controller

整体的最优的架构流向就是: client -> CDN -> LVS -> Nginx-Ingress-Controller -> Pod

Nginx-Ingress-Controller 的具体介绍在后面章节进行分析。

3,体验优化优化 1:实现动态 upstream,减少 Nginx Reload 带来的 502

为何需要支持动态 upstream 呢?这是因为,在 K8s 下,服务的 Pod IP 会经常改变,比如每次发布更新的时候 Pod IP 都会变化,这也就意味着,nginx 的 upstream 的 server 列表会经常改变,那么每次 IP 有变化的时候,nginx 都需要 reload 的话,那么在线上高并发、大流量的场景下,长连接的服务会经常在 nginx reload 的时候出现 502,这个是不能接受的,非常影响业务的 SLA

那么为何长连接的服务会经常在 nginx reload 的时候出现 502 呢?这个要重点分析下 nginx 在进行 reload 的时候,对于老连接是怎么处理的,一个确定的流程是:

• 如果当前连接是空闲状态,那么直接关闭• 如果当前连接还在等待 upstream response,那么会等待请求处理结束或者超时 (proxy_read_timeout),再关闭

这一过程对于短连接的请求,是挺合理的,表现也挺正常的。
但是对于长连接场景,nginx 有些处理不好的地方。
对于长连接请求,nginx 在处理完最后一个请求,返回 response 的时候,他依然是返回 Connection: keepalive 的 response header。
这样就会导致会有一个时间窗口差,在 nginx 对于这个连接进行 close 以及到 Linux 内核完整 close 这个连接,并且发出 FIN 到 client 这个时间段内,client 端如果是高并发的场景,那么由于是长连接,因此很也可能会继续复用这个连接来发起新的请求给 Nginx,这样 Nginx 机器所在的 Linux 内核看到对于一个已关闭的连接还有新的请求,那么就会直接返回 RST 包,从而导致了 client 的一些 502 的错误。

优化 2:实现 SlowStart 功能,减少 Pod 启动初期的 SLA 性能下降

SlowStart 策略,指的是,在 Pod 初次启动并且能够对外提供服务之后,刚开始给一个缓冲时间,在这个缓冲时间内,先提供小流量的请求,进行有 weight 权重的 RR 算法,只允许非常小比例的流量;这个缓冲时间之后,再开始无权重的 RR 算法。

一般而言,Pod 的 Readiness 探针是可 worker 之后,就认为这个 Pod 可以开始对外提供服务了。
但是针对某些 Java 服务,Readiness 探针 OK 后,还不能马上提供大量服务,因为 Java 需要启动 Java 虚拟机,初始化相关系统、组件;还有一些各种内存池、线程池 等初始化工作要做;而这些初始化工作在某些情况下可能需要一点耗时;或者某些情况下是有请求过来后才进行初始化,但是由于初始化需要时间,因此 Readiness 探针 OK 之后,还不能马上提供大量服务,否则在启动的时候就可能造成服务的些许不稳定,从而降低 SLA,给业务带来影响。
这个是我们实际 Java 项目所得出的结论,因为 jit 的影响,如果在低流量下完成 jit 编译,这样给一个缓冲时间,最终效果就是可以提高 SLA。
目前这个功能其实是一个规避措施,按理来说需要业务方自己解决的,因为不同的业务方可能情况也有些区别。

具体怎么实现呢?这就要结合 Kubernetes 本身机制来综合实现了。
一般 Kubernetes 中服务的部署是通过 Deployment + Service 来部署一个服务;那么这样的话,服务就可以支持 Deployment 的滚动更新的特性,通过配置MaxSurge(如 25%),MaxUnavailable(如 25%),minReadySeconds(如 30s),progressDeadlineSeconds(如 600s) 几个参数来控制滚动策略,可以实现每次滚动升级过程中新旧一起加起来的总的 Pod 数会小于等于(1+MaxSurge) desiredPods,而 available 可以的 Pod 节点数可以保证大于等于 MaxUnavailable desiredPods,新增 Pod 节点 ready 后等待最少 minReadySeconds 后成 available,整个滚动流程超过 progressDeadlineSeconds 600s 停滞则认为失败,回滚旧版本。

为此,SlowStart 的机制实现就可以利用这个特性了,如果开启了 SlowStart 功能,那么就判断 Pod 节点是否是本次更新新启动的节点,如果是新启动的的 Pod 节点则调整其 Pod 的 weight 成预设比例(一般是较小权重),当节点 ready 时间超过 MinReadySeconds 后 ,恢复 weight 成正常权重(默认:100) ,从而实现 SlowStart 慢启动。
这个机制的 SlowStart 功能实现的慢启动针对的是整个业务的 Service 级别的。
利用这个特性来判断节点是否为新增节点,总结来看需要满足的条件如下:

# 1. 节点在deployment发布周期内LatestPod.ReadyStatus.LastTransitionTime + progressDeadlineSeconds > CurTime# 2. 节点ready后未超过minReadySeconds窗口CurPod.ReadyStatus.LastTransitionTime + minReadySeconds > CurTime# 3. 当前节点初始化时间与最新节点初始化时间差值未超过minReadySeconds窗口,防止扩步长限流CurPod.InitializedStatus.LastTransitionTime + minReadySeconds > LatestPod.InitializedStatus.LastTransitionTime

如果是新增节点的话,则设置其 weight 为 100 slow-start-weight,并且设置 service 级别的触发器,在LatestPod.ReadyStatus.LastTransitionTime + minReadySeconds + 10s - CurTime 时间后恢复为默认权重( weight=100)。

优化 3:LB 配置发布和运维域名管理系统打通,减少服务暴露的流程步骤

一般的互联网公司,运维这边都会有自己的域名管理系统,开发人员可以通过提单的方式,让运维给自己的服务分配一个域名(内网、外网);然后开发人员拿到这个域名之后呢,再和自己的服务绑定,这个绑定的过程就是服务暴露的过程。
服务暴露就是指在 LB 这边建立对应的规则,然后让就可以通过这个域名来访问对应的服务了。

这个服务暴露的过程,首先需要人工提单,拿到域名后再进行手动配置,为此,如果公司有合适的机制和契机,那么应该需要将容器 LB 进行服务暴露的过程和域名管理系统打通,当业务需要进行服务暴露的时候,不再需要通过多个平台的操作来完成,只需要在容器 LB 这边的管理平台中进行服务暴露,然后内部可以自动生成域名或者自定义域名,然后自动和域名管理系统打通,然后正式生效对外提供服务。

这样的优化主要的目的就是为了提升用户体验,减少中间的人工操作环境,从而也可以进一步减少人力成本。

优化 4:移除物理机 Nginx,优化链路,降低成本

我们前面说到,在初期的时候,为了保证稳定和过渡,还是需要有物理机 Nginx 的存在,物理机 Nginx 的主要作用有两方面:

• 其一,可以通过物理机 Nginx 这一层来对容器 LB 的流量进行灰度放量,同时可以能及时回滚• 其二,整个公司的业务服务,会有很多依然部署在物理机上,初期只会有小部分服务会开始逐步往容器进行迁移,因此物理机 Nginx 还必须要保留

但是在项目中后期,容器 LB 会逐步趋于稳定,此时,就需要逐步移除物理机 Nginx,直接是 LVS 到容器 LB,但是移除物理机 Nginx 需要有大量的工作要去梳理,因为物理机 Nginx 的配置是手动配置的,可能有很多差异化、特性化的配置。

优化 5:采用 nginx-ingress-controller 方案,减少 nginx 配置的干预,一步到位

前面说到 nginx-ingress-controller 可以作为最优方案来替代 Nginx-Controller, nginx-ingress-controller 产生的主要目的就在于能够将 Kubernetes 中的 Service 所代理的 Pod 服务暴露在 Kubernetes 集群之外,这样就能够打通集群内外的访问问题,通过 ingress 可以直接进行七层的负载均衡,并且可以对外访问,同时减少了一些复杂的配置。

因此,请求流程 client -> LVS VIP -> ingress-controller -> 业务 POD

具体的 nginx-ingress-controller 方案参看下面最后的说明。

四,容器 LB 开发设计的核心考量点

容器 LB 开发设计的核心考量点有如下:

详细说明如下:

1,支持动态 upstream 的实现【非常重要】

K8s 容器平台下,业务服务的 Pod 的是动态变化的,比如再每次重新部署、滚动升级、被驱逐重建等情况之后, Pod 的 IP 都是会发生改变。
每次 Pod IP 改变,那么就意味着 Nginx 的 upstream 发生了变化,如果没有实现动态 upstream,那么将会导致每次 Pod IP 变化,Nginx 都需要进行异常 Reload 操作。
在线上大规模集群下,如果业务的 QPS 请求很高,Nginx 频繁 Reload 会导致 client 端的长连接请求在 Nginx Reload 的时候出现 502,这样将降低业务的 SLA,故而无法提供高可靠的服务保障。

故而,只要我们实现了动态 upstream,比如基于 lua 模块的实现,那么不管后端 Pod IP 如何变化,Nginx 后端 upstream 的 IP 将会通过 lua 共享内存传递并进行负载均衡,因此 Nginx 将不会进行 Reload,从而会大大提高 SLA 服务质量。

2,支持后端 pod 的健康检查

Pod 本身,K8s 的 kubelet 会做健康检查,那么容器 LB 层面为何还需要对 Pod(业务服务)做健康检查呢?

• kubelet 本身可能会出现故障导致不能及时摘除异常的 Pod,因此我们不能完全信任 kubelet• 如果 Node 节点出现异常,那么 kubelet 把 pod 标记不可用,基本需要几十秒,也就是影响几十秒之后才能检测到3,SlowStart 策略

Nginx 的商业版本有支持 slow_start 功能,使用如下:

upstream backend { server backend1.example.com slow_start=30s; server backend2.example.com;}

SlowStart 策略是指配置了 SlowStart 策略的 server,在 SlowStart 时间范围内,先给一定量的流量(比如 0% - 1%),在过了 SlowStart 时间之后,再恢复 100% 的流量。

这样,在 SlowStart 时间范围内,这个 server 就可以在低流量下处理一些服务内部初期的一些事情,比如 Java 服务,可以在低流量下完成 jit 编译、完成 Java 虚拟机初始化等,这样,当过了 SlowStart 时间之后,等一切就绪在恢复 100% 的流量,可以保证服务可以对外提供更好的质量。

当前,这个是商业版本的实现,开源版本无法使用,因此就需要我们自己实现,在 K8s 下,容器 LB 的 SlowStart 功能的具体实现可以参考文章前面的说明。

4,巡检模块

巡检模块不仅仅是针对容器 LB,可以是针对所有容器基础模块,这个的目的就在于,人为模拟一些实际情况,通过巡检,把容器 LB 的各个环节都定期检测一遍。
这个在上线初期尤为重要。

巡检模块的实现至少包括如下:

• 解耦待巡检服务(利于增加不同的巡检模块)• 多久检测一次(间隔、重试)• 检测的异常定义(比如 latency、error 等)• 出现异常的处理机制(比如告警、输出日志等)

通过巡检模块,可以有如下优势:

• 首先可以保证容器 LB 出现问题能够及时发现,因为是自定义任务来检测容器 LB 的各个环节,因此大概率可以先于业务本身发现。
巡检模块出现问题之后,需要及时告警给相关人员进行处理• 然后因为巡检了容器 LB 的各个环节,因此如果巡检模块没有出现问题,那么容器 LB 的整体就基本是正常的,这个对于维护人员的信心度可以大大增强。
5,Nginx SLA 统计模块

业界用的多是 tengine 的 ngx_http_reqstat_module,如果想要更优化,可以在此基础上进行扩展,增加如下这些功能:

• 慢请求统计• 支持 http 自定义错误码(如 6xx 7xx) 等的统计• 自定义 http status 的统计• 支持以 upstream 为维度来统计6,性能压测和优化

容器 LB 必须要进行大量压测和优化,以求达到最优的性能,提供最稳定的服务。
Nginx 相关的性能压测和优化可以查看我的另外几篇文章:

• 《Nginx 实战系列之一:Nginx压测方法论和性能指标》• 《Nginx 实战系列之二:Nginx优化中在Nginx侧和Linux系统侧必须要调整优化的参数详细和最佳推荐配置》• 《Nginx 实战系列之三:NginxTCPbacklog分析优化和性能相关经验汇总》
标签:

相关文章

今年为何偏早?(暴雨水汽龙舟气候事件)

5月9日起,中国南方地区迎来今年入汛以来最强降雨。11日傍晚,中央气象台发布暴雨橙色预警,这是今年首个暴雨橙色预警,也是近10年最...

99链接平台 2024-12-11 阅读603 评论0