MySQL:支持大部分的业务数据存储,当数据规模增大后有一些需要进行扩容的表,分表会带来一定的复杂性,有些业务希望能屏蔽这个事情,还有一些是因为历史原因在表设计的时候用 rmsdB 的形式存了一些本该由列存储的数据,希望做一下迁移。此外 MySQL 基于 SSD,虽然性能很好,花销也比较大;
Redis:可以提供大规模的缓存,也可以提供一定的存储支持。Redis 性能极好,主要的局限是做数据 Resharding 较为繁琐 ,其次是内存成本较高。
隔离性:
从业务方的视角来说,希望相关的服务做到环境隔离,权限收归业务,避免误操作和业务相互影响;
对于响应时间,服务的可用性,都可以根据业务的需要指定 SLA;
对于资源的分配和 BlockCache 等参数的配置也能够更加有适应性,提供业务级别的监控和报警,快速定位和响应问题;
资源利用率:从运维的角度,资源的分配要合理,尽可能的提升主机 CPU,内存包括磁盘的有效利用率;
成本控制: 团队用最小的成本去得到最大的运维收益,所以需要提供便捷的调用接口,能够灵活的进行 HBase 集群的申请,扩容,管理,监控. 同时成本包括机器资源,还有工程师. 当时我们线上的这套系统是由一位工程师独立去进行维护。
Node:定义主机节点,可以是物理机,也可以是虚拟机;
Pod:一组紧密关联的容器集合,是 Kubernetes 调度的基本单位;
ReplicationController:一组 Pod 的控制器,通过其能够确保 Pod 的运行数量和健康,并能够弹性伸缩。
2 * Master ReplicationController;
3 * Regionserver ReplicationController;
2 * Thriftserver ReplicationController(可选)。
ZooKeeper 集群:自身设计保证了可用性;
Master:通过多个 Master 注册在 ZooKeeper 集群上来进行主节点的 HA 和更新;
RegionServer:本身就是无状态的,节点失效下线以后会把上面的 region 自动迁走,对服务可用性不会有太大影响;
Thriftserver:当时业务大多数是 Python 和 Golang,通过用 Thrift 对 HBase 的进行,Thriftserver 本身是单点的,这里我们通过 HAProxy 来代理一组 Thriftserver 服务;
HDFS:本身又由 Namenode 和 DataNode 节点组成,Namenode 我们开启 HA 功能,保证了 HDFS 的集群可用性。
Pod 容器失效:Pod 是通过 ReplicationController 维护的, Kubernetes 的 ControllerManager 会在它 的存储 etcd 去监听组件的失效情况,如果副本少于预设值会自动新的 Pod 容器来进行服务;
Kubernetes 集群崩溃:该场景曾经在生产环境中出现过,针对这种情况,我们对 SLA 要求较高的业务采用了少量物理机搭配容器的方式进行混合部署,极端场景出现时,可以保证重要业务收到的影响可控。
所有在 Kubernetes 上构建的 HBase 集群都共享了一套 HDFS 集群,数据的可用性由 HDFS 集群的多副本来提供。
根据业务方对自身服务的描述,对相关的 QPS 以及 SLA 进行评估,为业务专门配置参数,包含 blockcache,region 大小以及数量等;
优点是针对业务优化,能够充分的利用资源,降低业务的资源占用成本;
管理成本增加,需要对每一个业务进行评估,对平台维护人员非常不友好,同时需要业务同学本身对 HBase 有理解。
CPU 以及 MEM 都按照预先设定好的配额来分配,提供多档的配置,将 CPU 和 MEM 的配置套餐化;
方便之处在于业务扩容时直接增加 Regionserver 的个数,配置稳定,运维成本较低,遇到问题时排障方便;
针对某些有特有访问方式的业务有局限性,如 CPU 计算型,大 KV 存储,或者有 MOB 需求的业务,需要特殊的定制。
# Example for hbase dockerfile
# install cdh5.5.0-hbase1.0.0
ADD hdfs-site.xml /usr/lib/hbase/conf/
ADD core-site.xml /usr/lib/hbase/conf/
ADD env-init.py /usr/lib/hbase/bin/
ENV JAVA_HOME /usr/lib/jvm/java-8-oracle
ENV HBASE_HOME /usr/lib/hbase
ENV HADOOP_PREFIX /usr/lib/hadoop
ADD env-init.py /usr/lib/hbase/bin/
ADD hadoop_xml_conf.sh /usr/lib/hbase/bin/
REQUEST_DATA = {
"name": 'test-cluster',
"rootdir": "hdfs://namenode01:8020/tmp/hbase/test-cluster",
"zkparent": "/test-cluster",
"zkhost": "zookeeper01,zookeeper02,zookeeper03",
"zkport": 2181,
"regionserver_num": '3',
"codecs": "snappy",
"client_type": "java",
"cpu": '1',
"memory": '30',
"status": "running",
}
source /usr/lib/hbase/bin/hadoop_xml_conf.sh
&& put_config --file /etc/hbase/conf/hbase-site.xml --property hbase.regionserver.codecs --value snappy
&& put_config --file /etc/hbase/conf/hbase-site.xml --property zookeeper.znode.parent --value /test-cluster
&& put_config --file /etc/hbase/conf/hbase-site.xml --property hbase.rootdir --value hdfs://namenode01:8020/tmp/hbase/test-cluster
&& put_config --file /etc/hbase/conf/hbase-site.xml --property hbase.zookeeper.quorum --value zookeeper01,zookeeper02,zookeeper03
&& put_config --file /etc/hbase/conf/hbase-site.xml --property hbase.zookeeper.property.clientPort --value 2181
&& service hbase-regionserver start && tail -f /var/log/hbase/hbase-hbase-regionserver.log
需要手动提前确定 HDFS 集群的存储,以及申请独立 ZooKeeper 集群,早期为了省事直接多套 HBase 共享了一套 ZooKeeper 集群,这和我们设计的初衷不符合;
容器标识符和 HBase Master 里注册的 Regionserver 地址不一致,影响故障定位;
单 Regionserver 运行在一个单独的 ReplicationController(以下简称 RC),但是扩容缩容为充分利用 ReplicationController 的特性,粗暴的采用增加或减少 ReplicationController 的方式进行扩容缩容。
最初的设计缺乏灵活性,与 HBase 服务配置有关的 hbase-site.xml 以及 hbase-env.sh 固化在 Docker Image 里,这种情况下, 如果需要更新大量配置,则需要重新 build 镜像;
由于最初设计是共享一套 HDFS 集群作为多 HBase 集群的存储,所以与 HDFS 有关的 hdfs-site.xml 和 core-site.xml 配置文件也被直接配置进了镜像。如果需要在 Kubas service 中上线依赖其他 HDFS 集群的 HBase,也需要重新构建镜像。
随着接入 HBase 集群的增多,不同的 HBase 集群业务对 HDFS 的 IO 消耗有不同的要求,因此有了分离 HBase 依赖的 HDFS 集群的需求;
versatile 主要问题源自 Docker 镜像对相关配置文件的固化,与 HDFS 有关的 hdfs-site.xml 和 core-site.xml 配置文件与相关 Docker 镜像对应,而不同 Docker 镜像的版本完全由研发人员自己管理,最初版本的实现并未考虑到这些问题。
指标数据不充分,堆内堆外内存变化,region 以及 table 的访问信息都未有提取或聚合;
region 热点定位较慢,无法在短时间内定位到热点 region;
新增或者下线组件只能通过扫 Kubas Service 的数据库来发现相关变更,组件的异常如 Regionserver 掉线或重启,Master 切换等不能及时反馈。
Deployment(部署)是 Kubernetes 中的一个概念,是 Pod 或者 ReplicaSet 的一组更新对象描述,用于取代之前的 ReplicationController。Deployment 继承了 ReplicationController 的所有功能,并拥有更多的管理新特性;
在新的 Kubas 管理系统中,新设计用 Deployment 代替 ReplicationController 做 Pod 的管理,使用一个 Deployment 部署一组 Regionservers 的方式来代替单 Regionserver 对应一个 ReplicationController 的设计,提升集群部署扩缩容管理的灵活性;
每一组 Deployment 都会注入各类信息维度的标签,如相关集群的信息就,服务类型,所属应用等。
ConfigMap 是 Kubernetes 用来存储配置文件的资源对象,通过 ConfigMap 可以将外部配置在启动容器之前挂载到容器中的指定位置,并以此为容器中运行的程序提供配置信息;
重构之后管理系统中,所有 HBase 的组件配置都存放至 ConfigMap 之中,系统管理人员会根据需-要预先生成若干 HBase 的配置模板存放到 Kubernetes 系统的 ConfigMap 中;
在业务方提供出 HBase 服务申请时,管理人员通过业务资源的需求结合配置模板,为申请的 HBase 集群组件渲染具体的 hbase-site.xml 以及 hbase-env.sh 等 HBase 配置相关的文件再存放到 ConfigMap 中;
最后在容器启动时,Kubernetes 会根据 Deployment 将 ConfigMap 中的配置文件 Mount 到配置中指定的路径中;
和 Deployment 的操作类似,每一份 ConfigMap 也都会标记上标签,将相关的 ConfigMap 和对应的集群和应用关联上。
RequestData
{
"name": "performance-test-rmwl",
"namespace": "online",
"app": "kubas",
"config_template": "online-example-base.v1",
"status": "Ready",
"properties": {
"hbase.regionserver.codecs": "snappy",
"hbase.rootdir": "hdfs://zhihu-example-online:8020/user/online-tsn/performance-test-rmwl",
"hbase.zookeeper.property.clientPort": "2181",
"hbase.zookeeper.quorum": "zookeeper01,zookeeper02,zookeeper03",
"zookeeper.znode.parent": "/performance-test-rmwl"
},
"client_type": "java",
"cluster_uid": "k8s-example-hbase---performance-test-rmwl---example"
}
servers:
{
"master": {
"servertype": "master",
"command": "service hbase-master start && tail -f /var/log/hbase/hbase-hbase-master.log",
"replicas": 1,
"image": "dockerimage.zhihu.example/apps/example-master:v1.1",
"requests": {
"cpu": "500m",
"memory": "5Gi"
},
"limits": {
"cpu": "4000m"
}
},
}
RUN mkdir -p /data/hbase/hbase-site
RUN mv /etc/hbase/conf/hbase-site.xml /data/hbase/hbase-site/hbase-site.xml
RUN ln -s /data/hbase/hbase-site/hbase-site.xml /etc/hbase/conf/hbase-site.xml
RUN mkdir -p /data/hbase/hbase-env
RUN mv /etc/hbase/conf/hbase-env.sh /data/hbase/hbase-env/hbase-env.sh
RUN ln -s /data/hbase/hbase-env/hbase-env.sh /etc/hbase/conf/hbase-env.sh
编制相关的 Dockerfile 并构建基础的 HBase 组件镜像;
为将要创建的 HBase 构建基础属性配置模板,订制基础资源,这部分可以通过 Kubas API 在 Kubernetes 集群中创建 ConfigMap;
具体创建部署集群时,通过调用 Kubas API,结合之前构建的 ConfigMap 模板,渲染出 HBase 集群中各类组件的详细 ConfigMap,然后在 Kubernetes 集群中构建 Deployment;
最终通过之前构建好的镜像加载组件 ConfigMap 中的配置,完成在 Kubernetes Node 中运行的一个 HBase 组件容器。
运维成本增高:需要运维的集群逐渐增高;
资源浪费:这是因为很多业务的业务量并不高,但是为了保证 HBase 的高可用,我们至少需要提供 2 个 Master + 3 个 Region Server,而往往 Master 的负载都非常低,这就造成了资源浪费。
业务 HBase 集群分别在多个 IDC 上运行,由业务确定 IDC 机房的主从方式,业务的从 IDC 集群数据通过平台方的数据同步组件进行数据同步;
各 IDC 的 Kubas 服务主要负责对本地 Kubernetes 集群的具体操作,包括 HBase 集群的创建删除管理,Regionserver 的扩容等 HBase 组件的管理操作,Kubas 服务部署与机房相关,仅对接部署所在机房的 Kubernetes 集群;
各 IDC 的 Kubas 服务向集群发现服务上报本机房集群信息,同时更新相关集群主从相关信息;
业务方通过平台方封装的 Client SDK 对多机房的 HBase 集群进行访问,客户端通过集群发现服务可以确定 HBase 集群的主从关系,从而将相关的读写操作分离,写入修改访问可以通过客户端指向主 IDC 的集群;
跨机房间的数据同步采用了自研的 HBase Replication WALTransfer 来提供增量数据的同步。
Region 的信息,上线 Region 数量,store 的数量、storefile 的大小、storefileindex 的大小,读取时 memstore 命中的次数和缺失次数;
blockcache 的信息,例如 blockcache 中使用多少、空闲多少、累计的缺失率、命中率等;
读写请求的统计信息,例如最大最小读写响应时间,读写的表分布、读写数据量、读写失败次数等;
compact 与 split 的操作信息,例如队列的长度、操作次数和时间等; handler 的信息,例如队列长度、处于活跃 handler 的数量以及活跃的 reader 数量;
其他维度的指标如容器 CPU 以及 Mem 占用来自 Kubernetes 平台监控,磁盘 IO,磁盘占用等来自主机监控。
采集 HBase 表 Region 信息,通过 HBase API 接口,获取每个 HBase Region 的数据统计信息,并将 Region 数据聚合成数据表信息;
通过调用开源组件形成 HBase 集群 Region 分布的图表,对 Region 热点进行定位。
提升集群安全稳定性。加入 HBase 权限支持,进一步提升多租户访问下的安全隔离性;
用户集群构建定制化。通过提供用户数据管理系统,向业务用户开放 HBase 构建接口,这样业务用户可以自行构建 HBase 集群,添加 Phoniex 等插件的支持;
运维检测自动化。自动对集群扩容,自动热点检测以及转移等。