瑞瑞哥的博客

openstack管理面大规模性能优化

openstack管理面大规模性能优化

我之前在菊花厂搞了一年多的openstack大规模,就大规模场景下的性能优化随便分享一下,水平有限,如果有问题还请各位交流。

前提

有一些前提或者约定,我先在这里写出来:

  1. 老东家的openstack是基于级联架构的,和社区有点不一样,所以特有的优化我就不提了。
  2. 工作中使用的数据库换了,社区是mysql,老东家使用的是基于postgresql的自研版本。

工作内容

云计算有管理面、网络面、存储面等概念。

管理面就是你在官网上点击购买按钮,然后触发了一大堆HTTP后台请求,这些请求所在的网络平面。

网络面就是你开了2台虚拟机,虚拟机之间互相打流的网络平面。可想而知,这个平面和管理面肯定不是共同网卡的,否则你随便开2个虚拟机互相打流,把带宽占满,别人的申请虚拟机的HTTP请求都被阻塞了怎么办?(实际上2个普通的虚拟机,打不满流量的,这里只是举个栗子)

存储面就是虚拟机所在物理节点,和存储节点之间的、用于存储IO的网络平面。因为很多虚拟机的存储,未必是计算节点的本地盘,都是通过scsi协议,把存储节点的卷远程挂在计算节点上。

管理面的关键字:

  1. 端到端虚拟机发放时间
  2. API: QPS 时延
  3. 资源采集指标
  4. 部署相关:高可用,主备,扩容
  5. 升级回退性能相关

虚拟机发放时延:

大致优化思想:

  1. 主要在镜像卷这块耗时较多,可以使用链接克隆和lazy-loading技术。

    链接克隆就是第一次用这个镜像时,存一份母卷在存储系统中。第二次就不用去镜像服务器下载镜像了,直接从存储系统里分裂一份。

    lazy-loading技术亚马逊也在用,就是加载镜像的时候只读系统所需要读的,如果有写的话写在一个cache。如果再去读同样的地方,先判断cache中写过没,有就用cache的,没有就用读卷里的。当卷全部被加载的时候,合并cache和卷。

    下面两个图简单显示了链接克隆和延迟加载:

    链接克隆

    lazyloading

  2. 优化逻辑和流程

    比如下发虚拟机需要卷和端口,那么接收到虚拟机请求的时候,可以同时非阻塞下发两个请求,没必要排队one-by-one。

  3. 状态定时查询改回调

    这个略。

API性能提升

扩容

  1. 增加openstack的haproxyworker数目,以增加转发能力。
  2. 增加openstack进程的节点数目,比如在多个服务器上部署nova-api
  3. 增加openstack进程的worker,比如每个nova-apiosapi改为5个。

quota锁优化

这篇文章,作者是一个20级专家。他手下的人直接推动了这篇文章内的内容。

缓存

举几个例子:

  1. memcached缓存token
  2. 缓存cloudinit各个接口上的需要的IP和VM对应关系,提升接口性能
  3. 调度时,scheduler内缓存计算节点的内存和CPU信息

分库

数据库按neutron nova cinder分库

rabbitmq也按照neutron nova cinder分库

减少定时任务

  1. nova-compute的定时任务减少,
  2. ceilometer不直接调用nova-api,而是每查询几次libvirt缓存结果,再调用一次nova-api

算法方面

对某些耗时的API进行Cprofile分析,找到瓶颈点进行优化。

SQL/DB性能提升

SQL本身性能差

SQL本身性能差,需要进行执行计划的分析,在这方面postgresqlmysql执行计划要详细很多。

常见问题:

1
2
3
4
5
6
7
sequence scan 全表扫描
index full scan 索引全扫描
Order by -> sort use disk 查询排序
滥用关键字distinct 导致执行hash/sort
表结构设计不合理
SQL过于复杂,优化器不认识
优化器过于简单,没能找到最优执行计划

要学习的表连接问题常见方法:

1
2
3
merge join
Hash join
Nested loop(嵌套循环)

返回太多额外不必要的数据

返回不必要的列

有的时候,你只要一个字段,结果ORM把一个类对象(几十个字段)都返回来了。还有时候同样或者冗余的数据会反复从数据库中取得。

这种问题,在你需要1000行的时候更加突出,1000行*40个额外字段是很耗性能的。

据我观察很多时候SQLalchemy的lazy load并没有生效,估计是用法问题。

返回不必要的行

这种情况也是有的。

SQLalchemy序列化性能问题

SQLalchemy序列化性能本身就因为嵌套很多层,或者反复构建各种数据结构而导致性能差。

如果碰到上面那种情况,问题会更加放大。

我们组做过性能分析,不管是用cprofile还是python APM(更换cpython解释器打日志),结果都显示高压力下CPU占用99%都是序列化。

大规模下数据库连接数问题

连接数多的问题

连接数搞的很大有个问题,举个栗子:

nova-api的实例数为13,worker为5,每个进程长连接哪怕为10,也是650个连接。

然后nova有两个库,novanova-api,加起来就很多了。更别提还有nova-schedulernova-conductor等。算下来大规模场景下多worker,多实例的情况下,哪怕nova cinder neutron分库了,每个库也有3000到4000的连接数,这是很恐怖的。

更不必说老东家用的PostgreSQL,进程模型,压力一来动不动就系统high load,更有甚者还遇到多个进程写slow log的问题:

PostgreSQL日志功能是一个专门进程提供的,其他链接进程想写日志,就得走管道。多个进程写日志就争抢管道,然后系统就更加high load,然后SQL就更慢,slow log更多,触发死循环和滚雪球。

GIL锁的问题

Python有GIL锁,同一时间只能用一个核心。这个情况如果是远程调用没问题,协程会自动迁走。问题是上面说的大量冗余数据+序列化,导致协程都在处理序列化,那么这时候其他协程的SQL,不能并发序列化(因为只能用一个核心)。

那么其他协程不能同时工作,只能干等,干等的话,数据库那边的临时短连接就不释放,这就造成数据库端连接过多,性能又差了;同时,数据库端看到的执行duration也很长,因为协程卡住了没执行完啊。

避免长事务

各种各样的长事务导致Postgresql下,表膨胀,无法auto vacuum

比如:

  1. 开发缺乏数据库知识,明明一个update全表就行的,非得在一个事务里,一行一行更新,一共几万行,造成一天多。
  2. PostgreSQL默认外键没有索引,如果这边删除100行,那边没索引,就会对这100个,每个去关联表全表扫描删除对应行。100 * 另外一张表长,这是很恐怖的。
  3. 一次性操作太多数据。有一次压测了很多内容,导致7天之后转移shadow_instances的操作,命中了几万虚拟机。然后PostgreSQLWAL日志主从就同步不过来了,造成WAL日志断链,断链完了rebuild,拉起来后还是断链。

索引不合理

  1. 缺少索引
  2. 外键缺少索引
  3. 重复索引(包括组合索引和独立索引的重复)
  4. 不敢加组合索引

PostgreSQL版本太低

外面都9.6,9.7,10什么的,我们还在用9.2.3,很多特性都没有,后续版本的优化都没上,也会影响性能。

参数优化

1
2
3
4
shared_buffers
work_mem
effective_cache_size
maintenance_work_mem

参考

这个专栏的作者是华为专家,下面的员工和我共事过很长时间,如有雷同是正常的。

https://zhuanlan.zhihu.com/p/33255990