负载均衡( Load Balancing )是开源 PaaS Rainbond 的亮点功能,主要由“软件定义负载均衡” Rainbond-Entrance 控制器完成。
本文将围绕设计架构和实现介绍 Rainbond-Entrance。
Rainbond 内部网络划分支持多租户,每个租户都有一个私有的 IP 段,不同租户的网络相互不可见。当我们把一个容器化应用部署到 Rainbond,Rainbond 会为该容器分配一个内部 IP,用于同一租户中不同应用在集群内部的通信,而集群外部无法直接访问,因此我们需要有一个集群入口控制器,以便用户可以方便地访问这些应用。
另外,Rainbond 中部署的每个应用都可以有多个实例,即假设我们为一个 WEB 应用部署了三个实例,每个实例分担一部分流量,我们就需要这三个实例前增加负载均衡控制器来完成分发流量的工作。
除了上述的基本功能以外,负载均衡控制器还必须支持更多功能,例如:
综上所述,我们需要一个同时支持 L4 或各类应用协议(L7)的负载均衡控制器集群,还必须能够自动发现集群中的应用变化以更新自己的转发规则。
web
:表示 Rainbond 中的一个应用,并且有三个实例api-server
:kubeneters 的 kube-apiserver 组件entrance
:Rainbond 的负载均衡控制器通用接口,支持多种负载均衡插件Rainbond 中的负载均衡是面向应用的,不同应用可以使用不同的负载均衡。Rainbond 的 Entrance 组件设计,使之可以集成集成多种负载均衡插件,也就是说,Rainbond 不仅支持常用的 OpenResty,还可以支持其它负载均衡插件,例如商业支持的 F5 等。
Entrance 的主要工作是从 kube-apiserver 中监听应用运行节点信息的变化,例如 Service、Endpoint、Pod 等,然后把这些资源抽象为通用的负载均衡抽象并缓存在 etcd 中,这些通用抽象包括:
Pool
:负载均衡池,其中包括多个节点,对应上图中的三个 WEB 实例Node
:Pool 中的一个节点,对应上图中的其中一个 WEB 实例Domain
:域名,负载均衡控制器可以识别一个数据包中的域名信息然后将数据转发给对应的 PoolVirtualService
:监听了某个端口的虚拟主机,还指明了端口的协议名称,主要用来处理 L4 入口控制和负载均衡Rule
:转发规则,用来描述域名跟 Pool 的对应关系,还指明了端口的协议名称与证书信息,处理 L7 入口控制和负载均衡当有资源发生变化时,Entrance 会将通用资源转化为相应插件的资源,并根据应用所选择的不同的插件驱动操作负载均衡控制器。
从架构中可以看到,有两个 Entrance 和两个 OpenResty 实例,它们的关系是:每个 Entrance 中持有所有 OpenResty 的地址,当有信息需要更新时,Entrance 会将信息更新到所有的 OpenResty。那两个 Entrance 之间怎么协调呢?这里我们利用 etcd 本身的特性做了分布式锁,保证只有一个 Entrance 有权限向 OpenResty 更新信息,如此实现了高可用。
OpenResty 是一个可以用 Lua 脚本来处理请求和业条逻辑的 WEB 应用,并且内置了众多 Lua 相关的指定和函数供开发者使用,很合适开发 Restful API 服务器,我们将 OpenResty 作为 Entrance 的插件之一原因如下:
我们在 OpenResty 端嵌入了一个 Rest API 服务器,这些 API 是用 Lua 写的。前面说过 OpenResty 集成了 Lua 脚本功能,我们可以直接用 Lua 来处理请求,下面是 Nginx 配置文件的其中一部分:
# custom api of upstream and server
server {
listen 10002;
location ~ /v1/upstreams/([-_0-9a-zA-Z.@]+) {
set $src_name $1;
content_by_lua_file lua/upstream.lua;
}
location ~ /v1/servers/([-_0-9a-zA-Z.@]+) {
set $src_name $1;
content_by_lua_file lua/server.lua;
}
}
当我们调用下面的 API 时:
curl -s localhost:10002/v1/servers/app1 -X POST -d "$json_data"
OpenResty 会执行相应的 Lua 脚中,也就是lua/server.lua
,前面说过,OpenResty 内置了很多 Lua 相关的指命与函数,可以让 Lua 与 Nginx 更好地交互,所以我们在脚本中很容器处理接收到的 JSON 数据,并将其转换为配置 Nginx 文件,由于 Lua 代码较多就不贴出来了,可以在本文的引用部分找到该项目地址。
这里有个需要注意的地方,当收到大量修改 server 和 upstream 的请求时,OpenResty 需要频繁加载配置文件,这样会增加负载且影响性能。实际上 OpenResty 有很多第三方插件可以使用,有一个叫 dyups 的插件可以做到动态修改 upstream,它的使用方式如下,Lua 代码:
-- 增加或更新指定 upstream
dyups.update("upstream_name", [[server 127.0.0.1:8088;]])
-- 删除指定 upstream
dyups.delete("upstream_name")
执行成功后就已经生效了,不需要我们执行nginx -s reload
命令,这会提高一些效率。
对于 server 的修改暂时还没有相应用插件做到动态修改,所以实际上我们的负载均衡控制器分两种情况,如果更新了 upstream 配置会即时生效,而更新 server 配置则需要加上nginx -s reload
命令。
我们用 Entrance 加 OpenResty 实现了一个可插拔且高可用的负载均衡控制器,整体来说并不复杂,希望本文能带给你一些帮助。
目前我们已经把 Rainbond 的 OpenResty 插件分离出来做为一个子项目并且开源在 Github 上,你可以下载并单独使用:Github 地址。
好雨 Rainbond(云帮)是一款以应用为中心的开源 PaaS,深度整合基于 Kubernetes 的容器管理、Service Mesh 微服务架构最佳实践、多类型 CI/CD 应用构建与交付、多数据中心资源管理等技术,为用户提供云原生应用全生命周期解决方案,构建应用与基础设施、应用与应用、基础设施与基础设施之间互联互通的生态体系,满足支撑业务高速发展所需的敏捷开发、高效运维和精益管理需求。