使用基于域名(或路径)的端点代替基于端口的端点¶
默认情况下,OpenStack-Ansible 使用基于端口的端点。这意味着每个服务将在其自己的唯一端口上提供公共和内部端点。例如,Keystone 将被添加为 https://domain.com:5000/v3,Nova 作为 https://domain.com:8774/v2.1,依此类推。
虽然这是最简单的方法,因为它不需要额外的配置并且易于上手,但它也有一些缺点。例如,某些客户端或组织可能不允许连接到自定义端口,这完全禁用了在这些部署中使用它们的能力。
为了解决这些限制,从 2023.1 (Antelope) 版本开始,可以使用基于域名或基于路径的端点。
警告
切换到基于域名或基于路径的端点后,所有内部和公共端点必须覆盖有效的 TLS 证书。否则,OpenStack 组件之间的通信可能会中断。如果您的部署中不使用 TLS 进行内部流量,请确保 HAProxy 已配置为内部请求不会重定向到 HTTPS。这必须由操作员手动调整。
配置基于域名的端点(推荐)¶
基于域名的端点根据 FQDN 分开直接请求到特定服务。通常,为此目的使用子域名。例如,Keystone 端点可能看起来像 https://identity.domain.com,而 Nova 端点可能像 https://compute.domain.com。
作为此类型设置的先决条件,您需要确保为您的域名存在相应的 A 或 CNAME 记录。此外,您需要确保对公共/内部端点拥有有效的通配符或 SAN 证书。
HAProxy 配置¶
为了让 HAProxy 将特定的 FQDN 传递给它自己的后端,我们将利用 map 文件 功能。
我们需要对每个 HAProxy 服务定义进行调整,以
防止为每个服务创建一个前端。由于我们现在期望流量仅在默认 80 和 443 端口上进入,因此不需要为每个服务创建一个单独的前端。一个 HAProxy map 文件附加到一个“基础”前端,该前端与
haproxy_server角色一起部署,并且独立于任何服务定义。可以使用 map 文件通过使用 map 文件中定义的规则来匹配主机请求标头,将传入请求定向到特定的后端。注意
如果对
haproxy_base_service_overrides变量进行任何更改,您需要重新运行openstack-ansible openstack.osa.haproxy --tags haproxy-service-config。haproxy_base_service_overrides: haproxy_maps: - 'use_backend %[req.hdr(host),map_dom(/etc/haproxy/base_domain.map)]' haproxy_map_entries: - name: base_domain entries: - "# Domain map file - this comment is defined in the base frontend config"
填充一个“基础”map 文件,其中包含每个服务后端的搜索模式。由于每个服务将使用自己的 FQDN,我们需要告知 HAProxy 在请求到达 FQDN 时应使用哪个后端。
Keystone 和 Nova 的示例配置如下
注意
对
haproxy_<service>_service_overrides变量进行更改后,您需要使用 haproxy-service-config 标签重新运行特定于服务的 playbook,例如openstack-ansible openstack.osa.keystone --tags haproxy-service-config。haproxy_keystone_service_overrides: haproxy_backend_only: true haproxy_map_entries: - name: base_domain entries: - "identity.{{ external_lb_vip_address }} keystone_service-back" - "identity.{{ internal_lb_vip_address }} keystone_service-back" haproxy_nova_api_compute_service_overrides: haproxy_backend_only: true haproxy_map_entries: - name: base_domain entries: - "compute.{{ external_lb_vip_address }} nova_api_os_compute-back" - "compute.{{ internal_lb_vip_address }} nova_api_os_compute-back" haproxy_nova_novnc_console_service_overrides: haproxy_backend_only: true haproxy_map_entries: - name: base_domain entries: - "novnc.{{ external_lb_vip_address }} nova_novnc_console-back"
由于基础前端将使用域映射,请确保传入外部 VIP 的请求也正确路由到 Horizon 后端。
添加以下覆盖
haproxy_horizon_service_overrides:
haproxy_backend_only: true
haproxy_map_entries:
- name: base_domain
entries:
- "{{ external_lb_vip_address }} horizon-back"
然后重新配置 HAProxy 映射
# openstack-ansible openstack.osa.horizon --tags haproxy-service-config
服务配置¶
除了 HAProxy 配置之外,我们还需要确保端点目录填充了正确的 URI。每个服务都有需要覆盖的一组变量。通常,这些变量具有以下格式
<service>_service_publicuri
<service>_service_internaluri
<service>_service_adminuri
以下是定义 Keystone 和 Nova 端点的示例
keystone_service_publicuri: "{{ openstack_service_publicuri_proto }}://identity.{{ external_lb_vip_address }}"
keystone_service_internaluri: "{{ openstack_service_internaluri_proto }}://identity.{{ internal_lb_vip_address }}"
keystone_service_adminuri: "{{ openstack_service_adminuri_proto }}://identity.{{ internal_lb_vip_address }}"
nova_service_publicuri: "{{ openstack_service_publicuri_proto }}://compute.{{ external_lb_vip_address }}"
nova_service_internaluri: "{{ openstack_service_internaluri_proto }}://compute.{{ internal_lb_vip_address }}"
nova_service_adminuri: "{{ openstack_service_adminuri_proto }}://compute.{{ internal_lb_vip_address }}"
nova_novncproxy_base_uri: "{{ nova_novncproxy_proto }}://novnc.{{ external_lb_vip_address }}"
更改了新域的端点定义后,从实用程序主机开始,在组件之间刷新端点数据
# openstack-ansible openstack.osa.utility_host
# openstack-ansible openstack.osa.setup_openstack --tags <service>-config
在 <service> 下,包括所有已部署的服务,例如 neutron、cinder、glance、nova、horizon 等。
使用 Let’s Encrypt¶
虽然您可以考虑为该域拥有通配符或 SAN TLS 证书以覆盖所有服务端点,但仍然可以使用 Let’s Encrypt 证书进行 dns-01 身份验证,或通过提供将覆盖的子域名列表。
因此,您的 Let’s Encrypt 配置可能如下所示
haproxy_ssl_letsencrypt_enable: true
haproxy_ssl_letsencrypt_email: "root@{{ external_lb_vip_address }}"
haproxy_ssl_letsencrypt_domains:
- "{{ external_lb_vip_address }}"
- "identity.{{ external_lb_vip_address }}"
- "compute.{{ external_lb_vip_address }}"
注意
请注意,内部 FQDN 仍然将使用自签名证书覆盖,因为在大多数用例中,Let’s Encrypt 除非使用 dns-01 身份验证,否则不应能够验证内部 VIP 的域所有权。
您可能还需要通过 PKI 角色来扩展已颁发 SAN 证书的 CN 名称。为此,您需要像下面的示例一样覆盖 haproxy_vip_binds 变量
haproxy_vip_binds:
- address: "{{ haproxy_bind_external_lb_vip_address }}"
interface: "{{ haproxy_bind_external_lb_vip_interface }}"
type: external
- address: "{{ haproxy_bind_internal_lb_vip_address }}"
interface: "{{ haproxy_bind_internal_lb_vip_interface }}"
type: internal
pki_san_records:
- "{{ internal_lb_vip_address }}"
- "identity.{{ internal_lb_vip_address }}"
- "compute.{{ internal_lb_vip_address }}"
您可能还需要调整由 haproxy_security_headers_csp 变量定义的 HSTS 标头。虽然默认规则默认允许子域,但您可能希望稍微限制记录。
注意
变量 haproxy_security_child_src_records 和 haproxy_security_connect_src_records 仅从 2024.2 (Dalmatian) 版本开始可用。对于早期版本,您需要覆盖整个 haproxy_security_headers_csp。
haproxy_security_child_src_records:
- "novnc.{{ external_lb_vip_address }}"
haproxy_security_connect_src_records:
- "{{ external_lb_vip_address }}
haproxy_security_frame_ancestors_records:
- "{{ external_lb_vip_address }}
配置基于路径的端点¶
基于路径的端点意味着在相同的 FQDN 上提供服务,但根据 URI 对它们进行区分。
例如,Keystone 可以配置为 https://domain.com/identity/v3,而 Nova 可以配置为 https://domain.com/compute/v2.1
警告
请注意,Horizon 利用 /identity 用于其 Keystone 面板,因此,如果您在 /(默认值)上提供 Horizon 并使用 /identity 将流量转发到 Keystone 后端,由于冲突,Horizon 内部的用户、角色、项目管理将中断。
虽然基于路径的端点可能看起来很有吸引力,因为它们使用 FQDN,因此不需要通配符 TLS,但它们更难维护且设置更复杂。还值得注意的是,尽管这种方法在 devstack 中使用,但并非所有服务都准备好支持基于路径的端点。
目前不支持基于路径的端点的异常示例包括 VM 的 VNC 控制台(将在 蓝图 中实现)、Magnum(错误报告 <https://launchpad.net/bugs/2083168>)和 Ceph Rados Gateway。
HAProxy 配置¶
与基于域名的端点类似,我们依赖 HAProxy map 功能。但是,我们使用 map_reg 代替 map_dom。
因此,我们需要定义要使用的 map 文件以及解析它的方法。为此,我们需要对“基础”服务应用覆盖。
haproxy_base_service_overrides:
haproxy_maps:
- 'use_backend %[path,map_reg(/etc/haproxy/base_regex.map)]'
如果您需要拥有 Ceph RGW 或想要组合基于域名和基于路径的方法,可以通过定义两个 map 文件来做到这一点
注意
如果对 haproxy_base_service_overrides 变量进行任何更改,您需要重新运行 openstack-ansible openstack.osa.haproxy --tags haproxy-service-config。
haproxy_base_service_overrides:
haproxy_maps:
- 'use_backend %[req.hdr(host),map_dom(/etc/haproxy/base_domain.map)] if { req.hdr(host),map_dom(/etc/haproxy/base_domain.map) -m found }'
- 'use_backend %[path,map_reg(/etc/haproxy/base_regex.map)]'
如果未匹配任何域,HAProxy 将继续使用基于路径的端点。
接下来,我们需要确保每个 HAProxy 服务配置都包含带有相应条件的 HAProxy map 填充,例如
注意
对 haproxy_<service>_service_overrides 变量进行更改后,您需要使用 haproxy-service-config 标签重新运行特定于服务的 playbook,例如 openstack-ansible openstack.osa.keystone --tags haproxy-service-config。
haproxy_keystone_service_overrides:
haproxy_backend_only: true
haproxy_map_entries:
- name: base_regex
entries:
- "^/identity keystone_service-back"
haproxy_nova_api_compute_service_overrides:
haproxy_backend_only: true
haproxy_map_entries:
- name: base_regex
entries:
- "^/compute nova_api_os_compute-back"
服务配置¶
与基于域名的端点类似,我们需要覆盖每个服务的端点定义。通常使用以下变量定义端点
<service>_service_publicuri
<service>_service_internaluri
<service>_service_adminuri
以下是定义 Keystone 和 Nova 端点的示例
keystone_service_publicuri: "{{ openstack_service_publicuri_proto }}://{{ external_lb_vip_address }}/identity"
keystone_service_internaluri: "{{ openstack_service_internaluri_proto }}://{{ internal_lb_vip_address }}/identity"
keystone_service_adminuri: "{{ openstack_service_adminuri_proto }}://{{ internal_lb_vip_address }}/identity"
nova_service_publicuri: "{{ openstack_service_publicuri_proto }}://{{ external_lb_vip_address }}/compute"
nova_service_internaluri: "{{ openstack_service_internaluri_proto }}://{{ internal_lb_vip_address }}/compute"
nova_service_adminuri: "{{ openstack_service_adminuri_proto }}://{{ internal_lb_vip_address }}/compute"
但是,配置中还有另一个重要的部分,这与基于域名的设置不同。所有服务都假定它们在根路径(即 /)上提供服务,而在基于路径的方法中,我们为每个服务使用唯一的路径。
因此,现在我们需要使服务尊重该路径并正确响应它。一种方法是在 uWSGI 中使用重写机制,例如
警告
以下示例不代表如何为大多数服务配置基于路径的端点的正确方法
keystone_uwsgi_ini_overrides:
uwsgi:
route: '^/identity(.*)$ rewrite:$1'
但是,这种方法不正确,并且会导致某些客户端或用例出现问题,尽管该服务看起来完全正常。上述方法的问题与服务在被询问时如何返回“self”URL 相关。大多数服务将使用其当前微版本和该微版本的 URI 作为回复。
如果您使用像上面显示的那样的 uWSGI 重写,您将得到如下回复
curl https://cloud.com/identity/ | jq
{
"versions": {
"values": [
{
"id": "v3.14",
"status": "stable",
"updated": "2020-04-07T00:00:00Z",
"links": [
{
"rel": "self",
"href": "https://cloud.com/v3/"
}
],
"media-types": [
{
"base": "application/json",
"type": "application/vnd.openstack.identity-v3+json"
}
]
}
]
}
}
如您所见,href 指向的位置不正确。虽然某些客户端可能不参考服务提供的 href 链接,但其他客户端可能会将其用作事实来源,这将导致失败。
一些服务,例如 keystone,具有可以控制如何定义 href 的配置选项。例如,keystone 具有 [DEFAULT]/public_endpoint 选项,但这种方法在所有服务中并不一致。此外,keystone 将为所有端点(包括 admin 和 internal)返回提供的 public_endpoint。
因此,唯一的正确方法是调整每个服务的 api-paste.ini。但是,Keystone 特别地,不支持 api-paste.ini 文件。因此,唯一的解决方法实际上是 uWSGI 重写并在 keystone.conf 中定义 public_endpoint
keystone_keystone_conf_overrides:
DEFAULT:
public_endpoint: "{{ keystone_service_publicuri }}"
对于其他服务,应用 api-paste.ini 可以通过变量来完成,但每个服务的具体内容都非常独特,因此无法轻松概括该方法。以下是为某些服务进行的覆盖示例
_glance_api_paste_struct:
/: {}
/healthcheck: {}
/image: api
/image/healthcheck: healthcheck
glance_glance_api_paste_ini_overrides:
composite:glance-api: "{{ _glance_api_paste_struct }}"
composite:glance-api-caching: "{{ _glance_api_paste_struct }}"
composite:glance-api-cachemanagement: "{{ _glance_api_paste_struct }}"
composite:glance-api-keystone: "{{ _glance_api_paste_struct }}"
composite:glance-api-keystone+caching: "{{ _glance_api_paste_struct }}"
composite:glance-api-keystone+cachemanagement: "{{ _glance_api_paste_struct }}"
neutron_api_paste_ini_overrides:
composite:neutron:
/: {}
/v2.0: {}
/network/: neutronversions_composite
/network/v2.0: neutronapi_v2_0
nova_api_paste_ini_overrides:
composite:osapi_compute:
/: {}
/v2: {}
/v2.1: {}
/v2/+: {}
/v2.1/+: {}
/compute: oscomputeversions
/compute/v2: oscomputeversion_legacy_v2
/compute/v2.1: oscomputeversion_v2
/compute/v2/+: openstack_compute_api_v21_legacy_v2_compatible
/compute/v2.1/+: openstack_compute_api_v21
我们建议参考每个服务的 api-paste.ini 以获取有关如何正确配置覆盖的更多详细信息。