小甲师兄

  • 主页
  • 所有文章
  • 标签
所有文章 友链 关于我

小甲师兄

  • 主页
  • 所有文章
  • 标签

linux性能优化系列(2)cpu上下文切换

2019-01-05

测试cpu上下文切换工具

—————————————————————————————————————————————————————————————————————————————
使用sysbench这个工具,这个工具是线程基准测试工具,用来测试线程,这样可以测试调度和cpu上下文切换

1
sysbench --num-threads=10 --max-time=300 --max-requests=10000000 --test=threads run

如何查看系统cpu上下文切换情况

—————————————————————————————————————————————————————————————————————————————

  • 1.vmstat工具可以查看系统的cpu上下文切换

    1
    2
    3
    4
    5
    6
    [root@con01 ~(keystone_admin)]# vmstat 1
    procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
    r b swpd free buff cache si so bi bo in cs us sy id wa st
    8 0 2928 28614520 7066708 11676172 0 0 3 34 0 0 1 5 94 0 0
    4 0 2928 28616408 7066708 11676196 0 0 0 12 8510 19720 2 14 85 0 0
    2 0 2928 28615164 7066708 11676208 0 0 0 116 7205 7983 2 14 85 0 0
  • 2.pidstat加-w

    1
    pidstat -wt -u 5
  • linux 性能

展开全文 >>

ceph osd pg统计

2019-01-05

ceph osd pg统计脚本

————————————————————————————————————————————————————————————————————
这里引用磨磨的一篇博客
http://www.zphj1987.com/2015/10/04/%E6%9F%A5%E8%AF%A2osd%E4%B8%8A%E7%9A%84pg%E6%95%B0/

  • ceph

展开全文 >>

cephfs部署,创建和删除

2019-01-05

cephfs部署

——————————————————————————————————————————————————————————————————————————

  • 1.ceph-deploy安装

    1
    yum install ceph-deploy
  • 2.ceph mds服务安装
    进入到/etc/ceph目录

    1
    2
    ceph-deploy gatherkeys {hostname}
    ceph-deploy mds create {hostmane}:{服务名字}

注意可以在一个节点上面启动多个mds服务,一般推荐一个机器上面跑一个mds服务

cephfs创建

—————————————————————————————————————————————————————————————————————————

  • 1.创建cephfs需要使用的池
    创建数据池
    1
    2
    [root@con01 ceph(keystone_admin)]# ceph osd pool create gkk_data 20 20
    pool 'gkk_data' created

创建元数据池

1
2
[root@con01 ceph(keystone_admin)]# ceph osd pool create gkk_metadata 20 20
pool 'gkk_metadata' created

创建cephfs文件系统

1
2
[root@con01 ceph(keystone_admin)]# ceph fs new gkk gkk_metadata gkk_data
new fs with metadata pool 7 and data pool 8

  • 2.开启多文件系统模式
    默认情况ceph集群只允许创建一套cephfs文件系统,但是可以打开一个flag就可以开始多文件系统模式
    1
    [root@con01 ceph(keystone_admin)]# ceph fs flag set enable_multiple true --yes-i-really-mean-it

打开flag之后创建多个cephfs文件系统如下所示

1
2
3
4
5
6
7
8
9
10
11
12
[root@con01 ceph(keystone_admin)]# ceph -s
cluster 8bb2dc2f-e7f0-400e-952b-842d20e3cc1e
health HEALTH_OK
monmap e1: 3 mons at {con01=10.85.46.87:6789/0,con02=10.85.46.93:6789/0,con03=10.85.46.97:6789/0}
election epoch 12, quorum 0,1,2 con01,con02,con03
fsmap e68: ceph-1/1/1 up gkk-1/1/1 up {[ceph:0]=con03=up:active,[gkk:0]=con01=up:active}, 1 up:standby
osdmap e255: 3 osds: 3 up, 3 in
flags sortbitwise,require_jewel_osds
pgmap v1934353: 4240 pgs, 9 pools, 182 GB data, 104 kobjects
182 GB used, 3607 GB / 3993 GB avail
4240 active+clean
client io 0 B/s rd, 5623 B/s wr, 0 op/s rd, 0 op/s wr

可以看到笔者的集群中有三个mds,有两个cephfs文件系统,名字分别叫ceph和gkk,其中他们分别占用一台mds用来管理元数据服务,分别为con03和con01

  • 3.配置一个文件系统使用多个mds服务,实现使用集群来管理文件系统的元数据
    1
    2
    ceph fs set ceph allow_multimds true --yes-i-really-mean-it
    ceph fs set ceph max_mds 3

此时集群如下:

1
2
3
4
5
6
7
8
9
10
11
cluster 8bb2dc2f-e7f0-400e-952b-842d20e3cc1e
health HEALTH_OK
monmap e1: 3 mons at {con01=10.85.46.87:6789/0,con02=10.85.46.93:6789/0,con03=10.85.46.97:6789/0}
election epoch 12, quorum 0,1,2 con01,con02,con03
fsmap e74: ceph-2/2/2 up gkk-1/1/1 up {[ceph:0]=con03=up:active,[ceph:1]=con02=up:active,[gkk:0]=con01=up:active}
osdmap e255: 3 osds: 3 up, 3 in
flags sortbitwise,require_jewel_osds
pgmap v1934434: 4240 pgs, 9 pools, 182 GB data, 104 kobjects
182 GB used, 3607 GB / 3993 GB avail
4240 active+clean
client io 10459 B/s rd, 2324 B/s wr, 12 op/s rd, 0 op/s wr

可以看到集群没有standby的元数据服务了

cephfs删除

—————————————————————————————————————————————————————————————————————————

  • 1.stop 所有的mds服务

    1
    systemctl stop ceph-mds@{hostname}
  • 2.删除文件系统

    1
    ceph fs rm ceph --yes-i-really-mean-it

此时看一下集群状态

1
2
3
4
5
6
7
8
9
10
11
[root@con01 ceph(keystone_admin)]# ceph -s
cluster 8bb2dc2f-e7f0-400e-952b-842d20e3cc1e
health HEALTH_OK
monmap e1: 3 mons at {con01=10.85.46.87:6789/0,con02=10.85.46.93:6789/0,con03=10.85.46.97:6789/0}
election epoch 12, quorum 0,1,2 con01,con02,con03
osdmap e264: 3 osds: 3 up, 3 in
flags sortbitwise,require_jewel_osds
pgmap v1934517: 4240 pgs, 9 pools, 182 GB data, 104 kobjects
182 GB used, 3607 GB / 3993 GB avail
4240 active+clean
client io 0 B/s rd, 9829 B/s wr, 0 op/s rd, 1 op/s wr

之前fsmap已经没有了

  • cephfs

展开全文 >>

dashboard下载镜像

2018-12-20

问题

最近有一个功能,需要在dashboard增加下载镜像的功能,我们都知道,可以通过dashboard上传镜像,可以通过命令下载镜像,但是不能通过dashboard上传镜像

解决方法

社区是有一个bp,传输门如下:
https://review.openstack.org/#/c/74799/
但是这个bp是基于很老的版本,笔者的openstack是pike版,所以需要做一些修改
修改如下:

  • 1./usr/share/openstack-dashboard/openstack_dashboard/settings.py文件330行修改

    1
    'images_panel': False,
  • 2./usr/share/openstack-dashboard/openstack_dashboard/dashboards/project/images/images/views.py最后的download_image函数修改如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    def download_image(request, **kwargs):
    try:
    image_data = api.glance.image_data(request, kwargs['image_id'])
    image = api.glance.image_get(request, kwargs['image_id'])
    image_format = getattr(image, 'disk_format', 'data')
    response = http.StreamingHttpResponse(
    image_data,
    content_type='application/octet-stream')
    response['Content-Disposition'] = ('attachment; filename="%s.%s"' %
    (kwargs['image_id'], image_format))
    response['Content-Length'] = image.size
    return response
    except Exception:
    msg = _('An error occured during download of image.')
    url = reverse('horizon:project:images:index')
    exceptions.handle(request, msg, redirect=url)
  • 3./usr/share/openstack-dashboard/openstack_dashboard/api/glance.py247行修改如下

    1
    2
    3
    4
    @profiler.trace
    def image_data(request, image_id):
    """Returns an Image object populated with metadata for a given image."""
    return glanceclient(request).images.data(image_id)
  • 4./usr/share/openstack-dashboard/openstack_dashboard/locale/zh_CN/LC_MESSAGES下面的mo对应的文件需要增加

    1
    2
    3
    4
    5
    msgid "Edit Image"
    msgstr "编辑镜像"

    msgid "Download Image"
    msgstr "下载镜像"

ok,重启一下httpd或者apache2服务

  • openstack dashboard horizon

展开全文 >>

k8s:configmap

2018-12-16

1.configmap创建方式

  • 从字符串创建
  • 从env文件创建
  • 从目录创建
  • 从json/yaml文件创建

2.configmap使用

  • 用作pod的环境变量
  • 通过环境变量作为pod命令参数
  • 使用volume将configmap作为文件或者目录直接挂载
  • k8s configmap

展开全文 >>

how to change horizon chinese

2018-12-15

问题描述

最近笔者想修改一下openstack dashboard上面中文,比如我想把”主机聚合”改成”主机集群”,笔者的openstack的版本是Pike,虽然这个问题网上有很多说明,但是不能解决我的全部问题

问题分析

网上都说在/usr/share/openstack-dashboard/openstack_dashboard/locale/zh_CN/LC_MESSAGES这个目录下会有一个po文件,只要把里面的相应中文改成你想要的就可以了,但是Pike版环境上这个目录下面没有这po文件,只有一个mo文件
aa
而且还是二进制文件,打开看不出啥一对乱码
然后笔者想到的是去github的原理里面找po文件
https://sourcegraph.com/github.com/openstack/horizon@master/-/blob/horizon/locale/zh_CN/LC_MESSAGES/django.po
然而。。。。
我在这个文件里面搜索竟然没有找到我要求的中文,什么鬼,那到底在哪里
然后很无奈的全局搜索了一下,最后竟然在上面mo二进制文件中有match,那就说有,那为什么源码的po文件没有,估计应该是编译的时候加入一些

问题解决

那问题就是如果把mo反编译会po,然后修改po,最后在编译会mo
mo到po

1
msgunfmt xxx.mo -o xxx.po

po到mo

1
msgfmt -o xxx.mo xxx.po

  • openstack horizon

展开全文 >>

allow other lvm in lvmgroup

2018-12-15

问题描述:

笔者有一套openstack环境,没有对接ceph,使用的lvm作为虚拟机的存储,虚拟机的系统卷都存放在一个叫做vms-group的lvmgroup中,不过其下有其他人创建了一个lvm的逻辑卷,但是不是给openstack的用的,导致OpenStack hypvisor中的local_gb_free不准确,导致虚拟机创建有问题

原理分析

通过源码分析可以知道,nova compute会定时同步hypervisor的资源,包括存储,那按理来说,从宿主机上面获取lvmgroup的总量和使用量应该是没有问题的,那为什么会导致出现最后hypervisor上面的资源统计不正确呢?
我们来看一下代码,代码是不会说谎的

1
2
3
4
5
6
7
def update_available_resource(self, context):

LOG.info(_LI("Auditing locally available compute resources for "
"node %(node)s"),
{'node': self.nodename})
resources = self.driver.get_available_resource(self.nodename)
...

在nova/compute/resource_tracker.py文件中的update_available_resource函数调用了get_available_resource函数,这个函数其实就是调用libvirt的接口,其实调用的是如下的命令

1
vgs --noheadings --nosuffix --separator --units b -o vg_size,vg_free vg

就是取出vg这个逻辑组的总量和剩余量,而使用量是这样计算的

1
2
3
{'total': int(info[0]),
'free': int(info[1]),
'used': int(info[0]) - int(info[1])}

可以看出这个使用量是包括这个lvmgroup中不是openstack创建使用的,那为什么最后的hypervisor统计不正确呢
我们继续往下看

1
2
3
def update_available_resource(self, context):
.....
self._update_available_resource(context, resources)

在update_available_resource函数中还调用了更新可用量的请求,_update_available_resource

1
2
3
4
5
def _update_available_resource(self, context, resources):

....
self._update_usage_from_instances(context, resources, instances)
.....

这个函数调用_update_usage_from_instances函数,统计虚拟机的使用量,我们好像有点眉目了,我们可以猜测一下,通过统计数据库中虚拟机存盘的使用量(系统盘)来统计openstack的使用量,这就不包含了lvmgroup中其他用途创建的逻辑卷,是不是这样,我们看一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def _update_usage_from_instances(self, context, resources, instances):

# set some initial values, reserve room for host/hypervisor:
resources['local_gb_used'] = CONF.reserved_host_disk_mb / 1024
resources['memory_mb_used'] = CONF.reserved_host_memory_mb
resources['free_ram_mb'] = (resources['memory_mb'] -
resources['memory_mb_used'])
resources['free_disk_gb'] = (resources['local_gb'] -
resources['local_gb_used'])
resources['current_workload'] = 0
resources['running_vms'] = 0

# Reset values for extended resources
self.ext_resources_handler.reset_resources(resources, self.driver)

for instance in instances:
if instance.vm_state != vm_states.DELETED:
self._update_usage_from_instance(context, resources, instance)

上面关键的是

1
resources['local_gb_used'] = CONF.reserved_host_disk_mb / 1024

local_gb_used这个参数被重新赋值了,之前的值就是通过上面调用命令之后计算出来的也就是真实值,这里进行了重新赋值,赋予了保留值,然后通过遍历数据库中的所有虚拟机,根据它们的机型中定义的系统盘值统计使用量,就是下面这个函数

1
_update_usage_from_instance

最后可以使用的free_gb,就是通过命令取出来的总值,减去预留值,减去虚拟机的使用量,这就导致没有包括其他用途使用的逻辑卷

解决方案

通过修改小修改_update_usage_from_instances函数来解决,笔者通过取两个使用量统计的最大值最为使用量,
一个是通过命令行获取,一个是预留值加上所有虚拟机的总使用量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
def _update_usage_from_instances(self, context, resources, instances):
"""Calculate resource usage based on instance utilization. This is
different than the hypervisor's view as it will account for all
instances assigned to the local compute host, even if they are not
currently powered on.
"""
self.tracked_instances.clear()

# purge old stats and init with anything passed in by the driver
self.stats.clear()
self.stats.digest_stats(resources.get('stats'))

# set some initial values, reserve room for host/hypervisor:
resources['local_gb_used_orig'] = resources['local_gb_used']
resources['local_gb_used'] = 0
resources['memory_mb_used'] = CONF.reserved_host_memory_mb
resources['free_ram_mb'] = (resources['memory_mb'] -
resources['memory_mb_used'])
resources['free_disk_gb'] = (resources['local_gb'] -
resources['local_gb_used'])
resources['current_workload'] = 0
resources['running_vms'] = 0

# Reset values for extended resources
self.ext_resources_handler.reset_resources(resources, self.driver)

for instance in instances:
if instance.vm_state != vm_states.DELETED:
self._update_usage_from_instance(context, resources, instance)

resources['local_gb_used'] = max(resources['local_gb_used'], resources['local_gb_used_orig']) + \
CONF.reserved_host_disk_mb / 1024
resources['free_disk_gb'] = (resources['local_gb'] -
resources['local_gb_used'])

  • openstack lvm

展开全文 >>

linux性能优化系列(1)平均负载

2018-11-29

获取平均负载

————————————————————————————————————————————————————————————————————————————
笔者一般使用两条命令获取平均负载:
uptime

1
2
[root@con02 ~]# uptime
15:37:00 up 814 days, 22:02, 4 users, load average: 0.87, 0.83, 0.82

top

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
top - 15:37:37 up 814 days, 22:02,  4 users,  load average: 1.01, 0.87, 0.84
Tasks: 644 total, 2 running, 642 sleeping, 0 stopped, 0 zombie
%Cpu(s): 4.2 us, 0.9 sy, 0.0 ni, 94.8 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 65858136 total, 8405052 free, 37853144 used, 19599940 buff/cache
KiB Swap: 0 total, 0 free, 0 used. 23213220 avail Mem

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
31919 nova 20 0 6284804 108152 16248 R 35.3 0.2 52:59.05 nova-compute
32023 nova 20 0 499104 95988 3664 S 23.5 0.1 9:26.80 nova-conductor
32005 nova 20 0 499616 96448 3660 S 17.6 0.1 9:23.34 nova-conductor
32014 nova 20 0 499288 96220 3664 S 17.6 0.1 9:19.99 nova-conductor
32025 nova 20 0 499008 95772 3664 S 17.6 0.1 9:24.99 nova-conductor
21167 root 20 0 147744 2452 1388 R 11.8 0.0 0:00.02 top
32028 nova 20 0 499692 96616 3660 S 11.8 0.1 10:28.59 nova-conductor
72 root 20 0 0 0 0 S 5.9 0.0 90:48.89 ksoftirqd/3
7329 root 20 0 41992 23172 21824 S 5.9 0.0 30:39.54 sap1007
7893 qemu 20 0 10.171g 5.320g 12468 S 5.9 8.5 92:06.55 qemu-kvm
10461 qemu 20 0 6319884 224920 12192 S 5.9 0.3 80:59.01 qemu-kvm
13771 glance 20 0 714392 66352 5760 S 5.9 0.1 557:17.33 glance-api
31637 mysql 20 0 15.114g 3.001g 138444 S 5.9 4.8 2315:34 mysqld
32002 nova 20 0 498576 95528 3664 S 5.9 0.1 10:26.97 nova-conductor
32004 nova 20 0 499644 96488 3664 S 5.9 0.1 10:43.25 nova-conductor
32007 nova 20 0 499436 96344 3660 S 5.9 0.1 10:26.56 nova-conductor
32021 nova 20 0 498600 95548 3660 S 5.9 0.1 9:46.67 nova-conductor
1 root 20 0 338680 4888 1940 S 0.0 0.0 80:02.58 systemd
2 root 20 0 0 0 0 S 0.0 0.0 0:42.26 kthreadd
3 root 20 0 0 0 0 S 0.0 0.0 132:42.57 ksoftirqd/0
5 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 kworker/0:0H
8 root rt 0 0 0 0 S 0.0 0.0 10:13.38 migration/0
9 root 20 0 0 0 0 S 0.0 0.0 0:00.01 rcu_bh
10 root 20 0 0 0 0 S 0.0 0.0 0:00.00 rcuob/0
11 root 20 0 0 0 0 S 0.0 0.0 0:00.00 rcuob/1
12 root 20 0 0 0 0 S 0.0 0.0 0:00.00 rcuob/2
13 root 20 0 0 0 0 S 0.0 0.0 0:00.00 rcuob/3
14 root 20 0 0 0 0 S 0.0 0.0 0:00.00 rcuob/4
15 root 20 0 0 0 0 S 0.0 0.0 0:00.00 rcuob/5
16 root 20 0 0 0 0 S 0.0 0.0 0:00.00 rcuob/6
17 root 20 0 0 0 0 S 0.0 0.0 0:00.00 rcuob/7
18 root 20 0 0 0 0 S 0.0 0.0 0:00.00 rcuob/8
19 root 20 0 0 0 0 S 0.0 0.0 0:00.00 rcuob/9
20 root 20 0 0 0 0 S 0.0 0.0 0:00.00 rcuob/10
21 root 20 0 0 0 0 S 0.0 0.0 0:00.00 rcuob/11
22 root 20 0 0 0 0 S 0.0 0.0 0:00.00 rcuob/12
23 root 20 0 0 0 0 S 0.0 0.0 0:00.00 rcuob/13
24 root 20 0 0 0 0 S 0.0 0.0 0:00.00 rcuob/14
25 root 20 0 0 0 0 S 0.0 0.0 0:00.00 rcuob/15
26 root 20 0 0 0 0 S 0.0 0.0 0:00.00 rcuob/16
27 root 20 0 0 0 0 S 0.0 0.0 0:00.00 rcuob/17
28 root 20 0 0 0 0 S 0.0 0.0 0:00.00 rcuob/18
29 root 20 0 0 0 0 S 0.0 0.0 0:00.00 rcuob/19
30 root 20 0 0 0 0 S 0.0 0.0 0:00.00 rcuob/20
31 root 20 0 0 0 0 S 0.0 0.0 0:00.00 rcuob/21
32 root 20 0 0 0 0 S 0.0 0.0 0:00.00 rcuob/22
33 root 20 0 0 0 0 S 0.0 0.0 0:00.00 rcuob/23
34 root 20 0 0 0 0 S 0.0 0.0 5143:32 rcu_sched

负载就是上面的load average: 0.87, 0.83, 0.82,分别表示过去一分钟,五分钟,十五分钟的负载

负载代表什么

——————————————————————————————————————————————————————————————————————————————
上面的负载值代表的其实是系统中的活跃进程数,包括正在运行和不可中断运行(比如一些内核态的写磁盘调度)

如何判断负载是高还是低

——————————————————————————————————————————————————————————————————————————————

  • 1.首先需要查看系统中有多少cpu

    1
    2
    [root@con02 ~]# grep "model name" /proc/cpuinfo | wc -l
    24
  • 2.比较负载值和cpu数目
    如果负载值大于cpu数目,name系统就处于超负载状态

查看分析负载工具

—————————————————————————————————————————————————————————————————————————————

  • 1.mpstat
    查看多个cpu的性能使用情况

  • 2.pidstat
    查看进程的性能

压测工具

—————————————————————————————————————————————————————————————————————————————

  • 1.stress
    这个stress可以压测cpu,内存和磁盘io
  • linux 性能

展开全文 >>

每天5分钟go(5)

2018-10-27

1. 并发机制是在runtime库中实现的

2. go程序理论上可以运行10000个协程

3. M P G模型中

3.1 M值的两种设置方法

  • 初始化的时候可以设置
  • 之后可以通过runtime/debug设置

    3.2 P值的两种设置方法

  • 通过runtime.GOMAXPROCS设置
  • 通过环境变量GOMAXPROCS设置
    p值有一个硬性上限 256
    注意:通过runtime.GOMAXPROCS设置的时候会停止所有p和G,影响程序的性能,所以设置P值的时候一般是在main函数之前

    P有状态

    见go并发实践136页
    P有两个队列,一个是可运行队列,用来存放即将运行的程序,另一个是自由队列,用来重用G,就不需要创建新的G

    go语句的执行顺序

    步骤1:go 语句
    步骤2:生成newporc
    步骤3:本地p和调度器看看有没有可用的自由G,没有新建一个
    步骤4:初始化关联go函数,G的状态以及ID
    步骤5:存到本地P的runnext,然后会把原来runnext上面的G放到p的可运行队列的尾,如果队列满了,则追加到调度器的课运行队列中

    G也有状态

    见go并发实践140页
  • go

展开全文 >>

nova周期性任务分析

2018-10-20

1.周期任务

周期性任务是指每隔指定时间运行一次指定任务,跟linux下面的crontab类似,那nova里面也有很多周期性的任务,比如周期性同步虚拟机的电源状态,周期信息上报计算节点的信息,这些都是在nova-compute服务做的,那这又是怎么实现的呢?

2.从nova-compute服务启动说起

对于周期性的任务的启动,如果是你,你准备在哪里启动他们,思考3秒
1
2
3
好,应该是在nova-compute服务启动的时候就启动这些周期性的任务,至于这些任务是以何种方式运行,比如是以脱离nova-compute进程的外部进程(exec),还是子进程,还是以一个线程又或者是一个协程?我们来具体看一下。

  • 1.从cmd看服务启动流程
    nova的所有服务都从cmd目录下面启动,nova-compute也不例外
    main函数如下
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    def main():
    config.parse_args(sys.argv)
    logging.setup(CONF, 'nova')
    utils.monkey_patch()
    objects.register_all()

    gmr.TextGuruMeditation.setup_autorun(version)

    if not CONF.conductor.use_local:
    block_db_access()
    objects_base.NovaObject.indirection_api = \
    conductor_rpcapi.ConductorAPI()

    server = service.Service.create(binary='nova-compute',
    topic=CONF.compute_topic,
    db_allowed=CONF.conductor.use_local)
    service.serve(server)
    service.wait()

可以看出主要是生成一个service服务对象,这个对象是openstack抽出来的一个基础类,一个类代表一个服务,比如nova-api,nova-compute,这个有很多人已经分析过了,可以google一下
这里要注意我们要关注的是在创建service对象的时候,它有一个manager成员比较重要,我们来具体看一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@classmethod
def create(cls, host=None, binary=None, topic=None, manager=None,
report_interval=None, periodic_enable=None,
periodic_fuzzy_delay=None, periodic_interval_max=None,
db_allowed=True):

if not host:
host = CONF.host
if not binary:
binary = os.path.basename(sys.argv[0])
if not topic:
topic = binary.rpartition('nova-')[2]
if not manager:
manager_cls = ('%s_manager' %
binary.rpartition('nova-')[2])
manager = CONF.get(manager_cls, None)
if report_interval is None:
report_interval = CONF.report_interval
if periodic_enable is None:
periodic_enable = CONF.periodic_enable
if periodic_fuzzy_delay is None:
periodic_fuzzy_delay = CONF.periodic_fuzzy_delay

debugger.init()

service_obj = cls(host, binary, topic, manager,
report_interval=report_interval,
periodic_enable=periodic_enable,
periodic_fuzzy_delay=periodic_fuzzy_delay,
periodic_interval_max=periodic_interval_max,
db_allowed=db_allowed)

return service_obj

manager主要是nvoa-compute的业务处理入口,这个是在service初始化函数的时候,是通过类的动态函数加载的

1
2
3
4
5
6
7
8
9
10
def __init__(self, host, binary, topic, manager, report_interval=None,
periodic_enable=None, periodic_fuzzy_delay=None,
periodic_interval_max=None, db_allowed=True,
*args, **kwargs):
....
self.manager_class_name = manager
....
manager_class = importutils.import_class(self.manager_class_name)
self.manager = manager_class(host=self.host, *args, **kwargs)
....

这个manager对应的代码其实就是ComputeManager函数
aaa
除了manager之外,还有一个periodic_enable属性,用来指示这个service支持周期性事件

1
periodic_enable = CONF.periodic_enable

上面会在service创建完之后,serve服务,就是启动服务,我们跟踪代码
bbb

  • 2.服务start分析
    service函数如下
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    def start(self):

    ...
    if self.periodic_enable:
    if self.periodic_fuzzy_delay:
    initial_delay = random.randint(0, self.periodic_fuzzy_delay)
    else:
    initial_delay = None

    self.tg.add_dynamic_timer(self.periodic_tasks,
    initial_delay=initial_delay,
    periodic_interval_max=
    self.periodic_interval_max)

过滤掉跟周期性调度无关的,可以看到如果service支持周期事件,则会在tg这个协程组中增加一个动态定时器,这个定时器就是用来做周期性任务的,可以看到定时器调用的是self.periodic_tasks,这个函数调用流程如下:
ccc
可以有人会问PeriodicTasks是个啥,嗯,好吧,是时候亮出我的python本事了,解释一下nova的周期性事件调度框架

  • 3.manager继承关系
    我一般比较喜欢看类的继承关系,通过继承关系能看项目的一个数据大概,想当年看ceph代码的时候就是靠着类的继承关系(好像扯远了)
    ddd
    可以看到,我们nova的Computemanager间接继承了PeriodicTasks,是我们的compute的管理对象具有周期调度的能力,中间manager是服务管理对象的基类,nova api,conductor都会有一个,所以上面的periodic_tasks最终会调度到PeriodicTasks这个父类的方法中
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    def run_periodic_tasks(self, context, raise_on_error=False):
    """Tasks to be run at a periodic interval."""
    idle_for = DEFAULT_INTERVAL
    for task_name, task in self._periodic_tasks:
    full_task_name = '.'.join([self.__class__.__name__, task_name])

    spacing = self._periodic_spacing[task_name]
    last_run = self._periodic_last_run[task_name]

    # Check if due, if not skip
    idle_for = min(idle_for, spacing)
    if last_run is not None:
    delta = last_run + spacing - time.time()
    if delta > 0:
    idle_for = min(idle_for, delta)
    continue

    LOG.debug("Running periodic task %(full_task_name)s",
    {"full_task_name": full_task_name})
    self._periodic_last_run[task_name] = _nearest_boundary(
    last_run, spacing)

    try:
    task(self, context)
    except Exception as e:
    if raise_on_error:
    raise
    LOG.exception(_LE("Error during %(full_task_name)s: %(e)s"),
    {"full_task_name": full_task_name, "e": e})
    time.sleep(0)

    return idle_for

可以看出这个代码就是循环调用周期函数,然后执行
这里有一个问题周期性的函数怎么注册的呢,我们可以看到Computemanager类中,很多函数有这个装饰器periodic_task.periodic_task

1
2
@periodic_task.periodic_task
def _check_instance_build_time()

这有什么用呢,好我们分析一下周期任务框架

  • 4.周期任务框架

    4.1周期任务注册

    就是使用上面提到的periodic_task.periodic_task装饰器对一个普通的函数进行注册就可以变成周期性任务函数,可以看一下装饰器函数
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    def periodic_task(*args, **kwargs):

    def decorator(f):
    # Test for old style invocation
    if 'ticks_between_runs' in kwargs:
    raise InvalidPeriodicTaskArg(arg='ticks_between_runs')

    # Control if run at all
    f._periodic_task = True
    f._periodic_external_ok = kwargs.pop('external_process_ok', False)
    if f._periodic_external_ok and not CONF.run_external_periodic_tasks:
    f._periodic_enabled = False
    else:
    f._periodic_enabled = kwargs.pop('enabled', True)
    f._periodic_name = kwargs.pop('name', f.__name__)

    # Control frequency
    f._periodic_spacing = kwargs.pop('spacing', 0)
    f._periodic_immediate = kwargs.pop('run_immediately', False)
    if f._periodic_immediate:
    f._periodic_last_run = None
    else:
    f._periodic_last_run = time.time()
    return f

    return decorator
    else:
    return decorator(args[0])

函数很简单的就是给普通的函数增加了一些属性,可以周期调度的属性,这个是用了python中一切都是对象的特性,对象都可以增加属性

4.2周期任务的加载

通过上面4.1周期性任务注册之后,任务是怎么加载到Computemanager的呢?
我们注意到

1
2
@six.add_metaclass(_PeriodicTasksMeta)
class PeriodicTasks(object):

PeriodicTasks这个类有一个元类,嗯,使用到了python的元编程,一般元类用来可以改变类对象的构造方法,python中的构造方法和初始化方法可以google,这里不是重点

1
2
3
4
5
6
7
class _PeriodicTasksMeta(type):
...
def __init__(cls, names, bases, dict_):
....
for value in cls.__dict__.values():
if getattr(value, '_periodic_task', False):
cls._add_periodic_task(value)

这里面的__init__就是构造方案,就是在生成PeriodicTasks类对象的构造方法,也是生成Computemanager类对象的构造方法,里面有一个_add_periodic_task函数,嗯,这就是把周期性任务函数加入Computemanager的地方,这里的cls不是指代PeriodicTasks类,而是Computemanager,cls.__dict__这个可以找到一个类的所有属性,包括变量和方法,我们上面注册看到,周期性的方法是增加了_periodic_task属性,所以getattr(value, ‘_periodic_task’, False)这个函数就是捞出周期性任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def _add_periodic_task(cls, task):

if task._periodic_spacing < 0:
LOG.info(_LI('Skipping periodic task %(task)s because '
'its interval is negative'),
{'task': name})
return False
if not task._periodic_enabled:
LOG.info(_LI('Skipping periodic task %(task)s because '
'it is disabled'),
{'task': name})
return False

if task._periodic_spacing == 0:
task._periodic_spacing = DEFAULT_INTERVAL

cls._periodic_tasks.append((name, task))
cls._periodic_spacing[name] = task._periodic_spacing
return True

可以看出加入任务主要是在Computemanager对象中增加三个缓存对象_periodic_tasks,_periodic_spacing,以及下面的_periodic_last_run,分别代表任务队列,每个任务的执行间隔,每个任务的上次运行时间点,使用的dict存储
说到这里我们大致知道,在service服务对象创建的时候会生成computemanager对象,生成这个对象的时候会把这个类中使用periodic_task.periodic_task装饰的任务注册到computemanager对象的三个缓存dict中,然后再在service.start启动的时候,会根据是否本服务支持周期任务调度,在协程池中增加一个动态定时器,这个定时器会有周期性的调度在computermanager中注册的周期性任务

  • 5.周期任务是跑在线程还是协程
    那么这些周期任务是怎么运行的呢?这个就需要我们去看一下那个动态定时器
    1
    2
    3
    4
    5
    6
    def add_dynamic_timer(self, callback, initial_delay=None,
    periodic_interval_max=None, *args, **kwargs):
    timer = loopingcall.DynamicLoopingCall(callback, *args, **kwargs)
    timer.start(initial_delay=initial_delay,
    periodic_interval_max=periodic_interval_max)
    self.timers.append(timer)

可以看出定时器就是一个loopcall,只不过是会根据任务指定的时间间隔进行周期性的调度,进入timer.start函数看一下周期性任务是怎么运行的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
def start(self, initial_delay=None, periodic_interval_max=None):
self._running = True
done = event.Event()

def _inner():
if initial_delay:
greenthread.sleep(initial_delay)

try:
while self._running:
idle = self.f(*self.args, **self.kw)
if not self._running:
break

if periodic_interval_max is not None:
idle = min(idle, periodic_interval_max)
LOG.debug('Dynamic looping call %(func_name)r sleeping '
'for %(idle).02f seconds',
{'func_name': self.f, 'idle': idle})
greenthread.sleep(idle)
except LoopingCallDone as e:
self.stop()
done.send(e.retvalue)
except Exception:
LOG.exception(_LE('in dynamic looping call'))
done.send_exception(*sys.exc_info())
return
else:
done.send(True)

self.done = done

greenthread.spawn(_inner)
return self.done

找到答案了,周期任务是以协程方式运行的

1
greenthread.spawn(_inner)

而且是所有的周期任务都在一个协程里面使用死循环运行
到这里,周期性任务框架讲完,那我们如何增加自己的周期性任务呢

3.增加周期性的任务

  • 1.在computemanager中增加需要周期性任务运行的逻辑代码函数然后使用periodic_task.periodic_task装饰器进行修饰
  • 2.可以要增加一个周期任务的间隔时间的配置参数作为spacing参数,例如
    1
    periodic_task.periodic_task(spacing=CONF.scheduler_instance_sync_interval)

4.后续有空可以分享一下ceph代码中的周期性任务调度方法

  • nova periodic

展开全文 >>

« Prev1…345678Next »
© 2022 小甲师兄
Hexo Theme Yilia by Litten
  • 所有文章
  • 友链
  • 关于我

tag:

  • Ceph
  • ceph 编译
  • openstack nova cinder
  • openstack lvm
  • openstack nova
  • ceph
  • Ceph rwl
  • cephfs
  • ceph rbd
  • C tcmu-runner
  • Linux 性能优化实践(5)
  • linux 性能
  • openstack dashboard horizon
  • openstack glance
  • openstack cinder glance
  • go
  • ceph osd
  • openstack horizon
  • python iscsi kernel
  • k8s 张磊
  • k8s configmap
  • k8s cephfs
  • openstack
  • openstack neutron
  • openstack nova ceph
  • rbd
  • rbd iscsi python
  • mysql
  • ceph iscsi rbd
  • libvirt
  • Openstack nova ceph
  • ceph coverage
  • nova periodic
  • ceph log
  • ceph auth
  • harbor docker ceph s3
  • openstack nova cinder glance
  • rbd openstack
  • Ceph Cephfs osdc
  • Ceph rbd
  • openstack nova glance
  • Ceph osd

    缺失模块。
    1、请确保node版本大于6.2
    2、在博客根目录(注意不是yilia根目录)执行以下命令:
    npm i hexo-generator-json-content --save

    3、在根目录_config.yml里添加配置:

      jsonContent:
        meta: false
        pages: false
        posts:
          title: true
          date: true
          path: true
          text: false
          raw: false
          content: false
          slug: false
          updated: false
          comments: false
          link: false
          permalink: false
          excerpt: false
          categories: false
          tags: true
    

  • iceyao
  • int32bit
鹅厂小猿一枚
记录成长点滴