背景
OpenStack Nova限制虚拟机网速资源是通过设置Nova Flavor(云主机类型)的元数据来实现的,Nova在创建虚拟机的时候根据Flavor所设置的元数据生成对应的资源限制字段libvirt xml文件,最终作用在kvm虚拟机上。如果虚拟机运行期间管理员想针对虚拟机资源做限制,目前OpenStack只能通过Nova resize进行Flavor的更改,但是resize(实际上就是冷迁移)过程中会重启虚拟机,如果不是线上业务,都好办。
Flavor资源限制元数据设置参考:https://wiki.openstack.org/wiki/InstanceResourceQuota
libvirt xml format参考: https://libvirt.org/formatdomain.html
Nova可限制的资源有:
- CPU,调用libvirt,底层cgroup实现。但是cgroup限制不了cpu使用率;限制进程的cpu使用率推荐使用cpulimit https://github.com/opsengine/cpulimit, 这里也顺便介绍个模拟cpu使用率的工具:https://github.com/beloglazov/cpu-load-generator
- 磁盘IO,调用libvirt,libvirt层有两种实现,一种是IO throttling by qemu,另一种是blkiotune feature调用cgroup实现,Nova用的是前者;这两种有何别呢?cgroup不支持non-host-block IO限制,诸如网络文件系统 (NFS、Glusterfs) 或者其它qemu远程块存储(Ceph/rbd, sheepdog) 都不支持;详情参考: https://www.redhat.com/archives/libvir-list/2011-September/msg00003.html。
- Network,调用libvirt,底层tc实现,而不是cgroup实现,因为cgroup是进程级别限制,这样虚拟机上的所有网卡都会受影响;虽然如此,不过Nova那边还不支持单独网卡的限制,只能设置inbound和outbound。 Nova目前并没有针对内存限制的实现,社区上很早之前就有bp了:https://blueprints.launchpad.net/nova/+spec/flavor-quota-memory
环境
CentOS 7.2 (OpenStack Ocata)单节点
具体实现
本次demo以在线限制虚拟机的某块网卡为例, 仅供参考。 添加server_set_interface api,得益于stevedore库插件式api特点
vim /usr/lib/python2.7/site-packages/nova-15.0.3-py2.7.egg-info/entry_points.txt
server_password = nova.api.openstack.compute.server_password:ServerPassword
server_tags = nova.api.openstack.compute.server_tags:ServerTags
server_usage = nova.api.openstack.compute.server_usage:ServerUsage
server_set_interface = nova.api.openstack.compute.server_set_interface:ServerSetInterface # 此处添加
servers = nova.api.openstack.compute.servers:Servers
services = nova.api.openstack.compute.services:Services
shelve = nova.api.openstack.compute.shelve:Shelve
server_set_interface api入口代码,继承extensions扩展nova api
[root@test-aio-2 nova(keystone_admin)]# cat /usr/lib/python2.7/site-packages/nova/api/openstack/compute/server_set_interface.py
"""Server set interface management extension."""
from nova.api.openstack import common
from nova.api.openstack import extensions
from nova.api.openstack import wsgi
from nova import compute
ALIAS = 'os-server-interface'
class Controller(wsgi.Controller):
def __init__(self, *args, **kwargs):
super(Controller, self).__init__(*args, **kwargs)
self.compute_api = compute.API()
@wsgi.action('setInterface')
@wsgi.response(202)
@extensions.expected_errors((400, 404, 409, 501))
def set_interface(self, req, id, body):
context = req.environ['nova.context']
interface = body['setInterface']['interface']
rate = body['setInterface']['rate']
instance = common.get_instance(self.compute_api, context, id)
info = self.compute_api.set_interface(context, instance, interface, rate)
return info
class ServerSetInterface(extensions.V21APIExtensionBase):
"""Server set interface support."""
name = "ServerSetInterface"
alias = ALIAS
version = 1
def get_controller_extensions(self):
controller = Controller()
extension = extensions.ControllerExtension(self, 'servers', controller)
return [extension]
def get_resources(self):
return []
self.compute_api.set_interface封装
[root@test-aio-2 nova(keystone_admin)]# vim /usr/lib/python2.7/site-packages/nova/compute/api.py
@check_instance_lock
@check_instance_cell
@check_instance_state(vm_state=[vm_states.ACTIVE])
def set_interface(self, context, instance, interface=None, rate=None):
self._record_action_start(context, instance,
instance_actions.SET_INTERFACE)
info = self.compute_rpcapi.set_interface(context,
instance=instance,
interface=interface,
rate=rate)
return info
self.compute_rpcapi.set_interface rpc客户端调用接口封装
[root@test-aio-2 nova(keystone_admin)]# vim /usr/lib/python2.7/site-packages/nova/compute/rpcapi.py
def set_interface(self, ctxt, instance, interface, rate):
version = '4.0'
cctxt = self.router.by_instance(ctxt, instance).prepare(
server=_compute_host(None, instance), version=version)
return cctxt.call(ctxt, 'set_interface',
instance=instance, interface=interface,
rate=rate)
rpc server接口封装
[root@test-aio-2 nova(keystone_admin)]# vim /usr/lib/python2.7/site-packages/nova/compute/manager.py
@wrap_exception()
@reverts_task_state
@wrap_instance_event(prefix='compute')
@wrap_instance_fault
def set_interface(self, context, instance, interface, rate):
try:
info = self.driver.set_interface(instance, interface, rate)
LOG.info(_LI("Interface set"), instance=instance)
except NotImplementedError:
LOG.warning(_LW('set_interface is not implemented '
'by this driver or guest instance.'),
instance=instance)
return info
self.driver.set_interface接口封装
[root@test-aio-2 nova(keystone_admin)]# vim /usr/lib/python2.7/site-packages/nova/virt/libvirt/driver.py
def set_interface(self, instance, interface, rate):
guest = self._host.get_guest(instance)
try:
xml = guest.get_xml_desc()
tree = etree.fromstring(xml)
ifaces = tree.findall('devices/interface/target')
i = ifaces[int(interface)-1].get('dev')
params = {'outbound.peak': rate,
'inbound.peak': rate,
'inbound.burst': rate,
'inbound.average': rate,
'outbound.average': rate,
'outbound.burst': rate}
guest.set_interface(i, params)
except libvirt.libvirtError as ex:
error_code = ex.get_error_code()
msg = (_('Error from libvirt while set interface '
'"%(interface)s": [Error Code %(error_code)s] %(ex)s')
% {'interface': interface, 'error_code': error_code, 'ex': ex})
raise exception.InternalError(msg)
return guest.get_interface(i)
guest.set_interface和guest.get_interface接口
[root@test-aio-2 nova(keystone_admin)]# vim /usr/lib/python2.7/site-packages/nova/virt/libvirt/guest.py
def get_interface(self, interface):
"""Get interface rate."""
return self._domain.interfaceParameters(interface, 0)
def set_interface(self, interface, params):
"""Configures interface rate."""
self._domain.setInterfaceParameters(interface, params, 0)
测试
重启openstack-nova-api、openstack-nova-compute服务,在/var/log/nova/nova-api.log日志中看到o s-server-interface api extention已经加载成功了。
[root@test-aio-2 ~(keystone_admin)]# less /var/log/nova/nova-api.log | grep os-server-interface
2017-07-12 08:34:14.363 31376 INFO nova.api.openstack [req-015fb839-5a37-4630-8655-aa1967ffbcea - - - - -] Loaded extensions: ['extensions', 'flavors', 'image-metadata', 'image-size', 'images', 'ips', 'limits', 'os-admin-actions', 'os-admin-password', 'os-agents', 'os-aggregates', 'os-assisted-volume-snapshots', 'os-attach-interfaces', 'os-availability-zone', 'os-baremetal-nodes', 'os-block-device-mapping', 'os-cells', 'os-certificates', 'os-cloudpipe', 'os-config-drive', 'os-console-auth-tokens', 'os-console-output', 'os-consoles', 'os-create-backup', 'os-deferred-delete', 'os-evacuate', 'os-extended-availability-zone', 'os-extended-server-attributes', 'os-extended-status', 'os-extended-volumes', 'os-fixed-ips', 'os-flavor-access', 'os-flavor-extra-specs', 'os-flavor-manage', 'os-flavor-rxtx', 'os-floating-ip-dns', 'os-floating-ip-pools', 'os-floating-ips', 'os-floating-ips-bulk', 'os-fping', 'os-hide-server-addresses', 'os-hosts', 'os-hypervisors', 'os-instance-actions', 'os-instance-usage-audit-log', 'os-keypairs', 'os-lock-server', 'os-migrate-server', 'os-migrations', 'os-multinic', 'os-multiple-create', 'os-networks', 'os-networks-associate', 'os-pause-server', 'os-quota-class-sets', 'os-quota-sets', 'os-remote-consoles', 'os-rescue', 'os-scheduler-hints', 'os-security-group-default-rules', 'os-security-groups', 'os-server-diagnostics', 'os-server-external-events', 'os-server-groups', 'os-server-interface', 'os-server-password', 'os-server-tags', 'os-server-usage', 'os-services', 'os-shelve', 'os-simple-tenant-usage', 'os-suspend-server', 'os-tenant-networks', 'os-used-limits', 'os-user-data', 'os-virtual-interfaces', 'os-volumes', 'server-metadata', 'server-migrations', 'servers', 'versions']
2017-07-12 08:34:14.847 31376 INFO nova.api.openstack [req-015fb839-5a37-4630-8655-aa1967ffbcea - - - - -] Loaded extensions: ['extensions', 'flavors', 'image-metadata', 'image-size', 'images', 'ips', 'limits', 'os-admin-actions', 'os-admin-password', 'os-agents', 'os-aggregates', 'os-assisted-volume-snapshots', 'os-attach-interfaces', 'os-availability-zone', 'os-baremetal-nodes', 'os-block-device-mapping', 'os-cells', 'os-certificates', 'os-cloudpipe', 'os-config-drive', 'os-console-auth-tokens', 'os-console-output', 'os-consoles', 'os-create-backup', 'os-deferred-delete', 'os-evacuate', 'os-extended-availability-zone', 'os-extended-server-attributes', 'os-extended-status', 'os-extended-volumes', 'os-fixed-ips', 'os-flavor-access', 'os-flavor-extra-specs', 'os-flavor-manage', 'os-flavor-rxtx', 'os-floating-ip-dns', 'os-floating-ip-pools', 'os-floating-ips', 'os-floating-ips-bulk', 'os-fping', 'os-hide-server-addresses', 'os-hosts', 'os-hypervisors', 'os-instance-actions', 'os-instance-usage-audit-log', 'os-keypairs', 'os-lock-server', 'os-migrate-server', 'os-migrations', 'os-multinic', 'os-multiple-create', 'os-networks', 'os-networks-associate', 'os-pause-server', 'os-quota-class-sets', 'os-quota-sets', 'os-remote-consoles', 'os-rescue', 'os-scheduler-hints', 'os-security-group-default-rules', 'os-security-groups', 'os-server-diagnostics', 'os-server-external-events', 'os-server-groups', 'os-server-interface', 'os-server-password', 'os-server-tags', 'os-server-usage', 'os-services', 'os-shelve', 'os-simple-tenant-usage', 'os-suspend-server', 'os-tenant-networks', 'os-used-limits', 'os-user-data', 'os-virtual-interfaces', 'os-volumes', 'server-metadata', 'server-migrations', 'servers', 'versions']
使用curl测试api
#interface: 网卡序号
#rate: 速率,单位字节
#获取token,可以使用openstack token
curl -g -i -X POST \ http://172.16.234.41:8774/v2.1/674bbd3aad0942f593487027e8df14c2/servers/0ced1991-f5dd-4906-b1a7-82997bb74363/action \
-H "X-Auth-Token: 3e62cd28bfb5463b941d35b274372df6" \
-H "Content-Type: application/json" \
-d '{"setInterface": {"interface": 1, "rate": 10240}}'
查看虚拟机libvirt xml文件,如果有bandwidth字段,说明已经生效
[root@test-aio-2 ~(keystone_admin)]# virsh dumpxml 12 | grep -A 10 bandwidth
TODO
- nova policy集成
- api body schema校验
- 虚拟机硬重启后限速依然生效
- nova cell集成
参考链接
- https://git.cs.umu.se/cklein/libvirt/commit/8b29c4598609d9b22a39e6f6bf3bf7afe8303faa
- http://10616534.blog.51cto.com/10606534/1878609
「真诚赞赏,手留余香」
真诚赞赏,手留余香
使用微信扫描二维码完成支付