Consul是一个免费的开源工具,它提供了服务发现、健康检查、负载均衡和全局分布式的键值存储。此外,它还提供了一组用于构建编排工作流和工具的原型。在微服务体系架构中,应用程序通常运行在许多IP地址上,并绑定到各种端口。服务发现有助于发现这些不同的服务,而不管它们位于何处。

由于同一服务的多个实例常常在微服务体系架构中同时运行,因此我们需要一种策略,以便在处理健康状态的更改、实例数量的更改和集群状态的更改时,均衡地平衡服务的所有健康实例的流量。这是负载均衡层的工作。本文讨论了在微服务体系架构中与Consul负载均衡的一些常见策略。

Consul Directly

与Consul负载均衡的一种方法是使用Consul的内置负载均衡功能。Consul集成健康检查与服务发现。这意味着不健康的主机永远不会从查询返回到服务发现层。在这种模式下,每当应用程序和服务希望在数据中心中找到其他服务时,它们都直接与Consul进行通信。

请考虑以下配置文件,其中包含后端服务的IP地址:

services:
  backend: 10.2.5.391

通常认为硬编码IP地址是一种不好的做法,尤其是在微服务体系结构中。随着应用程序在整个系统中移动,使这个配置文件保持最新变得很有挑战性,特别是在计算机集群中。相反,更好的方法是利用Consul的DNS发现层。

services:
  backend: backend.service.consul

正如“www.hashicorp.com”是一个web地址,“backend.service.consul”也是一样。TLD或域后缀是可配置的,但默认值是.consul。以TLD结尾的任何请求都将被解析到Consul。.service命名空间告诉Consul查找服务(与机器节点相对照)。backend 是要查找的服务的名称。请求“backend.service.consul"解析到一组IP地址,就像请求“www.hashicorp.com”解析到一组IP地址一样。然而,对于Consul,解析发生在服务发现层,并集成到健康检查机制中。

每当应用程序或内核解析这个DNS条目时,它将收到一组IP地址的随机round-robin响应,这些地址对应于集群中的健康服务。DNS接口基本上提供了与任何应用程序的零接触服务发现集成。

优点

  • 不依赖外部工具或进程
  • 不需要监视或维护其他服务
  • 默认高度可用
  • 尽可能接近实时
  • DNS很容易使用,工作量很小
  • 健康检查是分布式的,集群负载最小

缺点

  • 单点故障——即使Consul在默认情况下高度可用,如果Consul不可用或不可访问,这种模式不会提供故障转移
  • 要求直接在应用程序中使用HTTP API或进行DNS查询,并假设端口或进行两个DNS查询以查找地址和端口
  • 应用程序和Consul之间的紧耦合

Fabio

Fabio是一个开源工具,它为Consul管理的服务提供了快速、现代、零配置负载均衡HTTP(S)和TCP路由器。用户注册服务和健康检查到Consul,fabio将自动路由流量到他们,不需要额外的配置。

用户注册一个以urlprefix-开头的标签的服务,例如:

urlprefix-/my-service

在本例中,当对fabio 发出/my-service请求时,fabio将自动将流量路由到集群中的健康服务。Fabio还支持更高级的路由配置

优点

  • 与Consul的丰富整合
  • 比DNS方法更能控制负载均衡
  • 在GitHub上超过4000星的支持和采用
  • 支持TCP代理
  • 访问日志和许多其他很棒的特性
  • 集成HashiCorp Vault
  • 用于路由可视化的可选Web UI
  • 非常详细的开源文档

缺点

  • 需要额外的服务来运行和监视
  • 与Consul和Consul标签紧耦合

Nginx/HAProxy与Consul模板

Consul负载均衡的另一种方法是使用第三方工具(如Nginx或HAProxy)来平衡流量,以及使用开源工具(如 Consul Template)来管理配置。在这种模式下, Consul Template动态地管理nginx.conf或haproxy.conf配置文件,它定义负载均衡器和服务器列表。这个列表是模板化的,Consul Template作为一个服务运行,以保持模板是最新的。Consul Template与Consul集群有持续的连接,当发生对服务池的更改时, Consul Template写入一个新的配置文件,并向Nginx或HAProxy进程发送信号,以重新加载其配置。下面是一个示例Nginx Consul Template模板:

upstream myapp {
{{ range service "myapp" }}
  server {{ .Address }}:{{ .Port }}
{{ end }}
}

在本例中,Consul Template将监视所有名为“myapp”的服务。如果他们的状态发生了更改,Consul Template将渲染一个新的配置文件,并向Nginx进程发出重新加载的信号。上面的模板将渲染nginx.conf成下面这样:

upstream myapp {
  server 10.2.5.60:13845
  server 10.6.96.234:45033
  server 10.10.20.123:18677
}

需要注意的是,在这种模式下,无论是Nginx还是HAProxy都不知道Consul的存在;它们只是读取配置,就好像值是由操作符或配置管理工具硬编码的一样。

优点

  • 处理在非默认端口上运行的应用程序,而不需要额外的API请求
  • Nginx和HAProxy都是经过实战检验的工具
  • 组织可能已经拥有使用这些工具的专业知识或现有基础结构
  • 如果Consul离线,仍然有记录的最后已知的良好状态的服务
  • Consul Template还集成了Vault,如果配置文件具有TLS私钥或共享密码等机密数据,那么这是一个理想的解决方案

缺点

  • 需要两个额外的服务来管理和监控- Nginx/HAProxy和Consul Template
  • 由于阻塞查询,低效的模板会给Consul集群带来巨大的压力
  • 实践“每个容器一个服务”范式具有挑战性
  • 一个“flappy”集群(一个服务在健康和不健康之间切换的集群,或者一个持续快速变化的集群)可能会导致Nginx/HAproxy配置的不稳定性

Nginx自定义模块

最近,我制定了一个目标,试图从等式中删除 Consul Template,但要保持Nginx经过时间考验的灵活性和熟悉性。社区内有一些非常有趣和创新的方法,即:

最终,这些方法要么涉及太多的活动部件,要么包含超出Consul公司基本服务发现范围的特性。因此,我编写了ngx_http_consul_backend_module模块,该模块对每个nginx请求动态地选择上游。

就像这样:

http {
  server {
    listen       80;
    server_name  example.com;

    location /my-service {
      consul $backend service-name;
      proxy_pass http://$backend;
    }
  }
}

对于每个请求,Consul密钥告诉Nginx将委托给自定义模块并选择一个随机的健康后端,然后将请求代理给该后端。确实有需要改进的地方,但是这种概念验证表明,在没有中介工具的情况下,Nginx和Consul可以直接连接在一起。

优点

  • 处理在非默认端口上运行的应用程序,而不需要额外的API请求
  • 没有外部工具-只是运行Nginx并直接指向Consul
  • 使用官方Consul SDK客户端库提供HTTP/2、陈旧的查询以及更多开箱即用的东西

缺点

  • 需要从源代码编译Nginx以安装自定义模块
  • 每个请求调用Consul到后端(每个请求吃掉请求的RTT和Consul解决方案的RTT)
  • 需要了解Nginx定制模块的知识
  • 不与Vault集成TLS私钥或共享密码
  • 模块还没有经过实战测试

HAProxy 1.8

HAProxy 1.8(目前是本文撰写时的发布候选版本)添加了内置功能,无需使用第三方工具或模块,即可通过DNS找到服务。HAProxy已经有内置DNS解析器一段时间了,但是HAProxy 1.8通过SRV记录和EDNS的支持为端口带来了解析,使其与Consul完美地匹配。

优点

  • 没有外部工具-只是运行HAProxy和直接指向Consul
  • 优雅的处理重载,TTLs等
  • 支持Kubernetes和Docker群集服务发现

缺点

  • 需要HAProxy
  • 在失败场景上的灵活性不如Consul Template

结论

Consul负载均衡的微服务应用有很多策略和技术。这篇文章详细介绍了一些最常见的技术,并希望能够激发灵感,找到新的和令人兴奋的方法来集成行业标准负载平衡工具和Consul。无论您是直接使用Consul,桥接与Consul Template的差距,编译自定义的Nginx自己,或尝试HAProxy 1.8发布候选,我们期待听到您如何平衡您的微服务负载。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐