metadata在OpenStack中的使用(1)

2014-03-25 Lingxian Kong 更多博文 » 博客 » GitHub »

原文链接 https://lingxiankong.github.io/2014-03-25-metadata.html
注:以下为加速网络访问所做的原文缓存,经过重新格式化,可能存在格式方面的问题,或偶有遗漏信息,请以原文为准。


本文原本是在我的CSDN博客里发布,时隔几个月,重新温习一下,顺手完善一些内容,发在这里。(深藏功与名)

版本:Havana
部署:多节点
网络类型:vlan

之前的一篇blog中碰到了虚拟机访问169.254.169.254的问题,在F版中,会在网络节点上做NAT转换,直接访问nova的metadata服务,但这种方法,在使用namespace时就不生效了,因为namespace支持IP地址重叠,这样nova就无法区分到底是哪个虚拟机请求metadata。

该问题在G版得到解决,blueprint在此。采取的方法是在HTTP头部识别是哪个虚拟机。同时,G版在neutron中加入了两个服务:namespace metadata proxy和metadata agent。一个虚拟机访问169.254.169.254的流程如下图:

在细讲流程之前,估计很多人并不清楚metadata的作用和由来。不懂的童鞋看这里

以下所有的命令都是在真实环境上的执行,虚拟机的镜像是经典的cirros。

虚拟机发出请求

虚拟机启动时会访问169.254.169.254获取一些内容。这个从我之前出错的日志中就可以看出来:
'http://169.254.169.254/2009-04-04/meta-data/instance-id' failed [0/120s]: http error [404]

在虚拟机内部访问169.254.169.254,看一下返回什么内容:

$ curl http://169.254.169.254/2009-04-04/meta-data
ami-id
ami-launch-index
ami-manifest-path
block-device-mapping/
hostname
instance-action
instance-id
instance-type
kernel-id
local-hostname
local-ipv4
placement/
public-hostname
public-ipv4
public-keys/
ramdisk-id
reservation-id
security-groups

那么这些内容是如何获取到的?因为虚拟机内部没有特殊的路由,所以数据包会直接发送到虚拟机的默认网关,而默认网关是在network node上。虚拟机的网卡信息如下:

$ ip -4 address show dev eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast qlen 1000
    inet 10.10.0.45/24 brd 10.10.0.255 scope global eth0

虚拟机的路由信息:

$ ip route
default via 10.10.0.1 dev eth0 
10.10.0.0/24 dev eth0  src 10.10.0.45  

namespace-metadata-proxy

因为使用了namespace,在network node上每个namespace里都会有相应的iptables规则和网络设备。
先来看看对应的iptables规则(省略了无关的规则):

n781dba536f96:~ # ip netns list
qdhcp-df4191cb-6303-415b-a5b1-7708809f9a0f
qrouter-c43ebbe2-8f20-42c3-bf03-f87f7d062f94
n781dba536f96:~ # ip netns exec qrouter-c43ebbe2-8f20-42c3-bf03-f87f7d062f94 iptables-save
-A neutron-l3-agent-PREROUTING -d 169.254.169.254/32 -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 9697
-A neutron-l3-agent-INPUT -d 127.0.0.1/32 -p tcp -m tcp --dport 9697 -j ACCEPT

iptables规则中,会把目的地址169.254.169.254的数据包,重定向到本地端口9697,那么看一下network node上,在该端口的监听进程:

n781dba536f96:~ # ip netns exec qrouter-c43ebbe2-8f20-42c3-bf03-f87f7d062f94 netstat -nlpt | grep 9697
tcp        0      0 0.0.0.0:9697            0.0.0.0:*               LISTEN      32120/python 

进程号32120,看一下这个进程:

n781dba536f96:~ # ps -f --pid 32120 | fold -s -w 90
UID        PID  PPID  C STIME TTY          TIME CMD
root     32120     1  0 07:00 ?        00:00:00 /usr/bin/python 
/usr/bin/neutron-ns-metadata-proxy 
--pid_file=/var/lib/neutron/external/pids/c43ebbe2-8f20-42c3-bf03-f87f7d062f94.pid 
--metadata_proxy_socket=/var/lib/neutron/metadata_proxy 
--router_id=c43ebbe2-8f20-42c3-bf03-f87f7d062f94 --state_path=/var/lib/neutron 
--metadata_port=9697 --debug 
--log-file=neutron-ns-metadata-proxy-c43ebbe2-8f20-42c3-bf03-f87f7d062f94.log 
--log-dir=/var/log/neutron 

可能很多人想知道,这个进程从何而来,解释这个问题,可能要贴点代码:

def _spawn_metadata_proxy(self, router_info):  
    def callback(pid_file):  
        proxy_cmd = ['neutron-ns-metadata-proxy',  
                     '--pid_file=%s' % pid_file,  
                     '--router_id=%s' % router_info.router_id,  
                     '--state_path=%s' % self.conf.state_path,  
                     '--metadata_port=%s' % self.conf.metadata_port]  
        proxy_cmd.extend(config.get_log_args(  
            cfg.CONF, 'neutron-ns-metadata-proxy-%s.log' %  
            router_info.router_id))  
        return proxy_cmd  

    pm = external_process.ProcessManager(  
        self.conf,  
        router_info.router_id,  
        self.root_helper,  
        router_info.ns_name())  
    pm.enable(callback)  

可见,启用namespace场景下,对于每一个router,都会创建这样一个进程。该进程监听9697端口,其主要功能:
1、向请求头部添加X-Forwarded-For和X-Neutron-Router-ID,分别表示虚拟机的fixedIP和router的ID

也就是说,要使用metadata服务,系统中必须有router

2、将请求代理至Unix domain socket(/var/lib/neutron/metadata_proxy)

在进行问题定位过程中,可以在日志中搜索"Request: "关键字来查询metadata proxy收到的消息。

Neutron Metadata Agent

network node上的metadata agent监听/var/lib/neutron/metadata_proxy:

n781dba536f96:~ # netstat -lxp | grep metadata 
unix  2      [ ACC ]     STREAM     LISTENING     122282583 31855/python        /var/lib/neutron/metadata_proxy
n781dba536f96:~ # ps -f --pid 31855 | fold -s
UID        PID  PPID  C STIME TTY          TIME CMD
108      31855     1  0 11:08 ?        00:00:00 /usr/bin/python 
/usr/bin/neutron-metadata-agent --config-file /etc/neutron/neutron.conf 
--config-file /etc/neutron/metadata_agent.ini

该进程的功能是,根据请求头部的X-Forwarded-For、X-Neutron-Network-ID以及X-Neutron-Router-ID参数,向Neutron service查询port信息,进而查询虚拟机ID,然后向Nova Metadata服务发送请求(默认端口8775),消息头:X-Forwarded-For,X-Instance-ID、X-Tenant-ID、X-Instance-ID-Signature分别表示虚拟机的fixedIP,虚拟机ID,虚拟机所属的租户和虚拟机ID的签名(至于为什么要加租户ID,可以参考这个bug)。

在进行问题定位过程中,可以在日志中搜索"Request: "关键字来查询metadata agent收到的消息。

Nova Metadata Service

Nova的metadata service是随着nova-api启动的,会同时启动三个服务:ec2, osapi_compute, metadata。虚拟机访问169.254.169.254的返回内容,其实就是metadata服务向nova-conductor查询后,返回给network node上的metadata agent,再由metadata agent返回给metadata proxy,最后返回给虚拟机的。

root@controller231:~# netstat -nlpt | grep 8775  
tcp        0      0 0.0.0.0:8775            0.0.0.0:*               LISTEN      1714/python       
root@controller231:~# ps -f --pid 1714  
UID        PID  PPID  C STIME TTY          TIME CMD  
nova      1714     1  0 16:40 ?        00:00:01 /usr/bin/python /usr/bin/nova-api --config-file=/etc/nova/nova.conf 

Nova中涉及到的配置项:

service_neutron_metadata_proxy=True
neutron_metadata_proxy_shared_secret=password