在云计算中,Metadata 并不是一个陌生的概念。从字面上看,Metadata 是元数据的意思。而在云计算中,Metadata 服务能够向虚机注入一些额外的信息,这样虚机在创建之后可以有一些定制化的配置。在 OpenStack 中,Metadata 服务能够向虚机提供主机名,ssh 公钥,用户传入的一些定制数据等其他信息。
在 OpenStack 中,虚机访问 Metadata 服务都是访问 169.254.169.254 这个 IP 地址,但是 OpenStack 不提供这样一个地址的服务,查看 endpoint 也不能找到这样 IP 地址的信息,那虚机是如何获取 Metadata 服务?并且,为什么会是这个 IP 地址?
首先来看为什么会是这个 IP 地址?一方面 169.254.X.X 是保留 IP,可以用来提供服务而不与实际 IP 冲突;另一方面,Metadata 最早是由亚马逊提出,亚马逊的服务中,Metadata 就是从 169.254.169.254 提供服务。很多镜像为了支持亚马逊,将内部获取 Metadata 的 IP 地址写死了。而 OpenStack 为了兼容这些镜像,也采用了这个 IP 地址作为 Metadata 的服务地址。
那既然 169.254.169.254 是保留 IP,OpenStack 中的虚机是如何访问到 Metadata 服务?具体的实现机制在 OpenStack 内部也有过变化。OpenStack 中,Metadata 数据实际上是由 Nova 管理提供的。在早期的 OpenStack 版本中,直接通过 iptables 中的 nat 表,将 169.254.169.254 映射到了真实提供 Metadata 服务的 IP 地址上。可以查看 nova-network 里面的代码:
def metadata_forward(): """Create forwarding rule for metadata.""" if CONF.metadata_host != '127.0.0.1': iptables_manager.ipv4['nat'].add_rule('PREROUTING', '-s 0.0.0.0/0 -d 169.254.169.254/32 ' '-p tcp -m tcp --dport 80 -j DNAT ' '--to-destination %s:%s' % (CONF.metadata_host, CONF.metadata_port)) else: iptables_manager.ipv4['nat'].add_rule('PREROUTING', '-s 0.0.0.0/0 -d 169.254.169.254/32 ' '-p tcp -m tcp --dport 80 ' '-j REDIRECT --to-ports %s' % CONF.metadata_port) iptables_manager.apply()
自从 Folsom 版本,OpenStack Neutron 引入了 Linux network namespaces,使得 IP 地址可以在不同的 namespace 中重叠(overlap)。原有的 Metadata 工作机制将不再适用。最简单的例子,两个具有相同 IP 的虚机请求 Metadata 数据,Nova 在收到请求之后不知道将 Metadata 数据回发给哪个虚机,因为两个虚机的 IP 是一样的!
本部分将介绍在 F 版本之后,OpenStack Metadata service 如何工作在 Linux network namespaces 中。下图简单的描述了 OpenStack Metadata service 的请求路径,本文接下来的部分都将介绍这样的路径在 OpenStack Neutron 中是如何实现的。
以下环境读者可以任选其一:
要使用 Metadata,首先需要做一些的配置,如清单 2 所示。
/etc/nova/nova.conf: [DEFAULT] metadata_listen=192.168.122.157 metadata_listen_port=8775 [neutron] service_metadata_proxy=true metadata_proxy_shared_secret=SECRETE_KEY /neutron/metadata_agent.ini: [DEFAULT] auth_url=http://192.168.122.157:5000/v2.0
auth_region=RegionOne admin_tenant_name=service admin_user=neutron admin_password=admin_pass nova_metadata_ip=192.168.122.157 nova_metadata_port = 8775 metadata_proxy_shared_secret=SECRETE_KEY
清单 2 中的配置非常直观,简单介绍如下:
从配置可以看出,为了使 OpenStack Metadata service 在 Linux network namespaces 中工作,利用了 OpenStack Neutron 中的部分服务。而且还可以看出,neutron-metadata-agent 与 Nova Metadata 服务相连了。接下来介绍一下 neutron-metadata-agent。
neutron-metadata-agent 是从 Grizzly 版本开始引入 OpenStack Neutron。从参考资源中列出的 blueprint 可以看出,neutron-metadata-agent 就是为了解决 OpenStack Metadata service 在 Linux network namespaces 中工作的问题而引入的。在 OpenStack Metadata service 中,neutron-metadata-agent 的作用就是连接 Nova Metadata 服务和网络在 namespace 中的虚机的。
与 Nova Metadata 服务相连,这个很容易理解。从前面的配置项也能看出来,在/neutron/metadata_agent.ini 中配置了需要连接 Nova Metadata 的所有信息。连接网络在 namespace 中的虚机,这个稍微有点复杂。参考之前提过的 blueprint,一起在该 blueprint 中引入的还有一个服务:neutron-ns-metadata-proxy。而 neutron-ns-metadata-proxy 正是运行在 namespace 中,并连接 neutron-metadata-agent 和虚机。
neutron-ns-metadata-proxy 会在两种情况下运行,先来看第一种情况,假设用户创建了 virtual router,并且需要 Metadata 服务的虚机所在的网络连接在 virtual router 上。相关的配置如清单 3 所示:
/neutron/l3_agent.ini: enable_metadata_proxy = True metadata_port = 9697 metadata_proxy_socket = /var/lib/neutron/metadata_proxy
虚机在启动的时候,会向 169.254.169.254 这个 IP 地址请求 Metadata 数据。查看虚机的 route table。可以发现 default route 指向了虚机所在网络的 gateway,如清单 4 所示。
$ route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 0.0.0.0 100.0.0.1 0.0.0.0 UG 0 0 0 eth0 100.0.0.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
也就是说,发向 169.254.169.254 的请求会先发送到 100.0.0.1。接下来看看 virutal router 中如何处理该请求。先看看 virtual router 的 namespace 中的 iptalbes 规则。
Chain neutron-l3-agent-PREROUTING (1 references) 15 900 REDIRECT tcp -- qr-+ * 0.0.0.0/0 169.254.169.254 tcp dpt:80 redir ports 9697 Chain neutron-l3-agent-INPUT (1 references) 0 0 DROP tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:9697
从清单 5 可以看出,所有发向 169.254.169.254 的请求被转发到了 9697 端口,并且外部的访问 9697 端口的请求都被丢弃了。这里的 9697 端口就是清单 3 中所配置的端口。接下来看看 9697 端口相关的信息。
[root@xhh157 ~]# ip netns exec qrouter-2bf026d1-8b3f-4e39-9020-3f1827af2ae0 / netstat -anp | grep 9697 tcp 0 0 0.0.0.0:9697 0.0.0.0:* LISTEN 1047/python [root@xhh157 ~]# ps -ef | grep 1047 neutron 1047 1 0 Jul13 ? 00:00:00 /usr/bin/python /bin/neutron-ns-metadata-proxy --pid_file=/var/lib/neutron/external/pids/2bf026d1-8b3f-4e39-9020-3f1827af2ae0.pid --metadata_proxy_socket=/var/lib/neutron/metadata_proxy --router_id=2bf026d1-8b3f-4e39-9020-3f1827af2ae0 --state_path=/var/lib/neutron --metadata_port=9697 --metadata_proxy_user=1008 --metadata_proxy_group=1008 --log-file=neutron-ns-metadata-proxy-2bf026d1-8b3f-4e39-9020-3f1827af2ae0.log --log-dir=/var/log/neutron
从清单 6 可以看出,9697 端口是进程号为 1047 的进程在监听,而 1047 进程正是 neutron-ns-metadata-proxy。也就是说,虚机中发出的 Metadata 数据请求最终到达了 neutron-ns-metadata-proxy。
neutron-ns-metadata-proxy 再通过 Unix Domain socket 将请求发送到 neutron-metadata-agent。从清单 6 的 neutron-ns-metadata-proxy 进程信息可以看到有这样一个参数 metadata_proxy_socket=/var/lib/neutron/metadata_proxy。这个 socket 文件就是清单 3 中配置的。可以看看监听这个 socket 的进程信息。
[root@xhh157 ~]# netstat -anp | grep metadata unix 2 [ ACC ] STREAM LISTENING 113041 1549/python /var/lib/neutron/metadata_proxy [root@xhh157 ~]# ps -ef | grep 1549 neutron 1549 1 1 Jul13 ? 00:07:36 /usr/bin/python /bin/neutron-metadata-agent --config-file=/etc/neutron/neutron.conf --config-file=/etc/neutron/metadata_agent.ini --log-file=/var/log/neutron/metadata-agent.log
从清单 7 可以看出,通过 Unix Domain socket,neutron-ns-metadata-proxy 将虚机的请求发送到了 neutron-metadata-agent。结合前面的描述,虚机的请求最终会发送到 Nova Metadata 服务。
neutron-ns-metadata-proxy 进程会随 virtual router 的创建而创建,同时也会随 virtual router 的移除而终止。在 OpenStack Kilo 版本中,支持 legacy router,HA router,DVR 与 neutron-ns-metadata-proxy 一同工作。从用户的角度,不用考虑这些不同类型的 virtual router 对 Metadata 的影响。
在本小节开始的时候,说过 neutron-ns-metadata-proxy 会在两种情况下运行。前面介绍了有 virtual router 的情况。其实即使用户没有创建 virtual router,仍然可以在 namespace 的环境下使用 OpenStack Metadata 服务。首先看看相关的配置:
/neutron/dhcp_agent.in: enable_metadata_network = True enable_isolated_metadata = True
从清单 8 可以看出,没有 virtual router 时,Neutron 通过 dhcp namespace 来连接 Metadata 服务和虚机。前面说过,虚机在启动的时候,会向 169.254.169.254 这个 IP 地址请求 Metadata 数据。此时没有连接 virtual router,在虚机的 route table 中会多一条 route 规则。
$ route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 0.0.0.0 200.0.0.1 0.0.0.0 UG 0 0 0 eth0 169.254.169.254 200.0.0.2 255.255.255.255 UGH 0 0 0 eth0 200.0.0.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
对比清单 9 和清单 4 可以看出,多出一条针对 169.254.169.254 的 route 规则。这条规则会直接把 Metadata 数据请求发送到 dhcp server 上。在 dhcp 的 namespace 里面,neutron-ns-metadata-proxy 直接监听在 80 端口,所以在 iptables 里面看不到什么特别的规则。neutron-ns-metadata-proxy 与 neutron-metadata-agent 的连接与前面一样,这里就不再赘述了。
为什么这里 neutron-ns-metadata-proxy 可以直接监听在 80 端口,而前面不行?因为 virtual router 的 namespace 要承接 L3 服务,80 端口作为一个常用的端口如果被 Metadata 占用将非常不方便。而在 dhcp 的 namespace 中,80 端口本来没有使用,就没有必要做转发,可以直接使用。
在 dhcp namespace 中,neutron-ns-metadata-proxy 进程会随 network 的 dhcp server 的创建而创建。同样的,从用户的角度不用考虑管理 neutron-ns-metadata-proxy 进程。
因为 Linux network namespaces 的引入,使得原本的简单通过 iptables 转发 169.254.169.254 来连接 Metadata 服务不再适用。Neutron 通过自身的实现,将虚机在 namespace 中请求 Metadata 数据经过一系列的转发,最终与 Nova Metadata 服务连接。