Neutron LBaaS负载均衡器名称中文问题解决方案




环境

M版本neutron+neutron_lbaas+haproxy方案,没有使用octavia。

neutron.conf配置如下:

[DEFAULT]
service_plugins = neutron_lbaas.services.loadbalancer.plugin.LoadBalancerPluginv2
service_provider = LOADBALANCERV2:Haproxy:neutron_lbaas.drivers.haproxy.plugin_driver.HaproxyOnHostPluginDriver:default

service_lbaas.conf无特殊配置,基本都是默认值。

问题描述

创建中文名称的负载均衡器,之后创建监听器,lbaas-agent.log报错:

2017-12-21 03:35:31.661 3652 ERROR neutron_lbaas.agent.agent_manager [-] Unable to deploy instance for loadbalancer: 34af08dc-5dd6-4bdd-a8bc-3546ebfebe04
2017-12-21 03:35:31.661 3652 ERROR neutron_lbaas.agent.agent_manager Traceback (most recent call last):
2017-12-21 03:35:31.661 3652 ERROR neutron_lbaas.agent.agent_manager   File "/usr/lib/python2.7/site-packages/neutron_lbaas/agent/agent_manager.py", line 185, in _reload_loadbalancer
2017-12-21 03:35:31.661 3652 ERROR neutron_lbaas.agent.agent_manager     self.device_drivers[driver_name].deploy_instance(loadbalancer)
2017-12-21 03:35:31.661 3652 ERROR neutron_lbaas.agent.agent_manager   File "/usr/lib/python2.7/site-packages/oslo_concurrency/lockutils.py", line 271, in inner
2017-12-21 03:35:31.661 3652 ERROR neutron_lbaas.agent.agent_manager     return f(*args, **kwargs)
2017-12-21 03:35:31.661 3652 ERROR neutron_lbaas.agent.agent_manager   File "/usr/lib/python2.7/site-packages/neutron_lbaas/drivers/haproxy/namespace_driver.py", line 175, in deploy_instance
2017-12-21 03:35:31.661 3652 ERROR neutron_lbaas.agent.agent_manager     self.create(loadbalancer)
2017-12-21 03:35:31.661 3652 ERROR neutron_lbaas.agent.agent_manager   File "/usr/lib/python2.7/site-packages/neutron_lbaas/drivers/haproxy/namespace_driver.py", line 203, in create
2017-12-21 03:35:31.661 3652 ERROR neutron_lbaas.agent.agent_manager     self._spawn(loadbalancer)
2017-12-21 03:35:31.661 3652 ERROR neutron_lbaas.agent.agent_manager   File "/usr/lib/python2.7/site-packages/neutron_lbaas/drivers/haproxy/namespace_driver.py", line 361, in _spawn
2017-12-21 03:35:31.661 3652 ERROR neutron_lbaas.agent.agent_manager     haproxy_base_dir)
2017-12-21 03:35:31.661 3652 ERROR neutron_lbaas.agent.agent_manager   File "/usr/lib/python2.7/site-packages/neutron_lbaas/services/loadbalancer/drivers/haproxy/jinja_cfg.py", line 91, in save_config
2017-12-21 03:35:31.661 3652 ERROR neutron_lbaas.agent.agent_manager     n_utils.replace_file(conf_path, config_str)
2017-12-21 03:35:31.661 3652 ERROR neutron_lbaas.agent.agent_manager   File "/usr/lib/python2.7/site-packages/neutron/common/utils.py", line 535, in replace_file
2017-12-21 03:35:31.661 3652 ERROR neutron_lbaas.agent.agent_manager     tmp_file.write(data)
2017-12-21 03:35:31.661 3652 ERROR neutron_lbaas.agent.agent_manager   File "/usr/lib64/python2.7/socket.py", line 316, in write
2017-12-21 03:35:31.661 3652 ERROR neutron_lbaas.agent.agent_manager     data = str(data) # XXX Should really reject non-string non-buffers
2017-12-21 03:35:31.661 3652 ERROR neutron_lbaas.agent.agent_manager UnicodeEncodeError: 'ascii' codec can't encode characters in position 20-21: ordinal not in range(128)

问题原因

写入haproxy配置文件的时候,配置里面包含中文的负载均衡名称,导致写入失败。

写入配置文件过程是先使用tempfile库生成临时配置,之后拷贝到负载均衡UUID为名称的haproxy配置目录下,但是写入临时配置文件的时候发现包含无法处理成Unicode的字符,导致写入失败,用的是tempfile.NamedTemporaryFile这个实例写入临时文件,与open打开文件写入不同,这个实例返回的是一个pipe实例,而不是文件句柄实例,所以下面调用到了python自带socket库,socket通过pipe写入字符串时无法处理配置字符串中类似’\u4e2d\u6587’的中文编码,就报了UnicodeEncodeError错误。

解决方案

修改haproxy配置模板。官方默认的模板是包含3个文件,默认路径是/usr/lib/python2.7/site-packages/neutron_lbaas/services/loadbalancer/drivers/haproxy/templates/,文件名分别为haproxy_base.j2、haproxy.loadbalancer.j2、haproxy_proxies.j2,模板内容示例:

{# # Copyright 2014 OpenStack Foundation
#
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.
#
#}
{% extends 'haproxy_proxies.j2' %}
{% set loadbalancer_name = loadbalancer.name %}  ### 定义变量loadbalancer_name对应的数据来源
{% set usergroup = user_group %}
{% set sock_path = stats_sock %}
{# # Copyright 2014 OpenStack Foundation
#
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.
#
#}
# Configuration for {{ loadbalancer_name }}   ### 就是这行注释模板中用到的负载均衡中文名称出错了
global

有两种方法解决该问题:

  1. 修改变量定义{% set loadbalancer_name = loadbalancer.name %},改成{% set loadbalancer_name = loadbalancer.vip_address %},用其他非中文变量如vip_address代替可能为中文的name即可
  2. 直接删除# Configuration for {{ loadbalancer_name }}这行注释,干净省事儿,注释要不要都无所谓,反正目录名称是负载均衡UUID,可以区分出来

最后,如果手工修改了haproxy配置文件模板,可以通过修改模板文件路径的配置项来加载自己的模板,当然也可以不修改配置而是修改源码中的配置模板后打包发布,我个人倾向于增加配置而不是二次打包,否则后续包的维护成本比较高。修改的配置文件是:

[haproxy]
# 加上这行,并且把自定义的jinjia2模板文件放到对应目录下
jinja_config_template = /etc/neutron/templates/haproxy.loadbalancer.j2

注意配置文件是/etc/neutron/lbaas_agent.ini,不是services_lbaas.conf,前者是neutron-lbaas-agent服务用到的,后者是neutron-server用到的,根据模板生成haproxy配置文件是在agent里面做的,所以修改后者是没效果的。