Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use gunicorn instead of the Werkzeug development server #132

Merged
merged 3 commits into from
Apr 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ RUN apk update && apk upgrade

RUN apk add --no-cache \
ca-certificates \
py3-gunicorn \
py3-paramiko \
py3-pip \
py3-prometheus-client \
Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"pyyaml",
"requests",
'Werkzeug',
'gunicorn',
],
classifiers=[
"Development Status :: 3 - Alpha",
Expand Down
11 changes: 10 additions & 1 deletion src/pve_exporter/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ def main():
help='Port on which the exporter is listening (9221)')
parser.add_argument('address', nargs='?', default='',
help='Address to which the exporter will bind')
parser.add_argument('--server.keyfile', dest='server_keyfile', help='SSL key for server')
parser.add_argument('--server.certfile', dest='server_certfile', help='SSL certificate for server')

params = parser.parse_args()

Expand All @@ -107,7 +109,14 @@ def main():
with open(params.config) as handle:
config = config_from_yaml(yaml.safe_load(handle))

gunicorn_options = {
'bind': f'{params.address}:{params.port}',
'threads': 2,
'keyfile': params.server_keyfile,
'certfile': params.server_certfile,
}

if config.valid:
start_http_server(config, params.port, params.address, collectors)
start_http_server(config, gunicorn_options, collectors)
else:
parser.error(str(config))
26 changes: 23 additions & 3 deletions src/pve_exporter/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
import logging
import time

import gunicorn.app.base
from prometheus_client import CONTENT_TYPE_LATEST, Summary, Counter, generate_latest
from werkzeug.routing import Map, Rule
from werkzeug.serving import run_simple
from werkzeug.wrappers import Request, Response
from werkzeug.exceptions import InternalServerError
from .collector import collect_pve
Expand Down Expand Up @@ -112,7 +112,27 @@ def __call__(self, request):
return urls.dispatch(view_func, catch_http_exceptions=True)


def start_http_server(config, port, address, collectors):
class StandaloneGunicornApplication(gunicorn.app.base.BaseApplication):
"""
Copy-paste from https://docs.gunicorn.org/en/stable/custom.html
"""

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)
Comment on lines +125 to +129
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is hardly readable. However, it follows the upstream example and I honestly have no better idea on how to translate the options dictionary into the gunicorn cfg format. Hence, I'm okay with it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. At least I added a comment pointing out the source.


def load(self):
return self.application


def start_http_server(config, gunicorn_options, collectors):
"""
Start a HTTP API server for Proxmox VE prometheus collector.
"""
Expand All @@ -136,4 +156,4 @@ def start_http_server(config, port, address, collectors):
duration.labels(module)

app = PveExporterApplication(config, duration, errors, collectors)
run_simple(address, port, app, threaded=True)
StandaloneGunicornApplication(app, gunicorn_options).run()