-
Notifications
You must be signed in to change notification settings - Fork 161
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
Warning: Silently ignoring app.run() because the application is run from the flask command line executable #135
Comments
Hey, thanks for flagging this! |
Sure, I will put something together and get back to you soon. |
Here's a working example: https://paste.sr.ht/~raxod502/e7527b5868cdee04160f457870299ff33ce0c1ed
|
Now that I've had a moment to review the source, I can confirm that simply adding this code before invoking os.environ.pop("FLASK_RUN_FROM_CLI") |
However, when running in development mode (with the live reloader) we encounter an additional problem, because the metrics Flask app tries to start its own live reloader from inside the first live reloader, which does not work since the live reloader establishes signal handlers and therefore can only run from the main thread:
|
It appears to be possible to work around this issue by unsetting
Working code: import os
import flask
import prometheus_flask_exporter
app = flask.Flask(__name__)
os.environ.pop("FLASK_RUN_FROM_CLI")
os.environ.pop("FLASK_ENV")
metrics = prometheus_flask_exporter.PrometheusMetrics(app)
metrics.start_http_server(8081, host="127.0.0.1") |
Thanks a lot for the investigation @raxod502 ! 🙇 |
Yeah... and environment variables are shared globally for the whole process, so messing with them is really nasty. Let's file an issue upstream: pallets/flask#4616 If a keyword argument is added to |
This metrics server does not appear to be part of the application under development. It is its own, separate application. Therefore, it should be run using a production server, even locally. Production servers include Gunicorn, Waitress, uWSGI, mod_wsgi, Eventlet, and Gevent. |
OK, sounds like the way to resolve this issue would be to modify prometheus_flask_exporter to use Gunicorn or an equivalent framework by default, since the official upstream recommendation is that prometheus_flask_exporter is not an appropriate use case for the Flask development server. Does that seem like an appropriate course of action to you, @rycus86? |
I'll have a look at this more deeply when I get a chance. I wouldn't necessarily want Gunicorn or other dependencies to be mandatory, so I'll need to dig deeper into what am I doing wrong in the code that surfaces this problem (sounds like maybe I use the wrong app somewhere perhaps). |
OK, so I had a chance now to look at the code. If this is intended for development mode only, I think it should be OK to just have the metrics endpoint exposed on the main Flask app, which happens by default.
If you
Does this work for you @raxod502 ? |
That works for development mode, but what about in production? It sounds like the Flask folks are saying it is not appropriate to use the Flask development server in production, yet it seems like prometheus_flask_exporter does not have an option to use anything other than the Flask development server. What if we add an API to prometheus_flask_exporter that simply returns the Flask app for exposing metrics, so that the user may choose how they wish to serve that application (e.g. by adding a dependency on Gunicorn or a similar WGSI package)? |
We do have examples on how to integrate with "production ready" servers, for example:
One thing to note: for the non-internal multiprocessing enabled Prometheus exporter wrappers, we use the
We could perhaps expose the contents of https://github.com/rycus86/prometheus_flask_exporter/blob/master/prometheus_flask_exporter/__init__.py#L272 as a function that you can then call from an endpoint you supply in whatever way is appropriate - if that's desirable, we can look into that. |
Ah, got it. That is indeed what I was looking for. I didn't read this part of the documentation at first because it was labeled for multiprocess-aware handling, which wasn't something I needed in my application. Should we migrate to using this function in all cases where the user is placing the metrics endpoint on a different port, so that we eliminate all use of the Flask development server except insofar as the user is running it themselves? I think that would resolve the issues that upstream has raised. |
I just looked quickly at |
They already have different (more robust?) ways to expose metrics over HTTP, see https://github.com/prometheus/client_python#http
Maybe this is worth calling out in this project's README, and suggest some alternatives - though on that note I think if you run the metrics endpoint on an insecure server that is not available for external users, it may be something that is an acceptable tradeoff for some. |
Yeah - although the development server is not "production ready", it may be less bad to use it for an internal metrics endpoint that will be seeing <0.1 request per second, and is not accessible to malicious outside traffic. But, if it's not production ready, we should still do our best to follow best practices and use something that is recommended instead. |
OK, so I added |
Yeah, I would assume that you would want to expose the second webserver from within the same process. Looking into the options in #135 (comment), it seems like Waitress, Eventlet, and Gevent are acceptable options. Since the Flask project doesn't want to support usage of the Flask development server even for internal facing metrics, I don't think we have any option other than to use a pure-Python WSGI server implementation to produce metrics from in-process. Then of course in production we must also use the existing techniques in prometheus_flask_exporter to ensure metrics are shared between all worker processes. Seems to me like it's either that or abandon the idea of serving metrics on a separate port. |
You should alternatively be able to set |
Well, sure, but only if we feel comfortable ignoring guidance from upstream that the Flask dev server (which is what gets run when you use |
My suggestion was an alternative to In my case, I'm doing the following os.environ.pop("FLASK_RUN_FROM_CLI")
app.run(debug=False, use_reloader=False) |
I agree with you, however, the comment from @davidism says this:
Which I'm interpreting to mean that using the Flask development server for something that is not the main Flask app, even in development, is not a supported use case by upstream. I have to confess I am not sure why, but if that's what we have to work with, it's what we have to work with. |
Gotcha. In that case, how hard would it be to make the metrics server "part of the application under development"? Maybe something like, Run Multiple Flask Applications from the Same Server. Granted, I haven't looked into this codebase to know how applicable that will be. My own reason for encountering the error discussed in this issue is that I needed to spawn a temporary server on a random open port to check a user's authentication. This temp server can run independently of the Flask app itself. I.e., to run the service normally, Here's a trimmed-down version of my code for reference: from flask import Flask
from signal import pthread_kill, SIGTSTP
from threading import Thread
import requests
app = Flask(__name__)
def find_free_port():
# implementation removed for brevity
return 5643
@app.cli.command("auth")
def auth(name):
port = find_free_port()
kwargs = {"port": port, "debug": False, "use_reloader": False}
thread = Thread(target=app.run, kwargs=kwargs)
thread.start()
url = f"http://localhost:{port}/auth"
r = requests.get(url)
refresh_token = r.json()["token"].get("refresh_token"):
print(f"refresh token is {refresh_token}")
pthread_kill(thread.ident, SIGTSTP) |
I am not sure this is possible.
That is for doing routing within a single WSGI application to proxy to multiple sub-applications by URL path. What we want is to run multiple top-level WSGI applications separately. As far as I can tell, what the Flask project is saying is you can't use |
Does this help? https://github.com/yrro/hitron-exporter/blob/4add40236f52042c381343fb3a696a6dead6cab0/hitron_exporter/__init__.py#L41 (I'm investigating an issue whereby [edit] to clarify, when I run |
I had a look at your code and checked it with a minimal example: from flask import Flask
from prometheus_client import make_wsgi_app
from prometheus_flask_exporter import PrometheusMetrics
app = Flask(__name__)
metrics = PrometheusMetrics(app)
metrics_wsgi_app = make_wsgi_app()
@app.get('/info')
def info():
import os
return {'response': 'ok', 'env': dict(os.environ)}
@app.get('/metrics')
@metrics.do_not_track()
def metrics():
return metrics_wsgi_app
if __name__ == '__main__':
app.run('0.0.0.0', 5000, debug=True, use_reloader=True) This works with edit: I've pushed the example code here: https://github.com/rycus86/prometheus_flask_exporter/tree/master/examples/flask-run-135 |
An awkward situation. Code:
Result:
And the metrics server does not listen on port 8081. It seems that Flask notices that I have invoked the main application using
flask run
, and has elected to disable the ability to run new Flask applications, which obviously breaksstart_http_server
from prometheus_flask_exporter.Not sure what the best way to handle this is, but wanted to flag.
The text was updated successfully, but these errors were encountered: