Openvswitch Agent 的分布式元数据数据通路¶
RFE: https://bugs.launchpad.net/neutron/+bug/1933222
当实例启动时,它们将尝试通过 Neutron 虚拟交换机(桥接)、虚拟设备、命名空间和元数据代理从 Nova 获取元数据。此后,元数据代理将没有其他功能。在大型场景中,大量部署的元数据代理将导致主机和消息队列浪费资源。因为元数据代理会根据用户的资源数量启动大量外部进程,并向 Neutron 服务器报告状态以保持活动连接。
我们将克服这个问题。本规范描述了如何为 Neutron openvswitch 代理实现一个代理扩展,以使元数据数据通路分布式。
问题描述¶
对于大规模云部署,应该运行多少个元数据代理? 这个问题没有确切的答案。 云运维人员可以将元数据代理设置到所有主机,类似于 DHCP 代理。
Config drive [1] 可以作为云提供虚拟机元数据的替代方案。 但 config drive 存在一些限制和安全问题。 例如,如果您将 config drive 设置为使用本地磁盘,则实时迁移可能不可用。 Config drive 使用额外的存储设备并在虚拟机内部挂载它。 无法为用户的特定脚本在线更改 userdata。 安全问题在于,由于挂载的文件系统可以被所有用户访问,如果元数据包含 root 密码或密钥,则密码和密钥可以被非 root 用户访问。
如果您正在运行 Neutron 代理,则没有替代方案可以取代云部署的元数据代理。
正如我们所见,元数据数据通路通过许多设备、命名空间和代理非常漫长。 一条元数据路径中断,例如代理宕机或外部进程死亡,不仅会影响主机,还会影响所有将启动新虚拟机的相关主机。
提议的变更¶
为 Neutron openvswitch 代理添加一个代理扩展,以使元数据数据通路分布式。
注意
此扩展仅适用于 openvswitch 代理,其他机制驱动程序将不被考虑,因为此新扩展将依赖于 openflow 协议和原理。
提出的解决方案¶
元数据专用 IP 和 MAC¶
主机中的每个 VM 将生成一个 Meta IP + MAC 对,该对在主机上的 VM 中是唯一的,并将用于标识元数据请求者 vm/port。 代理扩展将在第一次处理端口时完成生成工作,并将 Meta IP + MAC 存储到 ovsdb 中。 当 ovs-agent 重新启动时,handle_port 函数将首先尝试读取 ovsdb 以重新加载 Meta IP + MAC,如果不存在,则重新生成。
在代理扩展初始化期间,它将在 ovs-bridge br-meta 上添加一个名为“tap-meta”的内部端口。 此 tap 设备将具有 Meta Gateway MAC + IP。
潜在的配置¶
带有此扩展的 Neutron openvswitch 代理的配置选项
[METADATA]
provider_cidr = 100.100.0.0/16
provider_vlan_id = 998
provider_base_mac = "fa:16:ee:00:00:00"
provider_cidr将用作生成 VM 元数据 IP 的 IP 范围。provider_vlan_id将是来自本地 tap-meta 设备的固定 vlan。provider_base_mac将是生成 VM META MAC 的 MAC 前缀。
我们可以重用元数据代理的现有配置选项
METADATA_PROXY_HANDLER_OPTS = [
cfg.StrOpt('auth_ca_cert',
help=_("Certificate Authority public key (CA cert) "
"file for ssl")),
cfg.HostAddressOpt('nova_metadata_host',
default='127.0.0.1',
help=_("IP address or DNS name of Nova metadata "
"server.")),
cfg.PortOpt('nova_metadata_port',
default=8775,
help=_("TCP Port used by Nova metadata server.")),
cfg.StrOpt('metadata_proxy_shared_secret',
default='',
help=_('When proxying metadata requests, Neutron signs the '
'Instance-ID header with a shared secret to prevent '
'spoofing. You may select any string for a secret, '
'but it must match here and in the configuration used '
'by the Nova Metadata Server. NOTE: Nova uses the same '
'config key, but in [neutron] section.'),
secret=True),
cfg.StrOpt('nova_metadata_protocol',
default='http',
choices=['http', 'https'],
help=_("Protocol to access nova metadata, http or https")),
cfg.BoolOpt('nova_metadata_insecure', default=False,
help=_("Allow to perform insecure SSL (https) requests to "
"nova metadata")),
cfg.StrOpt('nova_client_cert',
default='',
help=_("Client certificate for nova metadata api server.")),
cfg.StrOpt('nova_client_priv_key',
default='',
help=_("Private key of client certificate."))
]
使用 http 作为 haproxy 的基本后端配置将是
backend backend_{{ instance_x.uuid }}_{{ metadata_ip_x }}
server metasrv http://nova_metadata_host:nova_metadata_port
如果在 haproxy 侧启用了 ssl 验证,则后端将是
backend backend_{{ instance_x.uuid }}_{{ metadata_ip_x }}
server metasrv https://nova_metadata_host:nova_metadata_port ssl verify required ca-file /path/to/auth_ca_cert
或者使用客户端证书的 ssl
backend backend_{{ instance_x.uuid }}_{{ metadata_ip_x }}
server metasrv https://nova_metadata_host:nova_metadata_port ssl verify required ca-file /path/to/nova_client_cert crt /path/to/nova_client_priv_key
注意
如果云具有自己的客户端证书,则 crt 参数可以指向您的客户端证书文件。 但这仅在 haproxy 版本 >= 2.4 时才受支持。
元数据数据管道¶
示例资源数据¶
VM1 - network1 local_vlan_id=1, fixed_ip 192.168.1.10, port mac fa:16:3e:4a:fd:c1, Meta_IP 100.100.0.10, Meta_MAC fa:16:ee:00:00:11
VM2 - network2 local_vlan_id=2, fixed_ip 192.168.2.10, port mac fa:16:3e:4a:fd:c2, Meta_IP 100.100.0.11, Meta_MAC fa:16:ee:00:00:22
VM3 - network1 local_vlan_id=1, fixed_ip 192.168.1.20, port mac fa:16:3e:4a:fd:c3, Meta_IP 100.100.0.12, Meta_MAC fa:16:ee:00:00:33
VM4 - network3 local_vlan_id=3, fixed_ip 192.168.3.10, port mac fa:16:3e:4a:fd:c4, Meta_IP 100.100.0.13, Meta_MAC fa:16:ee:00:00:44
META Gateway IP 100.100.0.1, META Gateway MAC: fa:16:ee:00:00:01
TCP 出站¶
来自 VM 的 HTTP 请求直接发送到 br-meta,并将 IP 标头更改为 tap-meta,在主机 haproxy 中添加 HTTP 标头,然后发送到 nova-metadata API。 数据通路
+----+ TCP +-----------------------------------+ TCP +---------------------------------------------+ TCP +------------------------+ TCP +-----------------------+
| +-----> Br-int +-----> Br-meta +-----> tap-Meta +-----> Haproxy |
| VM | | From VM port + 169.254.169.254:80 | | Source (VM MAC + IP --> Meta MAC + IP) | | Meta Gateway MAC + IP | | Match Meta IP |
| | | add local vlan | | Dest (MAC + IP --> Meta Gateway MAC + IP) | | Listened by | | Add Http header |
| | | to Br-meta | | to tap-Meta | | Haproxy | | to Nova-Metadata-API |
+----+ +-----------------------------------+ +---------------------------------------------+ +------------------------+ +-----------------------+
br-int 上的流程(一些关键字是伪代码)
Table=0
Match: ip,in_port=<of_vm1>,nw_dst=169.254.169.254 actions=mod_local_vlan:1,output:"To_br_meta"
Match: ip,in_port=<of_vm2>,nw_dst=169.254.169.254 actions=mod_local_vlan:2,output:"To_br_meta"
Match: ip,in_port=<of_vm3>,nw_dst=169.254.169.254 actions=mod_local_vlan:1,output:"To_br_meta"
Match: ip,in_port=<of_vm4>,nw_dst=169.254.169.254 actions=mod_local_vlan:3,output:"To_br_meta"
当您的 VM 尝试访问 169.254.169.254:80 时,目标 MAC + IP 应该是什么? 目标 IP 很明确,是 169.254.169.254。 复杂的情况是目标 MAC。 我们有三种场景
a. 如果您的 VM 只有一个指向网关的默认路由,那么请求的目标 MAC 应该是网关 MAC。
b. 如果您的 VM 具有直接指向 169.254.169.254 的路由(例如,通过 192.168.1.2 <DHCP 端口 IP>,通常由原始 DHCP 代理和元数据机制设置),那么一些 ARP 响应器将为这些 DHCP 端口 IP 添加,以防升级。 对于这些 DHCP 端口 IP,将响应一个假的 mac。
c. 如果您的 VM 具有链路路由,该路由告诉来宾操作系统 169.254.169.254 可直接访问。 因此,将添加一个用于 169.254.169.254 的 ARP 响应器。 因此,目标 MAC 也是假的。
br-meta 上的流程
Table=0
Match: ip,in_port=<patch_br_int>,nw_dst=169.254.169.254 Action: resubmit(,80)
Table=80
Match: dl_vlan=<local_vlan_1>,dl_src=fa:16:3e:4a:fd:c1,nw_src=192.168.1.10 Action: strip_vlan,mod_dl_src:fa:16:ee:00:00:11,mod_nw_src:100.100.0.10, resubmit(,87)
Match: dl_vlan=<local_vlan_2>,dl_src=fa:16:3e:4a:fd:c2,nw_src=192.168.2.10 Action: strip_vlan,mod_dl_src:fa:16:ee:00:00:22,mod_nw_src:100.100.0.11, resubmit(,87)
Match: dl_vlan=<local_vlan_1>,dl_src=fa:16:3e:4a:fd:c3,nw_src=192.168.1.20 Action: strip_vlan,mod_dl_src:fa:16:ee:00:00:33,mod_nw_src:100.100.0.12, resubmit(,87)
Match: dl_vlan=<local_vlan_3>,dl_src=fa:16:3e:4a:fd:c4,nw_src=192.168.3.10 Action: strip_vlan,mod_dl_src:fa:16:ee:00:00:44,mod_nw_src:100.100.0.13, resubmit(,87)
Table=87
Match: tcp,nw_dst=169.254.169.254,tp_dst=80 Action: mod_nw_dst:100.100.0.1, mod_dl_dst:fa:16:ee:00:00:01,output:"tap-meta"
TCP 入站¶
HTTP 数据包从 tap-meta 直接到达 br-meta,然后到达 br-int,最终直接到达 VM。 数据通路
+----+ +---------------------+ +---------------------------------------+ +---------------+ +------------------+
| | | Br-int | | Br-meta | | tap-Meta | | Haproxy |
| VM | | From patch_br_meta | | Source (IP ---> 169.254.169.254) | | | | Http Response |
| | TCP | mac_dst is VM | TCP | Dest(Meta MAC + IP ---> VM MAC + IP) | TCP | To | TCP | To Client IP |
| <-----+ output to VM <-----+ to Br-int <-----+ Meta MAC + IP <-----+ (Meta MAC + IP) |
+----+ +---------------------+ +---------------------------------------+ +---------------+ +------------------+
br-meta 上的流程
Table=0
Match: ip,in_port="tap-meta" actions=push_vlan,goto_table:91
Table=91
Match: dl_vlan=<998>,ip,nw_dst=100.100.0.10 Action: mod_vlan_vid:1,mod_dl_dst:fa:16:3e:4a:fd:c1,mod_nw_dst:192.168.1.10,mod_nw_src:169.254.169.254,output:"to-br-int"
Match: dl_vlan=<998>,ip,nw_dst=100.100.0.11 Action: mod_vlan_vid:2,mod_dl_dst:fa:16:3e:4a:fd:c2,mod_nw_dst:192.168.2.10,mod_nw_src:169.254.169.254,output:"to-br-int"
Match: dl_vlan=<998>,ip,nw_dst=100.100.0.12 Action: mod_vlan_vid:3,mod_dl_dst:fa:16:3e:4a:fd:c3,mod_nw_dst:192.168.1.20,mod_nw_src:169.254.169.254,output:"to-br-int"
Match: dl_vlan=<998>,ip,nw_dst=100.100.0.13 Action: mod_vlan_vid:4,mod_dl_dst:fa:16:3e:4a:fd:c4,mod_nw_dst:192.168.3.10,mod_nw_src:169.254.169.254,output:"to-br-int"
br-int 上的流程
Table=0
Match: ip,in_port=<patch_br_meta>,dl_vlan=1,dl_dst=<vm1_mac_fa:16:3e:4a:fd:c1>,nw_src=169.254.169.254 actions=strip_vlan,output:<of_vm1>
Match: ip,in_port=<patch_br_meta>,dl_vlan=2,dl_dst=<vm2_mac_fa:16:3e:4a:fd:c2>,nw_src=169.254.169.254 actions=strip_vlan,output:<of_vm2>
Match: ip,in_port=<patch_br_meta>,dl_vlan=3,dl_dst=<vm3_mac_fa:16:3e:4a:fd:c3>,nw_src=169.254.169.254 actions=strip_vlan,output:<of_vm3>
Match: ip,in_port=<patch_br_meta>,dl_vlan=4,dl_dst=<vm4_mac_fa:16:3e:4a:fd:c4>,nw_src=169.254.169.254 actions=strip_vlan,output:<of_vm4>
元数据 IP 的 ARP¶
Tap-meta 设备将驻留在主机内核 IP 堆栈上,在第一次响应 TCP 之前,主机(协议堆栈)需要知道 META_IP 的 MAC 地址。 因此,ARP 请求是广播的。 ARP 将从 tap-meta 设备发送到 br-meta 响应器。 ARP 响应器数据通路
+---------------------------+ +---------------+
| Br-meta | | tap-Meta |
| ARP Responder for Meta IP | | Learn |
| to | ARP | Meta IP's MAC |
| INPORT <------+ (ARP) |
+---------------------------+ +---------------+
br-meta 上的流程将是
Ingress:
Table=0
Match: arp,in_port="tap-meta" Action: resubmit(,90)
Table=90
Match: arp,arp_tpa=100.100.0.10 Action: ARP Responder with Meta_MAC fa:16:ee:00:00:11,IN_PORT
Match: arp,arp_tpa=100.100.0.11 Action: ARP Responder with Meta_MAC fa:16:ee:00:00:22,IN_PORT
Match: arp,arp_tpa=100.100.0.12 Action: ARP Responder with Meta_MAC fa:16:ee:00:00:33,IN_PORT
Match: arp,arp_tpa=100.100.0.13 Action: ARP Responder with Meta_MAC fa:16:ee:00:00:44,IN_PORT
主机 haproxy 配置¶
主机 haproxy 是用于所有 VM 的唯一进程。 主机 haproxy 将向元数据请求添加 HTTP 标头,这些标头是元数据 API 所需要的。 标头具有易于组装的固定算法。 对于每个 VM 的请求,haproxy 将添加一个独立的后端和一个检查源 IP(也称为 Meta_IP)的匹配规则。 当来自一个 VM 的请求(Meta_IP)时,它将被发送到匹配的后端,该后端添加 HTTP 标头,然后发送到真实的 nova-metadata-api。
配置
global
log /dev/log local0 {{ log_level }}
user {{ user }}
group {{ group }}
maxconn {{ maxconn }}
daemon
frontend public
bind *:80 name clear
mode http
log global
option httplog
option dontlognull
maxconn {{ maxconn }}
timeout client 30s
acl instance_{{ instance_1.uuid }}_{{ metadata_ip_1 }} src {{ metadata_ip_1 }}
acl instance_{{ instance_2.uuid }}_{{ metadata_ip_2 }} src {{ metadata_ip_2 }}
acl instance_{{ instance_3.uuid }}_{{ metadata_ip_3 }} src {{ metadata_ip_3 }}
acl instance_{{ instance_4.uuid }}_{{ metadata_ip_4 }} src {{ metadata_ip_4 }}
use_backend backend_{{ instance_1.uuid }}_{{ metadata_ip_1 }} if instance_{{ instance_1.uuid }}_{{ metadata_ip_1 }}
use_backend backend_{{ instance_2.uuid }}_{{ metadata_ip_2 }} if instance_{{ instance_2.uuid }}_{{ metadata_ip_2 }}
use_backend backend_{{ instance_3.uuid }}_{{ metadata_ip_3 }} if instance_{{ instance_3.uuid }}_{{ metadata_ip_3 }}
use_backend backend_{{ instance_4.uuid }}_{{ metadata_ip_4 }} if instance_{{ instance_4.uuid }}_{{ metadata_ip_4 }}
backend backend_{{ instance_1.uuid }}_{{ metadata_ip_1 }}
balance roundrobin
retries 3
option redispatch
timeout http-request 30s
timeout connect 30s
timeout server 30s
http-request set-header X-Instance-ID {{ instance_1.uuid }}
http-request set-header X-Tenant-ID {{ instance_1.project_id }}
http-request set-header X-Instance-ID-Signature {{ instance_1.signature }}
server metasrv ...
backend backend_{{ instance_2.uuid }}_{{ metadata_ip_2 }}
balance roundrobin
retries 3
option redispatch
timeout http-request 30s
timeout connect 30s
timeout server 30s
http-request set-header X-Instance-ID {{ instance_2.uuid }}
http-request set-header X-Tenant-ID {{ instance_2.project_id }}
http-request set-header X-Instance-ID-Signature {{ instance_2.signature }}
server metasrv ...
backend backend_{{ instance_3.uuid }}_{{ metadata_ip_3 }}
...
backend backend_{{ instance_4.uuid }}_{{ metadata_ip_4 }}
...
IPv6 元数据¶
仅具有类似地址 fe80::a9fe:a9fe 的 IPv6 元数据 [2],因此所有这些工作都可以镜像到 IPv6。 对于 IPv6,生成器将使用范围 fe80:ffff:a9fe:a9fe::/64 来分配 Meta_IPv6 地址。 Meta_gateway_IPv6 地址将是 fe80:ffff:a9fe:a9fe::1,Gateway MAC 仍然是相同的 fa:16:ee:00:00:01。 用于 Meta_IPv6 地址 fe80:ffff:a9fe:a9fe::abcd 和 Meta_MAC fa:16:ee:00:00:11 的 NA 响应器将是
Table=0
Match: icmp6,icmp_type=135,icmp_code=0,nd_sll=fa:16:ee:00:00:01,
actions=set_field:136->icmpv6_type,set_field:0->icmpv6_code,set_field:2->icmpv6_options_type,goto_table:90
Table=90
Match: icmp6,icmp_type=136,icmp_code=0,nd_target=fe80:ffff:a9fe:a9fe::abcd
actions=move:NXM_OF_ETH_SRC[]->NXM_OF_ETH_DST[],set_field:fa:16:ee:00:00:11->eth_src,
move:NXM_NX_IPV6_SRC[]->NXM_NX_IPV6_DST[],set_field:fe80:ffff:a9fe:a9fe::abcd->ipv6_src,
set_field:fa:16:ee:00:00:11->nd-tll,set_field:0xE000->OFPXMT_OFB_ICMPV6_ND_RESERVED,IN_PORT
实现¶
负责人¶
LIU Yulong <i@liuyulong.me>
工作项¶
为 ovs 桥接添加流程操作
为 ovs-agent 添加主机 haproxy 管理器
添加具有 ovsdb 设置的主机元数据 IP 和 Mac 生成器
添加 ovs-agent 扩展以设置 VM 端口的流程
测试。
文档。
依赖项¶
无
测试¶
测试用例以验证元数据是否可以正确设置,这可以使用带有新元数据驱动程序的新作业来完成。