建立 instance 的時候,我們希望對 instance 進行一些額外的配置,比如:添加 SSH 金鑰、配置 hostname 等等。這可以透過metadata service完成。Instance 啟動時,先會向 Metadata Service 請求並獲取自己的 metadata,instance 的 cloud-init會根據 metadata 完成這些額外的配置工作。而metadata service 所用的 IP是169.254.169.254。但是169.254.0.0/16
是屬於保留的網段,VM正常是無法存取這個IP,今天就讓我們研究OpenStack是如何讓VM存取這個IP。
查看instance裡的route, 發覺往metadata server169.254.169.254
,是透過instance的eth0往同一個網段的172.16.100.2
。
$ route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 172.16.100.1 0.0.0.0 UG 1002 0 0 eth0
169.254.169.254 172.16.100.2 255.255.255.255 UGH 1002 0 0 eth0
172.16.100.0 0.0.0.0 255.255.255.0 U 1002 0 0 eth0
檢查Openstack有 172.16.100.2
IP的port, 其實會對應至OpenStack的Network裡其中一個device owner為 network:distributed 的port.
$ openstack port list --network n1 --device-owner network:distributed
+--------+------+-------------------+----------------------------------------+--------+
| ID | Name | MAC Address | Fixed IP Addresses | Status |
+--------+------+-------------------+----------------------------------------+--------+
| 06ff4f | | fa:16:3e:9d:cc:52 | ip_address='172.16.100.2', subnet_id=' | DOWN |
| | | | 1dd3a923-6366-4754-9db2-1b65eb8391ee' | |
+--------+------+-------------------+----------------------------------------+--------+
還記得在Day-10: OpenStack-OVN-主機 三者間的關係裡曾經查過 tap76af9baf-90
和logical switch port 06ff4f
是透過veth pair,將ovnmeta-76af9baf-9fa4-4631-8f1d-5610ef87a036
namespace綁定到logical switch port。其實就像下圖所表示的。
最後再到ovnmeta-76af9baf-9fa4-4631-8f1d-5610ef87a036
namespace確認,172.16.100.2
與169.254.169.254都在同一個interface上。因此,instance可以和位在Linux Namespace裡的metadata server溝通。
ip netns exec ovnmeta-76af9baf-9fa4-4631-8f1d-5610ef87a036 ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: tap53f016af-91@if17: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether fa:16:3e:9d:cc:52 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.16.100.2/24 brd 172.16.100.255 scope global tap53f016af-91
valid_lft forever preferred_lft forever
inet 169.254.169.254/32 brd 169.254.169.254 scope global tap53f016af-91
valid_lft forever preferred_lft forever
在instance嘗試透過metadata service取得instance 的hostname
$ curl http://169.254.169.254/2009-04-04/meta-data/local-hostname
vm-1.novalocal$
利用ovn-trace
檢查instance和metadata server之間,會由06ff4f
這個localport送出。
NETWORK_NAME=n1
VM_NAME=vm_1
lsw=neutron-`openstack network list --name ${NETWORK_NAME} -f value -c ID`
lsp=`openstack port list --network ${NETWORK_NAME} --server ${VM_NAME} -f value -c ID`
meta_mac=`openstack port list --device-owner network:distributed --network ${NETWORK_NAME} -f value -c mac_address`
vm_mac=`openstack port list --network ${NETWORK_NAME} --server ${VM_NAME} -f value -c mac_address`
ovn-trace --minimal ${lsw} \
"inport == \"${lsp}\" &&
eth.src == ${vm_mac} &&
eth.dst == ${meta_mac}"
output("06ff4f");
到目前,我們只是說明了在instance是可以和與169.254.169.254連通。原因是169.254.169.254和172.16.100.2這個IP其實是屬於同一個namespace的IP. 透過localport
的特性,在同一個主機上的VM,可以和同一主機上的ovnmeta namespace通訊。但是ovnmeta namespace本身並不直接回傳VM所要的metadata,對metadata 的請求,是經過三層的轉發,由nova提供相關的資訊給VM本身。整體流程像下圖一樣。
如今天一開始看到的,在VM裡查看route資訊,可以看到要往metadata service的request,都會被轉送至172.16.100.2
$ route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 172.16.100.1 0.0.0.0 UG 1002 0 0 eth0
169.254.169.254 172.16.100.2 255.255.255.255 UGH 1002 0 0 eth0
172.16.100.0 0.0.0.0 255.255.255.0 U 1002 0 0 eth0
進一步查看ovnmeta namespace裡有那些TCP的服務正在listen,可以看到haproxy正在使用80 port.
ip netns exec ovnmeta-76af9baf-9fa4-4631-8f1d-5610ef87a036 netstat -lntp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 169.254.169.254:80 0.0.0.0:* LISTEN 5801/haproxy
找出haproxy process, 查看這個haproxy使用的設定檔內容。
ps aux |grep 5801
neutron 5801 0.0 0.0 140304 2652 ? Ss 02:56 0:00 haproxy -f /var/lib/neutron/ovn-metadata-proxy/76af9baf-9fa4-4631-8f1d-5610ef87a036.conf
果然,這個Haproxy 綁定至169.254.169.254:80,而後端的server是一個本機檔案/var/lib/neutron/metadata_proxy
,其實就是一個UNIX socket。
cat /var/lib/neutron/ovn-metadata-proxy/76af9baf-9fa4-4631-8f1d-5610ef87a036.conf
...
listen listener
bind 169.254.169.254:80
server metadata /var/lib/neutron/metadata_proxy
http-request add-header X-OVN-Network-ID 76af9baf-9fa4-4631-8f1d-5610ef87a036
file /var/lib/neutron/metadata_proxy
/var/lib/neutron/metadata_proxy: socket
最後,用lsof發覺neutron-ovn-metadata-agent
這個process在使用這個socket。再進一步檢視其所使用的設定檔 /etc/neutron/neutron_ovn_metadata_agent.ini
,裡面有指定nova_matadata
server的IP. 因而能透過nova metadata 取得VM的metadata.
lsof /var/lib/neutron/metadata_proxy
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
neutron-o 1600 neutron 11u unix 0xffff89bdf24d9b00 0t0 36875 /var/lib/neutron/metadata_proxy type=STREAM
neutron-o 3220 neutron 11u unix 0xffff89bdf24d9b00 0t0 36875 /var/lib/neutron/metadata_proxy type=STREAM
neutron-o 3220 neutron 17u unix 0xffff89bdf24d9b00 0t0 36875 /var/lib/neutron/metadata_proxy type=STREAM
ps 1600
PID TTY STAT TIME COMMAND
1600 ? Ss 0:05 neutron-ovn-metadata-agent (/usr/bin/python3 /usr/bin/neutron-ovn-metadata-agent --config-file /etc/neutron/neutron_ovn_metadata_agent.ini
cat /etc/neutron/neutron_ovn_metadata_agent.ini |grep nova
nova_metadata_host=192.168.121.230