高性能数据访问中间件 OBProxy(二):安装部署最佳实践

引言

OceanBase 作为一款分布式关系型数据库,随着 Observer 集群的规模不断扩大,如果直连 ObServer,停机/上下线机器的概率也会随之增加, OBProxy 便是在这种情况下应运而生,以解决分布式数据库系统的 SQL 路由、高可用等问题。

OBProxy,全称为 OceanBase Database Proxy,是 OceanBase 数据库专用的服务代理。使用 OBProxy 可以屏蔽后端 ObServer 集群本身的分布式带来的复杂性,让访问分布式数据库像访问单机数据库一样简单。为此,我们筹划了高性能的数据访问中间件——OBProxy 专题,包括九篇文章,将从 OBProxy 的部署、原理、功能、架构、问题排查、最佳实践等方面一次性讲透,让你真正地对 OBProxy 知其然知其所以然。

上一篇文章是我们 OBProxy 系列文章的首篇,主要解读了 OBProxy 的功能模块和详细的特性,本章主要聚焦安装部署部分。实际使用中,大家通过 obdeploy 工具就会自动安装部署完成 OBProxy,本文我们将详细介绍背后的原理,并总结生产环境的安装部署经验,内容更偏实战,学习本文后大家无需通过 Obdeploy 工具就可以安装部署OBProxy,那么我们开始吧。

1 安装步骤

OBProxy 的安装十分简单,对硬件资源要求不高,一般不容易遇到问题。下面以流程图方式说明安装步骤:

1.1 检查安装环境

1.1.1 平台适配

OBProxy 推荐安装在 x86_64 平台和 arm 平台。目前,OBProxy 在 x86_64 平台运行最久、最稳定、性能表现最佳。近两年我们也测试发现 OBProxy在 arm平台可以很好的工作。当前,在蚂蚁集团生产环境,已经有一批OBProxy 运行在 arm 平台机器上了。

1.1.2 操作系统

OBProxy 当前支持 Linux 系统,暂不支持 Windows 系统和其它系统。对于 Linux 系统,OBProxy 的配置项enable_strict_kernel_release控制对操作系统版本检查,检查不通过会导致启动失败,关键日志如下:

[2021-10-13 10:38:17.235062] WARN [PROXY] get_kernel_release_by_uname (ob_config_server_processor.cpp:1039) [2060][Y0-0] [lt=14] [dc=0] unknown uname release(uinfo.release="4.18.0-80.el8.x86_64", ret=-4016)

大家可以使用 uname 命令查看内核信息,该选项目前只允许 el 系列和 alios 系列通过,检查策略偏保守型,可能会产生一些误报。其它 linux 系统如果遇到上面问题,我们可以通过关闭该配置项予以解决。

1.1.3 硬件资源

OBProxy 启动成功后,CPU 占用 0.7c 左右,内存占用百余兆。当遇到请求流量后,CPU和内存使用会增加。
资源使用情况如下:

  • CPU:随着请求量增加,CPU使用率会增加,QPS也会接近线性增加,直到CPU满负载,QPS会基本恒定。
  • 内存:内存使用量主要和连接数有关,随着连接数增加,内存使用会变多,配置项client_max_connections会控制允许的客户端连接数,默认最多支持8192个客户端连接,超过该数值会导致客户端连接失败。
  • 磁盘:磁盘主要用来存放配置文件和日志文件,其中日志文件对磁盘的使用比较多,当磁盘使用量超过80%后,OBProxy会主动清理日志文件。

1.2 选择合适版本

如果只是学习使用,一般安装最新的版本即可。如果是生产环境,建议大家关注下每个版本的功能迭代和 bugfix 情况。无论什么项目,阅读 Release Notes 是一个很好的习惯,可以帮助大家避坑和掌握每个版本的功能支持情况,做到心中有数。

OBProxy 的代码已经开源,在 OceanBase 开源项目中找到 OBProxy 项目,点击 Releases 可以查看每个版本的详细信息。OBProxy 每个版本都经过了功能测试、压力测试和性能测试,大家可以放心使用。

1.3 通过 Obdeploy 安装

Obdeploy 是我们推荐的社区安装工具,除了安装 ObServer,也会安装 OBProxy,默认会安装最新版本,如果安装前想指定 OBProxy 版本,需在配置文件中指定,配置文件中 OBProxy 的配置可参考下面内容:

obproxy-ce:
  version: 3.2.3
  # Set dependent components for the component.
  # When the associated configurations are not done, OBD will automatically get the these configurations from the dependent components.
  depends:
    - oceanbase-ce
  servers:
    - 192.168.1.5
  global:
    listen_port: 2883 # External port. The default value is 2883.
    prometheus_listen_port: 2884 # The Prometheus port. The default value is 2884.
    home_path: /root/obproxy
    # oceanbase root server list
    # format: ip:mysql_port;ip:mysql_port. When a depends exists, OBD gets this value from the oceanbase-ce of the depends.
    # rs_list: 192.168.1.2:2881;192.168.1.3:2881;192.168.1.4:2881
    enable_cluster_checkout: false
    # observer cluster name, consistent with oceanbase-ce's appname. When a depends exists, OBD gets this value from the oceanbase-ce of the depends.
    # cluster_name: obcluster
    skip_proxy_sys_private_check: true
    enable_strict_kernel_release: false
    # obproxy_sys_password: # obproxy sys user password, can be empty. When a depends exists, OBD gets this value from the oceanbase-ce of the depends.
    # observer_sys_password: # proxyro user pasword, consistent with oceanbase-ce's proxyro_password, can be empty. When a depends exists, OBD gets this value

安装好后,可以通过obd cluster display查看部署情况,参考下述例子。通过该命令我们可以看到OBProxy 信息,其中 port 2883 表示提供 SQL 服务的端口是2883,也就是 JDBC URL 中需要填写的 PORT。

[root@xx.xx.xx.xx obtest]$obd cluster display obtest
Get local repositories and plugins ok
Open ssh connection ok
Cluster status check ok
Connect to observer ok
Wait for observer init ok
+--------------------------------------------------+
|                     observer                     |
+----------------+---------+------+-------+--------+
| ip             | version | port | zone  | status |
+----------------+---------+------+-------+--------+
| 172.30.199.109 | 3.1.3   | 2881 | zone1 | active |
| 172.30.199.110 | 3.1.3   | 2881 | zone2 | active |
| 172.30.199.111 | 3.1.3   | 2881 | zone3 | active |
+----------------+---------+------+-------+--------+

Connect to obproxy ok
+--------------------------------------------------+
|                     obproxy                      |
+----------------+------+-----------------+--------+
| ip             | port | prometheus_port | status |
+----------------+------+-----------------+--------+
| 172.30.199.109 | 2883 | 2884            | active |
+----------------+------+-----------------+--------+

1.4 安装后检查

上面命令obd cluster display已经做了状态检查。使用终端登录到 OBProxy 进程所在机器,通过ps -ef | grep obproxy命令查看进程信息。

[root@observer109 ~]# ps -ef | grep obproxy | grep -v grep
admin     6868     1  0 May20 ?        00:02:09 bash /home/admin/obproxy/obproxyd.sh /home/admin/obproxy 172.30.199.109 2883 daemon
admin     6901     1  0 May20 ?        00:44:11 /home/admin/obproxy/bin/obproxy --listen_port 2883

这里可以看到两个进程,obproxyd.sh 进程和 obproxy 进程。obproxy 就是 OBProxy 的进程名。obproxyd.sh是OBProxy 的守护脚本,负责启动 obproxy ,对 obproxy 做健康检查,如果 obproxy 进程不存在会主动拉起进程。

安装后,如果想确定 OBProxy 的版本,有三种方法:

  1. 新版本 OBProxy 的目录会带版本号,如 obproxy-3.2.3目录名表示3.2.3版本的 OBProxy
  2. 在 OBProxy 的目录下执行./bin/obproxy -V查看
  3. 使用 root@proxysys 账号登录后,执行show proxyinfo binary查看

方法 2 的结果如下,版本是 3.2.3:

[root@observer109 obproxy]# ./bin/obproxy -V
./bin/obproxy -V
obproxy (OceanBase 3.2.3 2)
REVISION: 6-local-99faebfc7130b70ad0f56330a28cab6a32ec9a33
BUILD_TIME: Mar 30 2022 01:53:08

方法 3 的结果如下,版本是 3.2.6:

MySQL [(none)]> show proxyinfo binary\G
*************************** 1. row ***************************
name: binary info
info: ObProxy-OceanBase 3.2.6-5902.el7
version:RELEASE_7U
MD5:
REVISION:5902-local-f6b6e366ce8ed7611a05c2fc4701c791f26e0356
BUILD_TIME:May 23 2022 09:19:44

2 目录介绍

安装完成后,除了查看进程信息,另一个比较重要的点是查看目录结构。进入到 OBProxy 目录下(一般为/home/admin/obproxy),通过 ls 命令查看。

OBProxy 目录结构如下:

[root@observer109 obproxy]# ls -alrt
total 44
drwxrwxr-x  2 admin admin 4096 May 12 14:55 lib
drwx------  8 admin admin 4096 May 12 14:55 ..
drwxrwxr-x  2 admin admin 4096 May 12 14:55 bin
drwxrwxr-x  2 admin admin 4096 May 12 14:57 sharding-config
drwxrwxr-x  2 admin admin 4096 May 12 14:57 control-config
drwxrwxr-x  2 admin admin 4096 May 12 14:57 log
drwxrwxr-x  2 admin admin 4096 May 12 14:57 run
drwxrwxr-x 10 admin admin 4096 May 20 13:43 .
-rw-r--r--  1 admin admin  910 May 20 13:43 obproxyd.sh
drwxrwxr-x  2 admin admin 4096 May 20 13:44 .conf
drwxrwxr-x  2 admin admin 4096 May 23 17:27 etc

比较重要的目录和文件包括:

  • bin目录:保存 obproxy 的二进制文件
  • etc目录和 .conf 目录:保存配置信息,.conf 是 etc 的备份,如果etc目录被删除,会使用.conf中的内容
  • sharding-config目录:保存sharding相关的配置文件
  • log目录:保存日志文件的目录,磁盘占用最大,日志文件也分为多种,帮助排查定位问题
  • obproxyd.sh:守护脚本,内容简单,大家可以通过阅读脚本代码了解实现原理

3 OBProxy 重启和退出

了解 OBProxy 的重启和退出机制,大家可以更灵活地使用 OBProxy,如进行版本升级、特性验证等。
OBProxy启动需要指定集群信息,有两种方式:

  1. 设置配置项obproxy_config_server_url,内容为 OCP 的地址,访问该地址可以获得集群信息
  2. 通过启动配置参数指定集群中机器的 IP 和 PORT,也叫做 rslist 方式,该方式只支持访问一个集群

接下来我们以 rslist 方式举例。

3.1 OBProxy 重启

本节内容我们不通过 Obdeploy 启动,直接通过二进制方式启动,大家通过 rpm 获取到 OBProxy 二进制后可以按照步骤操作,启动命令如下:

./bin/obproxy -p 2883 -r '192.168.1.1:2881' -c 'cluster1' -o enable_strict_kernel_release=false,work_thread_num=8

主要选项如下:

  • -p:指定服务监听端口号,一般使用2883
  • -r:此处使用的是 rslist 方式启动,直接指定 ObServer 的 IP 和 PORT,这里需要注意 ObServer 有一个 RPC端口和一个 SQL 端口,需要使用 SQL 端口,一般是 2881
  • -c:访问的集群名字,对应 ObServer 进程的 -n 选项,也可以通过 SQL 查询 OceanBase 数据库获得集群名;如果通过 OBProxy 代理多个集群,只能使用指定 OCP 对应 URL 的方式
  • -o:后面是配置项信息,可以首次启动指定,格式为k1=v1,k2=v2,和前面几个选项不同,此选项非必须

通过上面命令就可以启动 obproxy 进程了。需要注意的是,无论是使用 obproxy 二进制的绝对路径还是相对路径,都需要在 OBPRoxy 目录下执行上面命令。如果启动失败,前往 log/obproxy.log 文件中查看 WARN 和 ERROR 信息可很快定位问题。

第一次启动后,就会在etc和.conf目录下生成配置文件并将配置信息持久化,下次如果重新启动,直接执行./bin/obproxy命令即可。

如果还想用默认配置,需要执行rm -rf etc .conf删除持久化的配置信息,然后执行首次启动命令。

通过日志succ to init logger可以确定启动时间,该日志只在启动时打印一次。

3.2 OBProxy 退出

OBProxy 进程退出后,obproxyd.sh 将在 1s 内重新拉起 OBProxy,有时根本察觉不到 OBProxy 退出过。在生产环境,该机制可以快速恢复服务,起到止血作用。

退出主要分为主动退出和异常退出两种情况。主动退出比较简单,目前无专门的运维命令,直接使用 kill 命令杀掉进程即可。

对于异常退出,常见的情况有三种:

  1. 内存使用太多:OBProxy 本身对内存使用做了限制,通过配置项proxy_mem_limited控制,默认为2G,内存使用超过该配置值后 OBProxy 会主动退出,这样好处是防止 OOM 情况。但坏处是如果大小设置不合理会导致系统有剩余内存,OBProxy 也会退出,可以通过在 obproxy.log 中搜索memroy is out of limit确定是这种情况。

  2. 进程core掉:通过 dmesg 命令和在 core 文件目录查看可以确认。core行为都是非预期的,需要排查。名字中包含obproxy、ET_NET、ET_TASK、ET_BLOCKING、ET_GRPC和ASYNC_LOG的 core 文件都是 OBProxy 进程产生的。

  3. 被信号中止:通过搜索关键日志receive signal确定信号值,这种问题排查较困难,需要确定系统执行过的操作命令。

对于 OBProxy,一般查询启动时间确认是否重启过,查看启动时间的方法有很多,如下面例子:

$ls -l /proc/`pidof obproxy` -d
dr-xr-xr-x 8 zhixin.lm users 0 May 23 10:29 /proc/100277

3.3 小结

非生产环境安装时,重启和退出是一件比较简单的事情。但在生产环境,这件事至关重要。服务退出会影响现有连接和请求执行,客户端报误。如果重启很慢,意味着服务不能快速恢复,但大家不用担心 OBProxy 的启动速度,1s 内便可以提供正常服务。

为了加深大家理解,我们结合实际遇到的问题,举个例子:

image

左边部分是 Java 程序执行逻辑,右边是 obproxy 进程的退出和重启情况,从上往下表示时间从早到晚,请问到第四个圆圈时间点,应用程序表现是什么?

相信大家已经有答案了,executeQuery 会抛出异常。但真实场景中,我们一般只能看到 executeQuery 执行失败,需要排查问题才能发现 OBProxy 发生过重启,并且 executeQuery 失败原因不止上面一种情况,排查起来比较麻烦,后面连接管理章节会有更多内容。

4 部署

在满足1.1中的安装环境要求后,OBProxy 就可以安装部署了,生产环境部署主要考虑 3个因素:

  • 交付方式:OBProxy 有 rpm 包和 docker 两种形式,Obdeploy 和 OCP 都是通过 rpm 部署,蚂蚁内部 PASS平台通过 docker 方式启动
  • 数量:我们对 OBProxy 的数量没有做任何限制,但真实部署时,OBProxy 的数量和 APP 或 ObServer 的数量有一定关系,这取决于具体的部署方式
  • 部署方式:不同部署方式背后反映的是 RT 和资源抢占,有客户端部署、ObServer 端部署和独立部署三部分,这对性能至关重要

下面我们将结合实际场景做详细介绍。

4.1 部署在应用端

结合云原生技术,OBProxy 以 sidecar 方式和 APP 一起部署在同一个物理机上。APP 和 OBProxy 的数量满足1:1关系。OBProxy 和 OceanBase 数据库直连,中间没有负载均衡。实践证明,这种方式性能是最好的,同时需要注意,这里需要 APP、OBProxy 和 ObServer 之间的网络互通。

因为 APP 和 OBProxy 的个数是1:1的对应关系,因此这种部署方式会导致 OBProxy 的容器特别多,达到成千上万个,所以这种方式依赖底层的 k8s 等基础设施。

image

4.2 部署在 ObServer 端

部署在 ObServer 端是指在部署 ObServer 的机器上部署一个 OBProxy 进程,这样 ObServer 和 OBProxy 的数量满足 1:1 关系。与 4.1 对比,OBProxy 数量和 APP 没有了对应关系。除了一台 ObServer 部署一个 OBProxy,也可以一个 zone 内部署一个 OBProxy。

下图是专有云很常见的部署形态,和部署在应用端相比,有如下区别:

  1. 多了 LB 组件做 OBProxy 的负载均衡,链路更长
  2. APP 和 OBProxy 之间没有明确的一一对应关系,排查问题会困难些
  3. OBProxy 部署在 ObServer 所在机器,压力情况下会有机器的CPU/MEM资源抢占

image

4.3 独立部署

独立部署是指专门为 OBProxy 找一台机器部署。此时 OBProxy 的数量和 APP、ObServer 都没有关系,根据具体业务需求确定 OBProxy 的数量。对于 OBProxy 的部署机型,一般推荐选择小机型即可,如云上使用 16c16g 的ecs。独立部署后,ObServer 和 ObProxy 之间不存在资源抢占,可以更好地管理 OBProxy,将 OBProxy 做成资源池对外服务,目前公有云使用了该部署方式。

4.4 小结

部署方式和真实的物理环境、业务需求等相关。上面我们对不同部署方式的优缺点都做了介绍,大家可以根据实际业务需求选择合适的方案部署,服务业务才是最重要的。

5 总结

首篇文章我们和大家一起快速入门了 OBProxy,相信大家对 OBProxy 不再陌生。本章从安装和部署两个方面介绍,帮助大家使用更加丝滑。安装部分我们需要掌握一些避坑知识点,前文详细描述了可能遇到的问题。部署方式看似是一件很简单的事情,但上了生产环境后就大不一样,背后有很多考量和“纠结点”,大家也可以从实际出发,多想想其中面临的困难以及应对挑战的方法。

在上面内容中,我们还提到了需要注意或修改enable_strict_kernel_releaseproxy_mem_limited配置项的值。接下来,我们还会在设置上进行优化,逐步提高 OBProxy 的易用性,打磨一个开箱即用的好产品。

好了,期待下期再见!

6 课后互动

6.1 上期互动

问题:使用 OBProxy 后,大家访问数据库的代码需要做哪些改动?
答案:很多同学都答对了,无需修改访问数据库代码就可以访问,非常方便。大家也可能注意到,访问 OceanBase 数据库的用户名十分的复杂,有格式要求,名字的背后和 OceanBase 数据库的架构息息相关,后面章节会做介绍。

6.2 本期互动

obproxyd.sh 脚本对 OBProxy 进程做了探活,请问探活方法是什么?除了脚本中的方法,还有哪些探活方法?这些方法有什么优缺点,请举例说明。在问答区与我们互动吧~

1 个赞

坐沙发,这篇文章真是好啊,解答了很多关于obproxy的疑惑:
1.obproxy可以收工启动;
2.初次启动需要配置参数,再次启动会读取 etc 或 .conf 目录配置;
3.通过 obproxyd.sh 探活;
4.与应用部署方式的优劣:最优场景是与app放到一起。

关于课后题,当前是探活机制是每秒去检测 run 下的进程文件及后台进程是否存在,如果不存在,就重新启动拉起 obproxy 服务。

使用obdeploy部署OBProxy在 ObServer 端,obproxy-ce的servers和rs_list怎么配置多个?

指定rs list,多个observer用逗号隔开:

obproxy-ce:
  depends:
    - oceanbase-ce
  servers:
    - 10.211.55.74
  global:
    home_path: /home/chris/obproxy
    skip_proxy_sys_private_check: true
    enable_strict_kernel_release: false
    listen_port: 2883
    prometheus_listen_port: 2884
    cluster_name: obcluster
    obproxy_sys_password: obproxy-sys
    observer_sys_password: obproxy
    rs_list: 10.211.55.74:2881;10.211.55.75:2881;10.211.55.76:2881

对的,分号哈哈

1、想问的是每个observer上部署一个obproxy,是不是下面这样?
2、每个obproxy会默认把oceanbase-ce中的所有server作为 rs_list吗?
obproxy-ce:
depends:
- oceanbase-ce
servers:
- 10.211.55.74
- 10.211.55.75
- 10.211.55.76

问题1:每个observer上部署一个obproxy,是不是下面这样?->是的

问题2:关于rslist,不需要把所有server作为rslist,假设rslist只有一个,obproxy根据这个访问OB数据库sys租户的__all_virtual_proxy_server_stat表就可以获得所有的机器信息。填写两个也是一样的。
对于obd工具启动方式,是从每个zone选一台机器组成rslist的

1 个赞

明白了,谢谢大佬 :hand_with_index_finger_and_thumb_crossed:

还可以继续介绍OCP不是obproxy

是说介绍OCP部署obproxy?

后面是否考虑介绍下obproxy和odp-sharding的区别,从缩写上看都是odp容易混淆哦~ :smile:

找到说清楚的啦

嗯嗯,会的,后面专门一章讲解odp-sharding,包括原理、案例、实践等。

引言部分有后续内容介绍。