由于neutron-metadata-agent是控制节点上的进程,因此和Nova Metadata服务肯定是通的, OpenStack虚拟机如何访问Nova Metadata服务问题基本就解决了。
- curl 169.254.169.254 -> haproxy(80端口) -> UNIX Socket文件 -> neutron-metadata-agent -> nova-api-metadata
即一共需要三次转发。
但是Nova Metadata服务如何知道是哪个虚拟机发送过来的请求呢?换句话说,如何获取该虚拟机的uuid,我们将在下一章介绍。
4 Metadata服务如何获取虚拟机信息
前一章介绍了OpenStack虚拟机如何通过169.254.169.254到达Nova Metadata服务,那到达之后如何判断是哪个虚拟机发送过来的呢?
OpenStack是通过neutron-metadata-agent获取虚拟机的uuid的。我们知道,在同一个Neutron network中,即使有多个subnet,也不允许IP重复,即通过IP地址能够唯一确定Neutron的port信息。而neutron port会设置device_id标识消费者信息,对于虚拟机来说,即虚拟机的uuid。
因此neutron-metadata-agent通过network uuid以及虚拟机ip即可获取虚拟机的uuid。
不知道大家是否还记得在haproxy配置文件中存在一条配置项:
- http-request add-header X-Neutron-Network-ID 2c4b658c-f2a0-4a17-9ad2-c07e45e13a8a
即haproxy转发之前会把network id添加到请求头部中,而IP可以通过HTTP的头部X-Forwarded-For中获取。因此neutron-metadata-agent具备获取虚拟机的uuid以及project id(租户id)条件,我们可以查看neutron-metadata-agent获取虚拟机uuid以及project id实现,代码位于neutron/agent/metadata/agent.py:
- def _get_instance_and_tenant_id(self, req):
- remote_address = req.headers.get('X-Forwarded-For')
- network_id = req.headers.get('X-Neutron-Network-ID')
- router_id = req.headers.get('X-Neutron-Router-ID')
-
- ports = self._get_ports(remote_address, network_id, router_id)
- if len(ports) == 1:
- return ports[0]['device_id'], ports[0]['tenant_id']
- return None, None
如果谁都可以伪造Metadata请求获取任何虚拟机的metadata信息,显然是不安全的,因此在转发给Nova Metadata服务之前,还需要发一个secret:
- def _sign_instance_id(self, instance_id):
- secret = self.conf.metadata_proxy_shared_secret
- secret = encodeutils.to_utf8(secret)
- instance_id = encodeutils.to_utf8(instance_id)
- return hmac.new(secret, instance_id, hashlib.sha256).hexdigest()
metadata_proxy_shared_secret需要管理员配置,然后组合虚拟机的uuid生成一个随机的字符串作为key。
最终,neutron-metadata-agent会把虚拟机信息放到头部中,发送到Nova Metadata服务的头部信息如下:
- headers = {
- 'X-Forwarded-For': req.headers.get('X-Forwarded-For'),
- 'X-Instance-ID': instance_id,
- 'X-Tenant-ID': tenant_id,
- 'X-Instance-ID-Signature': self._sign_instance_id(instance_id)
- }
此时Nova Metadata就可以通过虚拟机的uuid查询metadata信息了,代码位于nova/api/metadata/base.py:
- def get_metadata_by_instance_id(instance_id, address, ctxt=None):
- ctxt = ctxt or context.get_admin_context()
- attrs = ['ec2_ids', 'flavor', 'info_cache',
- 'metadata', 'system_metadata',
- 'security_groups', 'keypairs',
- 'device_metadata']
- try:
- im = objects.InstanceMapping.get_by_instance_uuid(ctxt, instance_id)
- except exception.InstanceMappingNotFound:
- LOG.warning('Instance mapping for %(uuid)s not found; '
- 'cell setup is incomplete', {'uuid': instance_id})
- instance = objects.Instance.get_by_uuid(ctxt, instance_id,
- expected_attrs=attrs)
- return InstanceMetadata(instance, address)
-
- with context.target_cell(ctxt, im.cell_mapping) as cctxt:
- instance = objects.Instance.get_by_uuid(cctxt, instance_id,
- expected_attrs=attrs)
- return InstanceMetadata(instance, address)
5 在虚拟机外部如何获取虚拟机metadata (编辑:晋中站长网)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|