WIP: Add gunicorn as wsgi server
Simple replacement using gunicorn as a wsgi server instead of eventlets builtin server. This commit only adds a basic gunicorn wsgi server which can be used if the environment setting "DISABLE_EVENTLET=true" is set. Eventlet dependencies are not removed with this commit, and in fact both gunicorn and eventlet are running together (though not performant). Change-Id: Ia47a23dfafe193527506771d351ada806eed69b7 Signed-off-by: Christian Schwede <cschwede@redhat.com>
This commit is contained in:
committed by
Tim Burke
parent
8f5464fd37
commit
ed267339b0
@@ -26,6 +26,7 @@ future==0.16.0
|
|||||||
gitdb2==2.0.3
|
gitdb2==2.0.3
|
||||||
GitPython==2.1.8
|
GitPython==2.1.8
|
||||||
greenlet==0.4.14
|
greenlet==0.4.14
|
||||||
|
gunicorn==20.0.0
|
||||||
idna==2.6
|
idna==2.6
|
||||||
imagesize==1.0.0
|
imagesize==1.0.0
|
||||||
iso8601==0.1.12
|
iso8601==0.1.12
|
||||||
|
|||||||
@@ -11,3 +11,4 @@ xattr>=0.7.2;sys_platform!='win32' # MIT
|
|||||||
PyECLib>=1.3.1,!=1.6.2,!=1.6.3 # BSD
|
PyECLib>=1.3.1,!=1.6.2,!=1.6.3 # BSD
|
||||||
cryptography>=2.0.2 # BSD/Apache-2.0
|
cryptography>=2.0.2 # BSD/Apache-2.0
|
||||||
dnspython>=1.15.0 # http://www.dnspython.org/LICENSE
|
dnspython>=1.15.0 # http://www.dnspython.org/LICENSE
|
||||||
|
gunicorn>=20.0.0 # MIT
|
||||||
|
|||||||
@@ -47,6 +47,13 @@ import glob
|
|||||||
import itertools
|
import itertools
|
||||||
import stat
|
import stat
|
||||||
|
|
||||||
|
from swift.common.utils.config import config_true_value
|
||||||
|
|
||||||
|
|
||||||
|
def eventlet_disabled():
|
||||||
|
return config_true_value(os.environ.get('DISABLE_EVENTLET'))
|
||||||
|
|
||||||
|
|
||||||
import eventlet
|
import eventlet
|
||||||
import eventlet.debug
|
import eventlet.debug
|
||||||
import eventlet.greenthread
|
import eventlet.greenthread
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ from paste.deploy import loadwsgi
|
|||||||
from eventlet.green import socket, ssl, os as green_os
|
from eventlet.green import socket, ssl, os as green_os
|
||||||
from io import BytesIO, StringIO
|
from io import BytesIO, StringIO
|
||||||
|
|
||||||
|
import gunicorn.app.base
|
||||||
|
|
||||||
from swift.common import utils, constraints
|
from swift.common import utils, constraints
|
||||||
from swift.common.http_protocol import SwiftHttpProtocol, \
|
from swift.common.http_protocol import SwiftHttpProtocol, \
|
||||||
SwiftHttpProxiedProtocol
|
SwiftHttpProxiedProtocol
|
||||||
@@ -41,7 +43,8 @@ from swift.common.swob import Request, wsgi_unquote
|
|||||||
from swift.common.utils import capture_stdio, disable_fallocate, \
|
from swift.common.utils import capture_stdio, disable_fallocate, \
|
||||||
drop_privileges, get_logger, NullLogger, config_true_value, \
|
drop_privileges, get_logger, NullLogger, config_true_value, \
|
||||||
validate_configuration, get_hub, config_auto_int_value, \
|
validate_configuration, get_hub, config_auto_int_value, \
|
||||||
reiterate, clean_up_daemon_hygiene, systemd_notify, NicerInterpolation
|
reiterate, clean_up_daemon_hygiene, systemd_notify, NicerInterpolation, \
|
||||||
|
eventlet_disabled
|
||||||
|
|
||||||
SIGNUM_TO_NAME = {getattr(signal, n): n for n in dir(signal)
|
SIGNUM_TO_NAME = {getattr(signal, n): n for n in dir(signal)
|
||||||
if n.startswith('SIG') and '_' not in n}
|
if n.startswith('SIG') and '_' not in n}
|
||||||
@@ -401,8 +404,8 @@ def load_app_config(conf_file):
|
|||||||
return app_conf
|
return app_conf
|
||||||
|
|
||||||
|
|
||||||
def run_server(conf, logger, sock, global_conf=None, ready_callback=None,
|
def _run_server_eventlet(conf, logger, sock, global_conf=None,
|
||||||
allow_modify_pipeline=True):
|
ready_callback=None, allow_modify_pipeline=True):
|
||||||
# Ensure TZ environment variable exists to avoid stat('/etc/localtime') on
|
# Ensure TZ environment variable exists to avoid stat('/etc/localtime') on
|
||||||
# some platforms. This locks in reported times to UTC.
|
# some platforms. This locks in reported times to UTC.
|
||||||
os.environ['TZ'] = 'UTC+0'
|
os.environ['TZ'] = 'UTC+0'
|
||||||
@@ -462,6 +465,60 @@ def run_server(conf, logger, sock, global_conf=None, ready_callback=None,
|
|||||||
app._pipeline_final_app.watchdog.kill()
|
app._pipeline_final_app.watchdog.kill()
|
||||||
|
|
||||||
|
|
||||||
|
def _run_server_gunicorn(conf, logger, sock, global_conf=None,
|
||||||
|
ready_callback=None, allow_modify_pipeline=True):
|
||||||
|
# Ensure TZ environment variable exists to avoid stat('/etc/localtime') on
|
||||||
|
# some platforms. This locks in reported times to UTC.
|
||||||
|
os.environ['TZ'] = 'UTC+0'
|
||||||
|
time.tzset()
|
||||||
|
|
||||||
|
options = {
|
||||||
|
'bind': '%s:%s' % (
|
||||||
|
conf.get('bind_ip', '0.0.0.0'),
|
||||||
|
int(conf.get('bind_port')),
|
||||||
|
),
|
||||||
|
'workers': conf.get('workers', CPU_COUNT),
|
||||||
|
'keepalive': int(conf.get('keepalive_timeout', 2)),
|
||||||
|
'limit_request_line': constraints.MAX_HEADER_SIZE,
|
||||||
|
'backlog': int(conf.get('max_clients', '1024')),
|
||||||
|
}
|
||||||
|
|
||||||
|
if sock:
|
||||||
|
fd = sock.fileno()
|
||||||
|
options['bind'] = f'fd://{fd}'
|
||||||
|
|
||||||
|
if not global_conf:
|
||||||
|
if hasattr(logger, 'server'):
|
||||||
|
log_name = logger.server
|
||||||
|
else:
|
||||||
|
log_name = logger.name
|
||||||
|
global_conf = {'log_name': log_name}
|
||||||
|
app = loadapp(conf['__file__'], global_conf=global_conf,
|
||||||
|
allow_modify_pipeline=allow_modify_pipeline)
|
||||||
|
|
||||||
|
if ready_callback:
|
||||||
|
ready_callback()
|
||||||
|
|
||||||
|
try:
|
||||||
|
GunicornApplication(app, options).run()
|
||||||
|
except socket.error as err:
|
||||||
|
if err.errno != errno.EINVAL:
|
||||||
|
raise
|
||||||
|
finally:
|
||||||
|
if hasattr(app._pipeline_final_app, 'watchdog'):
|
||||||
|
app._pipeline_final_app.watchdog.kill()
|
||||||
|
|
||||||
|
|
||||||
|
def run_server(conf, logger, sock, global_conf=None, ready_callback=None,
|
||||||
|
allow_modify_pipeline=True):
|
||||||
|
if not eventlet_disabled():
|
||||||
|
_run_server_eventlet(conf, logger, sock, global_conf, ready_callback,
|
||||||
|
allow_modify_pipeline)
|
||||||
|
else:
|
||||||
|
_run_server_gunicorn(conf, logger, sock, global_conf, ready_callback,
|
||||||
|
allow_modify_pipeline)
|
||||||
|
|
||||||
|
|
||||||
class StrategyBase(object):
|
class StrategyBase(object):
|
||||||
"""
|
"""
|
||||||
Some operations common to all strategy classes.
|
Some operations common to all strategy classes.
|
||||||
@@ -1436,3 +1493,21 @@ def make_pre_authed_request(env, method=None, path=None, body=None,
|
|||||||
return make_subrequest(
|
return make_subrequest(
|
||||||
env, method=method, path=path, body=body, headers=headers, agent=agent,
|
env, method=method, path=path, body=body, headers=headers, agent=agent,
|
||||||
swift_source=swift_source, make_env=make_pre_authed_env)
|
swift_source=swift_source, make_env=make_pre_authed_env)
|
||||||
|
|
||||||
|
|
||||||
|
# https://docs.gunicorn.org/en/latest/custom.html#custom-application
|
||||||
|
class GunicornApplication(gunicorn.app.base.BaseApplication):
|
||||||
|
|
||||||
|
def __init__(self, app, options=None):
|
||||||
|
self.options = options or {}
|
||||||
|
self.application = app
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
def load_config(self):
|
||||||
|
config = {key: value for key, value in self.options.items()
|
||||||
|
if key in self.cfg.settings and value is not None}
|
||||||
|
for key, value in config.items():
|
||||||
|
self.cfg.set(key.lower(), value)
|
||||||
|
|
||||||
|
def load(self):
|
||||||
|
return self.application
|
||||||
|
|||||||
Reference in New Issue
Block a user