haproxy ns driver: set global haproxy maxconn

Haproxy uses two separate maxconn values: the maxconn setting in the
global context is used to adjust file descriptor limits and allocate
resources, while the maxconn setting in the frontend context controls if
additional connections are accepted on that frontend.

This patch adds a maxconn to the global context equal to the sum of the
connection limit on each listener. For listeners which no connection
limit specified, the haproxy default global connection limit (2000
connections) is used. This way we provide the same behavior as a default
haproxy configuraiton which no connection limit specified in the case of
a single load balancer.

Change-Id: Icc63771ed15a7f718dab0389c1fb63d628896e0a
Partial-Bug: 1544861
This commit is contained in:
Dustin Lundquist 2016-07-13 13:54:44 -07:00
parent 195d3a6f8f
commit a792d8906c
5 changed files with 28 additions and 2 deletions

View File

@ -248,14 +248,25 @@ def _transform_loadbalancer(loadbalancer, haproxy_base_dir):
listeners = [_transform_listener(x, haproxy_base_dir) listeners = [_transform_listener(x, haproxy_base_dir)
for x in loadbalancer.listeners if x.admin_state_up] for x in loadbalancer.listeners if x.admin_state_up]
pools = [_transform_pool(x) for x in loadbalancer.pools] pools = [_transform_pool(x) for x in loadbalancer.pools]
connection_limit = _compute_global_connection_limit(listeners)
return { return {
'name': loadbalancer.name, 'name': loadbalancer.name,
'vip_address': loadbalancer.vip_address, 'vip_address': loadbalancer.vip_address,
'connection_limit': connection_limit,
'listeners': listeners, 'listeners': listeners,
'pools': pools 'pools': pools
} }
def _compute_global_connection_limit(listeners):
# NOTE(dlundquist): HAProxy has a global default connection limit
# of 2000, so we will include 2000 connections for each listener
# without a connection limit specified. This way we provide the
# same behavior as a default haproxy configuration without
# connection limit specified in the case of a single load balancer.
return sum([x.get('connection_limit', 2000) for x in listeners])
def _transform_listener(listener, haproxy_base_dir): def _transform_listener(listener, haproxy_base_dir):
"""Transforms listener object """Transforms listener object

View File

@ -17,6 +17,7 @@
{% set loadbalancer_name = loadbalancer.name %} {% set loadbalancer_name = loadbalancer.name %}
{% set usergroup = user_group %} {% set usergroup = user_group %}
{% set sock_path = stats_sock %} {% set sock_path = stats_sock %}
{% set connection_limit = loadbalancer.connection_limit %}
{% block proxies %} {% block proxies %}
{% from 'haproxy_proxies.j2' import frontend_macro as frontend_macro, backend_macro%} {% from 'haproxy_proxies.j2' import frontend_macro as frontend_macro, backend_macro%}

View File

@ -20,6 +20,7 @@ global
group {{ usergroup }} group {{ usergroup }}
log /dev/log local0 log /dev/log local0
log /dev/log local1 notice log /dev/log local1 notice
maxconn {{ connection_limit }}
stats socket {{ sock_path }} mode 0666 level user stats socket {{ sock_path }} mode 0666 level user
defaults defaults

View File

@ -102,18 +102,21 @@ RET_LB = {
'name': 'test-lb', 'name': 'test-lb',
'vip_address': '10.0.0.2', 'vip_address': '10.0.0.2',
'listeners': [RET_LISTENER], 'listeners': [RET_LISTENER],
'connection_limit': RET_LISTENER['connection_limit'],
'pools': [RET_POOL]} 'pools': [RET_POOL]}
RET_LB_TLS = { RET_LB_TLS = {
'name': 'test-lb', 'name': 'test-lb',
'vip_address': '10.0.0.2', 'vip_address': '10.0.0.2',
'listeners': [RET_LISTENER_TLS], 'listeners': [RET_LISTENER_TLS],
'connection_limit': RET_LISTENER_TLS['connection_limit'],
'pools': [RET_POOL]} 'pools': [RET_POOL]}
RET_LB_TLS_SNI = { RET_LB_TLS_SNI = {
'name': 'test-lb', 'name': 'test-lb',
'vip_address': '10.0.0.2', 'vip_address': '10.0.0.2',
'listeners': [RET_LISTENER_TLS_SNI], 'listeners': [RET_LISTENER_TLS_SNI],
'connection_limit': RET_LISTENER_TLS_SNI['connection_limit'],
'pools': [RET_POOL]} 'pools': [RET_POOL]}
@ -148,7 +151,8 @@ def sample_vip_port_tuple():
def sample_listener_tuple(proto=None, monitor=True, persistence=True, def sample_listener_tuple(proto=None, monitor=True, persistence=True,
persistence_type=None, tls=False, sni=False): persistence_type=None, tls=False, sni=False,
connection_limit=98):
proto = 'HTTP' if proto is None else proto proto = 'HTTP' if proto is None else proto
port = '443' if proto is 'HTTPS' or proto is 'TERMINATED_HTTPS' else '80' port = '443' if proto is 'HTTPS' or proto is 'TERMINATED_HTTPS' else '80'
in_listener = collections.namedtuple( in_listener = collections.namedtuple(
@ -164,7 +168,7 @@ def sample_listener_tuple(proto=None, monitor=True, persistence=True,
default_pool=sample_pool_tuple( default_pool=sample_pool_tuple(
proto=proto, monitor=monitor, persistence=persistence, proto=proto, monitor=monitor, persistence=persistence,
persistence_type=persistence_type), persistence_type=persistence_type),
connection_limit=98, connection_limit=connection_limit,
admin_state_up=True, admin_state_up=True,
default_tls_container_id='cont_id_1' if tls else '', default_tls_container_id='cont_id_1' if tls else '',
sni_container_ids=['cont_id_2', 'cont_id_3'] if sni else [], sni_container_ids=['cont_id_2', 'cont_id_3'] if sni else [],
@ -284,6 +288,7 @@ def sample_base_expected_config(backend, frontend=None):
" group nogroup\n" " group nogroup\n"
" log /dev/log local0\n" " log /dev/log local0\n"
" log /dev/log local1 notice\n" " log /dev/log local1 notice\n"
" maxconn 98\n"
" stats socket /sock_path mode 0666 level user\n\n" " stats socket /sock_path mode 0666 level user\n\n"
"defaults\n" "defaults\n"
" log global\n" " log global\n"

View File

@ -447,6 +447,14 @@ class TestHaproxyCfg(base.BaseTestCase):
ret = jinja_cfg._transform_loadbalancer(in_lb, '/v2') ret = jinja_cfg._transform_loadbalancer(in_lb, '/v2')
self.assertEqual(sample_configs.RET_LB, ret) self.assertEqual(sample_configs.RET_LB, ret)
def test_compute_global_connection_limit(self):
lts = [
sample_configs.sample_listener_tuple(connection_limit=None),
sample_configs.sample_listener_tuple()]
in_listeners = [jinja_cfg._transform_listener(x, '/v2') for x in lts]
ret = jinja_cfg._compute_global_connection_limit(in_listeners)
self.assertEqual(2098, ret)
def test_include_member(self): def test_include_member(self):
ret = jinja_cfg._include_member( ret = jinja_cfg._include_member(
sample_configs.sample_member_tuple('sample_member_id_1', sample_configs.sample_member_tuple('sample_member_id_1',