站点图标 Linux-技术共享

关于高可用负载均衡的探索

64f5917080f1bbf7a9f2b06fe7c1974b

引言

我们今天要说的是一个老生常谈的问题:负载均衡。有点运维经验的人都对这个很了解了,可你的负载均衡有没有完美呢?在微服务大行其道的今天,每个公司几十上百个服务都很常见,更不用说多条产品线并存的公司了,这么多服务如何在扩缩容的时候实现服务发现和高可用,每天频繁升级更新的时候有没有实现用户无感知?当然每个人对于完美的定义不同,我们今天要说的是指对用户友好(高可用无感)、对运维友好(高效傻瓜)、对架构友好(追溯监控)的完美状态。

核心组件

>> Rancher 1.6

>> Traefik 1.5.3

>> dnsmasq

>> ab

必备知识

背景

目前公司有 3 大产品线,十多个小产品,再加上用于运营分析的内部服务和开发测试环境的各种系统和服务,有差不多上百个子域名。运维的职责之一就是要保证这么多域名稳定准确地指向相应的服务器或服务。这些服务中大部分是 Web 服务,还有 Spring Cloud 微服务。不管是用户通过浏览器访问 Web 服务,还是微服务之间的相互调用,稳定性肯定是衡量服务的首要指标。尤其是在向 DevOps 看齐的敏捷型团队,要想在每天频繁发布上线的时候也能保证服务的稳定,就必须使用负载均衡。

困境

先看看我们测试环境的一个产品线使用 Rancher 自带的负载均衡时的效果 。

如果这个列表看着不是那么眼花缭乱,请看下图:

目前这个产品线部署了不到 50 个服务,如果这个界面看着也挺清爽,请再看看每次升级/编辑负载均衡规则的界面吧:

是不是彻底眼花缭乱了呢,这就是我之前每次维护负载均衡器的痛苦。 这里并不是在诋毁 Rancher 负载均衡的不好,只是这个管理方式在服务较少的时候还是方便的,服务很多的时候就不是那么方便了。

如果你想说谁让你把这么多服务放在一个负载均衡的,因为这是我们探索负载均衡过程中的其中一个阶段。

探索

我们最初的做法是每次新增一个 Web 服务,就先在 Rancher 中部署好服务,然后在 Rancher 负载均衡中增加一个规则,最后还要去 DNS 服务器中新增一个 A 记录或者 CNAME 记录,这样用户才可以访问这个新的服务。虽然只有 3 步,经常有服务变动的时候也很累,还能不能更简便?

后来我们总结了生产环境不同服务使用不同二级域名但主域名都相同的规律,想到把所有有规律的相同分组(比如相同产品线)的域名泛解析到指定主机,只要在 Rancher 中将 LB 也调度到那个主机,后续需要在这个分组内新增 Web 服务就只需 2 步:

1 部署好服务;

2 在 Rancher 负载均衡中新增一个规则将想要的域名指向刚才部署,不需要添加 A 记录,用户就可以访问服务了。

这样着实方便了一些,但是一段时间后,服务越来越多,域名也相应越来越多,就遇到了上面管理负载均衡器界面眼花缭乱的困扰。我就在想有没有更简便的方案可以把手动管理负载均衡这一步也省略呢?能否实现每次新增或编辑一个 Web 服务只需要部署好服务这一步就可以了呢?

后来 Traefik 进入了我的视野,他可以整合各种 KV 存储解决方案和容器编排引擎,是实现自动负载均衡的绝佳选择。Traefik 还原生支持 Rancher 的 API,可以自发现 Rancher 上部署的服务。Rancher 社区应用商店也提供了 Traefik 应用模板,按照模板部署 Traefik 服务以后,所有 Web 服务只要添加几个标签就可以自动注册到 Traefik 并且绑定好了制定的域名。再加上前面的经验,只要泛域名解析到了 Traefik 服务所在的服务器 IP,即可实现了仅仅只需一个部署操作,用户就可以使用指定域名访问服务了。

实操

实际操作前,必须要有一个搭建好的 Rancher 1.6 环境,我们下面只说 Rancher 的 Agent 主机需要以下几个服务器用来做实验,网络规划如下:

说明一点: 如果是生产环境或者需要公司外部用户访问内部网站,就需要在你的公网域名所在的 DNS 中设置相关域名解析,不需要单独部署 DNS 服务。 在公司内网部署一个独立 DNS 服务器的好处就是对公司内网用户友好,不用每个人记住枯燥的 IP 和端口了。还有一个好处就是可以实现域名拦截,比如公司内网开发测试环境想用一个花钱也搞不到的好域名,仅限内网,嘿嘿。。。

第一步,准备主机

将以上 4 台主机分别添加到 Rancher,主机名没有要求。如果已经在 Rancher 集群里了,直接编辑主机按照上面网络规划分别添加标签。注意主机已经有的标签不要随意修改或删除,以免带来未知的问题。

四个节点都添加好的主机界面截图:

第二步,部署 DNS 服务器

如果你公司内部已经有 DNS 服务器,请在内部 DNS 上设置相关域名解析,可以略过这一步。如果对网络 DNS 了解不多,也请慎重操作,很容易引起你的电脑“无法上网”。

先添加一个应用,再添加服务,这里介绍一个自带 Web 管理界面的轻量级 DNS 服务器,镜像是:jpillora/dnsmasq,端口映射添加 53 和 5380 端口,分别对应容器 53/udp 和 8080/tcp 端口。注意 53 端口不能修改,必须是 UDP 协议。5380 端口是 DNS 管理控制台,端口可以根据需要设置。如下图:

DNS 服务启动好以后,打开 DNS 管理控制台http://10.0.1.10:5380,进行 DNS 配置: 最核心的一条泛解析配置address=/.aek.com/10.0.1.10可以把 aek.com 所有的子域名解析到 10.0.1.10 这个 IP。

配置好的截图如下:

采集失败,请手动处理

https://static001.infoq.cn/resource/image/c8/ee/c8a605f090c92ff2c553db1787c0f9ee.png

下一步在各个主机和用户电脑上的设置 DNS,将主 DNS 设置为 10.0.1.10。当然前提是用户的电脑是可以 ping 通 10.0.1.10 这个 IP。如果公司有 DHCP 服务器,将 DHCP 分配的主 DNS 设置为 10.0.1.10,DHCP 管辖下的电脑重启后都会应用这个主 DNS 了。 Windows 设置 DNS 效果:

Linux 修改 DNS 命令:

 
sed -i '1 i nameserver 10.0.1.10' /etc/resolv.conf
 
 
 
复制代码
 
 

RancherOS 修改 DNS 命令:

 
sudo ros c set rancher.network.dns.nameservers [10.0.1.10]sudo reboot
 
 
 
复制代码
 
 
 

设置好以后,验证 DNS 是否生效。打开 CMD,ping aek.com 或者随便这个域名的子域名,看看解析是否都指向我们的 gateway 服务器 10.0.1.10,如下图:

第三步,部署 Traefik

关于 Rancher 部署 Traefik 服务的详细介绍,请查看 Rancher 官方教程 「Rancher 部署 Traefik 实现微服务的快速发现」,这里只简单说一下。 在社区应用商店找到 Traefik 并部署,这里我们演示简单起见只需要修改 Http Port 端口为 80,Https Port 如果用到的话就改成 443,端口配置界面如下:

为了高可用,一个重要的选项要留意,一定要启用健康检查:

其他选项暂时不需要修改,点击启动按钮启动一个 traefik 服务。 启动以后发现 443 端口并没有映射出来,估计是这个社区镜像的 Bug,如果需要 https,就升级一下 traefik 服务,添加 443 端口映射即可。

打开网址http://10.0.1.10:8000就可以看到一个清爽的 Traefik 界面了。管理界面就两个界面,一个 Providers 显示注册上来的 Web 服务,我们还没有部署 Web 服务,所以现在是空的:

还有一个界面Health显示负载均衡的健康状态,平均响应时间和状态码统计图都在这里。还有一个非常重要的统计信息就是实时 HTTP 错误列表,每次服务升级发布上线的时候,留意这里有没有忽然出现一大堆错误,你的服务架构升级是否稳定,有没有影响用户体验就体现在这里了!

至此,一个 DNS 服务,一个 Traefik 服务就部署好了,接下来我们就看看 Traefik 的神奇效果。

第四步,部署 Web 服务

Rancher 的 Traefik 教程有一个细节需要更正,可能是教程里面的 traefik 版本和最新版本不同,所以教程里面说的关于域名的配置标签 traefik.domain 和 traefik.alias 并不好用。看了 Traefik 官方文档「Traefik 配置 Rancher 后端」中的说明,经过我的实际验证,在 Rancher 中实现自动注册 Web 服务到 Traefik 需要添加以下 3 个标签:

比如我们想要使用域名http://traefik.aek.com直接访问 Traefik 的管理面板,只需要升级 Traefik 服务添加如下 3 个标签:

 
traefik.enable=truetraefik.port=8000traefik.frontend.rule=Host:traefik.aek.com
 
 
 
复制代码
 
 
 
 

这里有个技巧,Rancher 设计很人性化的地方,一次性复制下面 3 个标签,在 Rancher 服务的标签界面点击“添加标签”以后,直接粘贴,刚才复制的 3 个标签已经全部填好了,如下图:

Traefik 服务升级好以后,刷新 Traefik 的控制台,Providers 里面就会多了一组负载均衡。

这个时候你就可以打开http://traefik.aek.com直接访问 traefik 的控制面板了。

接下来我们部署一个 Web 服务,看看自动注册并使用 DNS 解析的效果,使用我写的一个方便验证负载均衡后端和服务端的 Web 镜像 zhangsean/hello-web 已经发布到 Docker Hub,容器内部暴露 80 端口,不需要添加端口映射,只需要添加以下 3 个标签,我们启动 2 个容器,以便看看有没有负载均衡的效果:

 
traefik.enable=truetraefik.port=80traefik.frontend.rule=Host:web.aek.com
 
 
 
复制代码
 
 
 
 

部署界面截图:

hello-web 服务启动成功后,查看 traefik 控制台,会发现多了一组负载均衡规则:

这时候就可以打开网址 http://web.aek.com 访问刚部署的 hello-web 服务了。多次刷新,会看到 Server Name 在两个容器名之间轮换,说明 2 个服务后端都在提供服务了。

顺便说一下,很多业务后端服务需要记录客户端真实 IP,后端应用通过 HTTP 请求头 HTTP_X_FORWARDED_FOR 或 HTTP_X_REAL_IP 即可获取客户端真实 IP,区别是前者可能包含多层代理的 IP。 关于获取跨代理的客户端真实 IP 的详细讲解,请参考:HTTP 请求头中的 X-Forwarded-For,X-Real-IP

高可用验证

我们现在通过压力测试,验证这种负载均衡的高可用效果。我们选择老牌压力测试工具 ab,容器化的 ab 镜像是jordi/ab,为了避免单次压力测试的不稳定情况,我们使用 Rancher 批量发起多组压力测试。

我们首先测试在服务后端扩缩容的时候,服务的可用情况。 新建一个应用 HA-Test,然后添加服务 ab-web,镜像指定jordi/ab,不需要映射端口,命令里填写测试命令-n 1000 -c 10 -l http://web.aek.com/使用 10 并发执行 1000 次请求测试。因为页面是活动的所以一定要加-l 参数否则会看到很多失败请求,其实是 ab 检测到页面返回长度不一致认为请求失败了。自动重启选择“从不(仅启动一次)”,网络类型选择“主机”以便减少容器内部往来对压力测试造成的影响,添加一个标签test_node=true,调度规则也添加主机标签限制必须包含test_node=true,这样保证测试容器只会运行在 node3 上不会影响后端服务所在主机的性能。

新增 ab-web 服务:

网络设置:

标签设置:

调度规则:

启动服务以后,进入服务详情,看到服务是 Started-Once,等待服务运行并自动停止,ab 测试就完成了。查看服务日志即可看到测试结果。

测试结果:

现在想再测试一次,只需要手动启动服务或者给这个服务扩容即可。多运行几次压测,把这几个重要指标放到 Excel 中进行统计分析就可以知道服务的稳定性和性能了。

以上只是压测的介绍,压测时间才持续时间 1 秒钟肯定不能说明什么,我们通过调整-n 参数来增加服务请求数,让压测持续一段时间,在此期间扩容服务,看看服务稳定性怎样。

选择 ab-web 服务,将其克隆,在新服务里面把服务名改成ab-web-1w,在命令里面把请求个数参数调整为-n 10000,启动压测服务。等服务进入 Running 状态后,把 hello-web 服务扩容到 3 个后端,确保 hello-web 扩容的后端启动成功,并且在 Traefik 控制台确认 hello-web 后端已经有 3 个 server,这个时候也可以手动刷新 http://web.aek.com 看看 Server Name 是否出现第三个容器。这时候压测应该还在进行汇总,回到 ab-web-1w 服务查看日志,等待压测结束。 可以看到没有失败请求,说明扩容期间负载均衡服务很稳定。

我们再进行来验证服务缩容的时候负载均衡是否稳定,方法同上,将ab-web-1w克隆成ab-web-1w-2,其他参数不需要修改,启动服务,等待压测服务 Running,把 hello-web 服务缩容到 2 个,在 traefik 控制台确认 hello-web 的后端只剩 2 个,查看压测服务日志,等待压测结束,确认是否有失败请求。 很庆幸,压测结果显示,没有任何失败请求!

再来看看 Traefik 控制台的健康状态,除了无法请求 favicon 出现的 404 错误(Hello-web 镜像里面没有放 favicon.ico 文件),没有其他错误。这也印证了 Traefik 服务的高可用。

通过以上简单的压测,我们基本上验证了 Traefik 在服务扩缩容的过程中任然能够保持服务的稳定高可用。 再结合 DNS 泛解析,实现了对用户友好(高可用:升级服务过程中用户无感知)、对运维友好(部署简单高效操作傻瓜方便)、对架构友好(监控服务升级过程中有无异常)的简单高可用服务。

当然生产环境要比我们这个演示环境复杂的多,随机的并发流量,不稳定的网络等等因素也都在影响着负载均衡的高可用。 Traefik 还提供了运行状态 API,可以整合到监控系统里面实现更稳定持续的监控。Traefik 自身也支持 HA 模式,避免 Traefik 单点故障。

我们公司在生产环境用阿里云 SLB 做流量接入,后面接着 Traefik 的 HA 集群做自动域名和服务发现的路由,再后端是各种 Web 服务,上线运行半年左右,一直很稳定。

以上就是今天的分享,如有不严谨的地方,请多指正!谢谢大家!

Q&A 环节

Q1:请问多个服务用了同一个 host 标签,traefik 会怎么选择转到哪个服务?

A1:服务的容器运行在哪些主机上是 Rancher 的调度服务所负责的,和 Traefik 无关。Traefik 链接的是服务,这个服务的容器不管在哪些主机上运行都会被连接过来。

补充:我们目前没有遇到多个服务配置相同域名的情况,相比只会有一个服务生效,改天试一试看。实际应用时还说要避免这种情况吧。

Q2:请问一下这个负载均衡适合哪些环境和应用?

A2:据官方压测结果显示 Traefik 可以和 Nginx 相媲美,所以 Nginx 适合的环境 Traefik 都适合。目前官方说 Traefik 只适合 HTTP 和 HTTPS 应用,不知道后期会不会支持 TCP 负载均衡。

Q3:请问你们在生产环境中除了用阿里云 slb 做流量接入,后面接着 Traefik 的 HA 集群,中间是否还用到了 nginx 做代理,如果有,一般用了多少台呢?

A3:Traefik 就相当于 Nginx 了,而且还比 Nginx 好用,我们生产环境就没有用 Nginx 了。Traefik HA 集群用了 2 台主机,自动适应的主备结构。

Q4:我想问一个跟 traefik 能访问远程的 docker 吗?docker API 可以配置认证吗?

A4:只要是使用 HTTP 协议的 API 都可以使用 Traefik 代理。

Q5:请问下 dnsmasq 怎么实现的主备?

A5:我们目前的 DNSmasq 只是用在公司内网,由于没有什么压力,所以没有考虑 DNSmasq 的高可用。生产环境使用的是域名服务商的 DNS 服务器,这种服务器想必都有高可用吧,比如阿里云的 DNS 服务器有 2 个:223.5.5.5 和 223.6.6.6

Q6:我想问一下,rancher 自带的负载均衡除了 ui 上有些痛点,性能上有什么不足么?

A6:Rancher 自带的负载均衡基于 haproxy,也是很成熟的负载均衡解决方案,本来不存在性能很差的问题,但是 Rancher 默认的内部的网络交互上存在性能损耗问题。大家可以根据今天的压测思路,部署一个 Rancher 自带的负载均衡,用压测看看性能。同时也对比一下把服务直接在主机上暴露端口访问的性能,三者之间对比,性能对比还是比较明显的。

Q7:请说下 Traefik 的 HA 集群怎么部署?

A7:Traefik 的 HA 集群是基于 consul 后端做存储,启动多台 Traefik 连接到 consul,Traefik 会自动选举一个节点作为主节点,其他做备用节点。具体教程请查看官方说明:https://docs.traefik.io/user-guide/cluster/

Q8:有尝试 lvs 加 traefik 吗?

A8:我们目前是把 Traefik 的 HA 集群的多台主机都加入到一个阿里云 SLB 的虚拟服务器组,流量接入阿里云 SLB 后,自动负载到 Traefik 的集群,然后再路由到后端服务。目前没有使用 LVS。

Q9:traefix 在 https 上部署容易吗?

A9:Traefik 部署 https 应用还是很方便的,只要添加 SSL 证书先关配置即可。还有一个很有吸引力的特性,只要配置了 ACME,就可以对 SSL 证书自动续费。对于想小范围使用免费 SSL 证书的环境非常友好。

Q10:用了阿里云的 slb 还需要自己 traefik 的高可用么?阿里的 Slb 不是支持健康检查的么,我想的是可不可以利用这个来做高可用?

A10:阿里云 SLB 是流量接入端,如果在阿里云 SLB 上配置域名路由不仅麻烦(每个域名都手动添加一条记录),而且只能配置后端到某个服务或者服务器组,这样不利于服务的高效利用呀。所以还是需要像 Traefik 这样的自动适配代理来做域名适配转发。为了避免 traefik 这个核心域名路由转发节点的单点故障,所以最好是做 traefik 的 HA 集群。当然流量小的情况下,可以先不用 HA 集群。健康检查是说如果发现某个后端不健康,就把这个后端从负载均衡中移除。如果后端只有一个 traefik,万一这个 traefik 不健康,阿里云 SLB 不就断了后路了么?所以说至少用两个 traefik 来做 HA,就是避免单点故障。

Q11:请问你们的网络是如何规划的,比如,我们有 DMZ 区对外访问,核心区放置 Server,不同应用服务分不同网段?

A11:我们阿里云环境是用专有网络的,对外都是使用 SLB 做接入的。专有网络自己可以设置网段,比如 10.8.1.0/24 是容器服务器分区,10.8.2.0/24 是数据库服务器,在公司路由器上做路由跳转,公司内网指定的电脑才可以直接访问生产服务器。不应应用分不同网段,便于管理,也便于问题定位,建议做网段划分。

Q12:dns 的 ttl 设置有讲究么?

A12:DNS 服务的 TTL 是指在 DNS 服务器上缓存时间,设置越小,DNS 的变动就可以更快在全网生效,然而运营商的 DNS 设置 TTL 都是有限制的,默认 600 秒,想要设置更短,就要购买高级服务了。

Q13:单个服务支持多个域名吗?服务的 label 怎么配置呢?

A13:单个服务支持多个域名,多个域名用英文逗号分隔即可。

Q16:目前贵司后端服务发布 有什么灰度发布的方案吗?

A16:目前使用的 Rancher 的升级机制实现的灰度发布,每个服务在至少 2 个容器的情况下,升级发布服务的时候就可以保证服务不中断,因为 Rancher 会先停止一个容器,马上启动一个容器,保证这个新容器启动成功,才会停止升级下一个容器。这个策略基本满足了灰度发布的需要。经过简单压力测试,这种服务升级发布过程中,traefik 的前端是没有遇到请求错误的。

退出移动版