forked from orbit-oss/flask
rewrite deployment docs
This commit is contained in:
parent
9398630a8f
commit
2ea77c2782
15 changed files with 807 additions and 829 deletions
|
|
@ -70,8 +70,8 @@ If you wish to use background tasks it is best to use a task queue to
|
||||||
trigger background work, rather than spawn tasks in a view
|
trigger background work, rather than spawn tasks in a view
|
||||||
function. With that in mind you can spawn asyncio tasks by serving
|
function. With that in mind you can spawn asyncio tasks by serving
|
||||||
Flask with an ASGI server and utilising the asgiref WsgiToAsgi adapter
|
Flask with an ASGI server and utilising the asgiref WsgiToAsgi adapter
|
||||||
as described in :ref:`asgi`. This works as the adapter creates an
|
as described in :doc:`deploying/asgi`. This works as the adapter creates
|
||||||
event loop that runs continually.
|
an event loop that runs continually.
|
||||||
|
|
||||||
|
|
||||||
When to use Quart instead
|
When to use Quart instead
|
||||||
|
|
|
||||||
66
docs/deploying/apache-httpd.rst
Normal file
66
docs/deploying/apache-httpd.rst
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
Apache httpd
|
||||||
|
============
|
||||||
|
|
||||||
|
`Apache httpd`_ is a fast, production level HTTP server. When serving
|
||||||
|
your application with one of the WSGI servers listed in :doc:`index`, it
|
||||||
|
is often good or necessary to put a dedicated HTTP server in front of
|
||||||
|
it. This "reverse proxy" can handle incoming requests, TLS, and other
|
||||||
|
security and performance concerns better than the WSGI server.
|
||||||
|
|
||||||
|
httpd can be installed using your system package manager, or a pre-built
|
||||||
|
executable for Windows. Installing and running httpd itself is outside
|
||||||
|
the scope of this doc. This page outlines the basics of configuring
|
||||||
|
httpd to proxy your application. Be sure to read its documentation to
|
||||||
|
understand what features are available.
|
||||||
|
|
||||||
|
.. _Apache httpd: https://httpd.apache.org/
|
||||||
|
|
||||||
|
|
||||||
|
Domain Name
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Acquiring and configuring a domain name is outside the scope of this
|
||||||
|
doc. In general, you will buy a domain name from a registrar, pay for
|
||||||
|
server space with a hosting provider, and then point your registrar
|
||||||
|
at the hosting provider's name servers.
|
||||||
|
|
||||||
|
To simulate this, you can also edit your ``hosts`` file, located at
|
||||||
|
``/etc/hosts`` on Linux. Add a line that associates a name with the
|
||||||
|
local IP.
|
||||||
|
|
||||||
|
Modern Linux systems may be configured to treat any domain name that
|
||||||
|
ends with ``.localhost`` like this without adding it to the ``hosts``
|
||||||
|
file.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
:caption: ``/etc/hosts``
|
||||||
|
|
||||||
|
127.0.0.1 hello.localhost
|
||||||
|
|
||||||
|
|
||||||
|
Configuration
|
||||||
|
-------------
|
||||||
|
|
||||||
|
The httpd configuration is located at ``/etc/httpd/conf/httpd.conf`` on
|
||||||
|
Linux. It may be different depending on your operating system. Check the
|
||||||
|
docs and look for ``httpd.conf``.
|
||||||
|
|
||||||
|
Remove or comment out any existing ``DocumentRoot`` directive. Add the
|
||||||
|
config lines below. We'll assume the WSGI server is listening locally at
|
||||||
|
``http://127.0.0.1:8000``.
|
||||||
|
|
||||||
|
.. code-block:: apache
|
||||||
|
:caption: ``/etc/httpd/conf/httpd.conf``
|
||||||
|
|
||||||
|
LoadModule proxy_module modules/mod_proxy.so
|
||||||
|
LoadModule proxy_http_module modules/mod_proxy_http.so
|
||||||
|
ProxyPass / http://127.0.0.1:8000/
|
||||||
|
RequestHeader set X-Forwarded-Proto http
|
||||||
|
RequestHeader set X-Forwarded-Prefix /
|
||||||
|
|
||||||
|
The ``LoadModule`` lines might already exist. If so, make sure they are
|
||||||
|
uncommented instead of adding them manually.
|
||||||
|
|
||||||
|
Then :doc:`proxy_fix` so that your application uses the ``X-Forwarded``
|
||||||
|
headers. ``X-Forwarded-For`` and ``X-Forwarded-Host`` are automatically
|
||||||
|
set by ``ProxyPass``.
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
.. _asgi:
|
|
||||||
|
|
||||||
ASGI
|
ASGI
|
||||||
====
|
====
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,61 +0,0 @@
|
||||||
CGI
|
|
||||||
===
|
|
||||||
|
|
||||||
If all other deployment methods do not work, CGI will work for sure.
|
|
||||||
CGI is supported by all major servers but usually has a sub-optimal
|
|
||||||
performance.
|
|
||||||
|
|
||||||
This is also the way you can use a Flask application on Google's `App
|
|
||||||
Engine`_, where execution happens in a CGI-like environment.
|
|
||||||
|
|
||||||
.. admonition:: Watch Out
|
|
||||||
|
|
||||||
Please make sure in advance that any ``app.run()`` calls you might
|
|
||||||
have in your application file are inside an ``if __name__ ==
|
|
||||||
'__main__':`` block or moved to a separate file. Just make sure it's
|
|
||||||
not called because this will always start a local WSGI server which
|
|
||||||
we do not want if we deploy that application to CGI / app engine.
|
|
||||||
|
|
||||||
With CGI, you will also have to make sure that your code does not contain
|
|
||||||
any ``print`` statements, or that ``sys.stdout`` is overridden by something
|
|
||||||
that doesn't write into the HTTP response.
|
|
||||||
|
|
||||||
Creating a `.cgi` file
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
First you need to create the CGI application file. Let's call it
|
|
||||||
:file:`yourapplication.cgi`::
|
|
||||||
|
|
||||||
#!/usr/bin/python
|
|
||||||
from wsgiref.handlers import CGIHandler
|
|
||||||
from yourapplication import app
|
|
||||||
|
|
||||||
CGIHandler().run(app)
|
|
||||||
|
|
||||||
Server Setup
|
|
||||||
------------
|
|
||||||
|
|
||||||
Usually there are two ways to configure the server. Either just copy the
|
|
||||||
``.cgi`` into a :file:`cgi-bin` (and use `mod_rewrite` or something similar to
|
|
||||||
rewrite the URL) or let the server point to the file directly.
|
|
||||||
|
|
||||||
In Apache for example you can put something like this into the config:
|
|
||||||
|
|
||||||
.. sourcecode:: apache
|
|
||||||
|
|
||||||
ScriptAlias /app /path/to/the/application.cgi
|
|
||||||
|
|
||||||
On shared webhosting, though, you might not have access to your Apache config.
|
|
||||||
In this case, a file called ``.htaccess``, sitting in the public directory
|
|
||||||
you want your app to be available, works too but the ``ScriptAlias`` directive
|
|
||||||
won't work in that case:
|
|
||||||
|
|
||||||
.. sourcecode:: apache
|
|
||||||
|
|
||||||
RewriteEngine On
|
|
||||||
RewriteCond %{REQUEST_FILENAME} !-f # Don't interfere with static files
|
|
||||||
RewriteRule ^(.*)$ /path/to/the/application.cgi/$1 [L]
|
|
||||||
|
|
||||||
For more information consult the documentation of your webserver.
|
|
||||||
|
|
||||||
.. _App Engine: https://cloud.google.com/appengine/docs/
|
|
||||||
80
docs/deploying/eventlet.rst
Normal file
80
docs/deploying/eventlet.rst
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
eventlet
|
||||||
|
========
|
||||||
|
|
||||||
|
Prefer using :doc:`gunicorn` with eventlet workers rather than using
|
||||||
|
`eventlet`_ directly. Gunicorn provides a much more configurable and
|
||||||
|
production-tested server.
|
||||||
|
|
||||||
|
`eventlet`_ allows writing asynchronous, coroutine-based code that looks
|
||||||
|
like standard synchronous Python. It uses `greenlet`_ to enable task
|
||||||
|
switching without writing ``async/await`` or using ``asyncio``.
|
||||||
|
|
||||||
|
:doc:`gevent` is another library that does the same thing. Certain
|
||||||
|
dependencies you have, or other considerations, may affect which of the
|
||||||
|
two you choose to use.
|
||||||
|
|
||||||
|
eventlet provides a WSGI server that can handle many connections at once
|
||||||
|
instead of one per worker process. You must actually use eventlet in
|
||||||
|
your own code to see any benefit to using the server.
|
||||||
|
|
||||||
|
.. _eventlet: https://eventlet.net/
|
||||||
|
.. _greenlet: https://greenlet.readthedocs.io/en/latest/
|
||||||
|
|
||||||
|
|
||||||
|
Installing
|
||||||
|
----------
|
||||||
|
|
||||||
|
When using eventlet, greenlet>=1.0 is required, otherwise context locals
|
||||||
|
such as ``request`` will not work as expected. When using PyPy,
|
||||||
|
PyPy>=7.3.7 is required.
|
||||||
|
|
||||||
|
Create a virtualenv, install your application, then install
|
||||||
|
``eventlet``.
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
$ cd hello-app
|
||||||
|
$ python -m venv venv
|
||||||
|
$ . venv/bin/activate
|
||||||
|
$ pip install . # install your application
|
||||||
|
$ pip install eventlet
|
||||||
|
|
||||||
|
|
||||||
|
Running
|
||||||
|
-------
|
||||||
|
|
||||||
|
To use eventlet to serve your application, write a script that imports
|
||||||
|
its ```wsgi.server``, as well as your app or app factory.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
:caption: ``wsgi.py``
|
||||||
|
|
||||||
|
import eventlet
|
||||||
|
from eventlet import wsgi
|
||||||
|
from hello import create_app
|
||||||
|
|
||||||
|
app = create_app()
|
||||||
|
wsgi.server(eventlet.listen(("127.0.0.1", 8000), app)
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
$ python wsgi.py
|
||||||
|
(x) wsgi starting up on http://127.0.0.1:8000
|
||||||
|
|
||||||
|
|
||||||
|
Binding Externally
|
||||||
|
------------------
|
||||||
|
|
||||||
|
eventlet should not be run as root because it would cause your
|
||||||
|
application code to run as root, which is not secure. However, this
|
||||||
|
means it will not be possible to bind to port 80 or 443. Instead, a
|
||||||
|
reverse proxy such as :doc:`nginx` or :doc:`apache-httpd` should be used
|
||||||
|
in front of eventlet.
|
||||||
|
|
||||||
|
You can bind to all external IPs on a non-privileged port by using
|
||||||
|
``0.0.0.0`` in the server arguments shown in the previous section.
|
||||||
|
Don't do this when using a reverse proxy setup, otherwise it will be
|
||||||
|
possible to bypass the proxy.
|
||||||
|
|
||||||
|
``0.0.0.0`` is not a valid address to navigate to, you'd use a specific
|
||||||
|
IP address in your browser.
|
||||||
|
|
@ -1,238 +0,0 @@
|
||||||
FastCGI
|
|
||||||
=======
|
|
||||||
|
|
||||||
FastCGI is a deployment option on servers like `nginx`_, `lighttpd`_, and
|
|
||||||
`cherokee`_; see :doc:`uwsgi` and :doc:`wsgi-standalone` for other options.
|
|
||||||
To use your WSGI application with any of them you will need a FastCGI
|
|
||||||
server first. The most popular one is `flup`_ which we will use for
|
|
||||||
this guide. Make sure to have it installed to follow along.
|
|
||||||
|
|
||||||
.. admonition:: Watch Out
|
|
||||||
|
|
||||||
Please make sure in advance that any ``app.run()`` calls you might
|
|
||||||
have in your application file are inside an ``if __name__ ==
|
|
||||||
'__main__':`` block or moved to a separate file. Just make sure it's
|
|
||||||
not called because this will always start a local WSGI server which
|
|
||||||
we do not want if we deploy that application to FastCGI.
|
|
||||||
|
|
||||||
Creating a `.fcgi` file
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
First you need to create the FastCGI server file. Let's call it
|
|
||||||
`yourapplication.fcgi`::
|
|
||||||
|
|
||||||
#!/usr/bin/python
|
|
||||||
from flup.server.fcgi import WSGIServer
|
|
||||||
from yourapplication import app
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
WSGIServer(app).run()
|
|
||||||
|
|
||||||
This is enough for Apache to work, however nginx and older versions of
|
|
||||||
lighttpd need a socket to be explicitly passed to communicate with the
|
|
||||||
FastCGI server. For that to work you need to pass the path to the
|
|
||||||
socket to the :class:`~flup.server.fcgi.WSGIServer`::
|
|
||||||
|
|
||||||
WSGIServer(application, bindAddress='/path/to/fcgi.sock').run()
|
|
||||||
|
|
||||||
The path has to be the exact same path you define in the server
|
|
||||||
config.
|
|
||||||
|
|
||||||
Save the :file:`yourapplication.fcgi` file somewhere you will find it again.
|
|
||||||
It makes sense to have that in :file:`/var/www/yourapplication` or something
|
|
||||||
similar.
|
|
||||||
|
|
||||||
Make sure to set the executable bit on that file so that the servers
|
|
||||||
can execute it:
|
|
||||||
|
|
||||||
.. sourcecode:: text
|
|
||||||
|
|
||||||
$ chmod +x /var/www/yourapplication/yourapplication.fcgi
|
|
||||||
|
|
||||||
Configuring Apache
|
|
||||||
------------------
|
|
||||||
|
|
||||||
The example above is good enough for a basic Apache deployment but your
|
|
||||||
`.fcgi` file will appear in your application URL e.g.
|
|
||||||
``example.com/yourapplication.fcgi/news/``. There are few ways to configure
|
|
||||||
your application so that yourapplication.fcgi does not appear in the URL.
|
|
||||||
A preferable way is to use the ScriptAlias and SetHandler configuration
|
|
||||||
directives to route requests to the FastCGI server. The following example
|
|
||||||
uses FastCgiServer to start 5 instances of the application which will
|
|
||||||
handle all incoming requests::
|
|
||||||
|
|
||||||
LoadModule fastcgi_module /usr/lib64/httpd/modules/mod_fastcgi.so
|
|
||||||
|
|
||||||
FastCgiServer /var/www/html/yourapplication/app.fcgi -idle-timeout 300 -processes 5
|
|
||||||
|
|
||||||
<VirtualHost *>
|
|
||||||
ServerName webapp1.mydomain.com
|
|
||||||
DocumentRoot /var/www/html/yourapplication
|
|
||||||
|
|
||||||
AddHandler fastcgi-script fcgi
|
|
||||||
ScriptAlias / /var/www/html/yourapplication/app.fcgi/
|
|
||||||
|
|
||||||
<Location />
|
|
||||||
SetHandler fastcgi-script
|
|
||||||
</Location>
|
|
||||||
</VirtualHost>
|
|
||||||
|
|
||||||
These processes will be managed by Apache. If you're using a standalone
|
|
||||||
FastCGI server, you can use the FastCgiExternalServer directive instead.
|
|
||||||
Note that in the following the path is not real, it's simply used as an
|
|
||||||
identifier to other
|
|
||||||
directives such as AliasMatch::
|
|
||||||
|
|
||||||
FastCgiServer /var/www/html/yourapplication -host 127.0.0.1:3000
|
|
||||||
|
|
||||||
If you cannot set ScriptAlias, for example on a shared web host, you can use
|
|
||||||
WSGI middleware to remove yourapplication.fcgi from the URLs. Set .htaccess::
|
|
||||||
|
|
||||||
<IfModule mod_fcgid.c>
|
|
||||||
AddHandler fcgid-script .fcgi
|
|
||||||
<Files ~ (\.fcgi)>
|
|
||||||
SetHandler fcgid-script
|
|
||||||
Options +FollowSymLinks +ExecCGI
|
|
||||||
</Files>
|
|
||||||
</IfModule>
|
|
||||||
|
|
||||||
<IfModule mod_rewrite.c>
|
|
||||||
Options +FollowSymlinks
|
|
||||||
RewriteEngine On
|
|
||||||
RewriteBase /
|
|
||||||
RewriteCond %{REQUEST_FILENAME} !-f
|
|
||||||
RewriteRule ^(.*)$ yourapplication.fcgi/$1 [QSA,L]
|
|
||||||
</IfModule>
|
|
||||||
|
|
||||||
Set yourapplication.fcgi::
|
|
||||||
|
|
||||||
#!/usr/bin/python
|
|
||||||
#: optional path to your local python site-packages folder
|
|
||||||
import sys
|
|
||||||
sys.path.insert(0, '<your_local_path>/lib/python<your_python_version>/site-packages')
|
|
||||||
|
|
||||||
from flup.server.fcgi import WSGIServer
|
|
||||||
from yourapplication import app
|
|
||||||
|
|
||||||
class ScriptNameStripper(object):
|
|
||||||
def __init__(self, app):
|
|
||||||
self.app = app
|
|
||||||
|
|
||||||
def __call__(self, environ, start_response):
|
|
||||||
environ['SCRIPT_NAME'] = ''
|
|
||||||
return self.app(environ, start_response)
|
|
||||||
|
|
||||||
app = ScriptNameStripper(app)
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
WSGIServer(app).run()
|
|
||||||
|
|
||||||
Configuring lighttpd
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
A basic FastCGI configuration for lighttpd looks like that::
|
|
||||||
|
|
||||||
fastcgi.server = ("/yourapplication.fcgi" =>
|
|
||||||
((
|
|
||||||
"socket" => "/tmp/yourapplication-fcgi.sock",
|
|
||||||
"bin-path" => "/var/www/yourapplication/yourapplication.fcgi",
|
|
||||||
"check-local" => "disable",
|
|
||||||
"max-procs" => 1
|
|
||||||
))
|
|
||||||
)
|
|
||||||
|
|
||||||
alias.url = (
|
|
||||||
"/static/" => "/path/to/your/static/"
|
|
||||||
)
|
|
||||||
|
|
||||||
url.rewrite-once = (
|
|
||||||
"^(/static($|/.*))$" => "$1",
|
|
||||||
"^(/.*)$" => "/yourapplication.fcgi$1"
|
|
||||||
)
|
|
||||||
|
|
||||||
Remember to enable the FastCGI, alias and rewrite modules. This configuration
|
|
||||||
binds the application to ``/yourapplication``. If you want the application to
|
|
||||||
work in the URL root you have to work around a lighttpd bug with the
|
|
||||||
:class:`~werkzeug.contrib.fixers.LighttpdCGIRootFix` middleware.
|
|
||||||
|
|
||||||
Make sure to apply it only if you are mounting the application the URL
|
|
||||||
root. Also, see the Lighty docs for more information on `FastCGI and Python
|
|
||||||
<https://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModFastCGI>`_ (note that
|
|
||||||
explicitly passing a socket to run() is no longer necessary).
|
|
||||||
|
|
||||||
Configuring nginx
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
Installing FastCGI applications on nginx is a bit different because by
|
|
||||||
default no FastCGI parameters are forwarded.
|
|
||||||
|
|
||||||
A basic Flask FastCGI configuration for nginx looks like this::
|
|
||||||
|
|
||||||
location = /yourapplication { rewrite ^ /yourapplication/ last; }
|
|
||||||
location /yourapplication { try_files $uri @yourapplication; }
|
|
||||||
location @yourapplication {
|
|
||||||
include fastcgi_params;
|
|
||||||
fastcgi_split_path_info ^(/yourapplication)(.*)$;
|
|
||||||
fastcgi_param PATH_INFO $fastcgi_path_info;
|
|
||||||
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
|
|
||||||
fastcgi_pass unix:/tmp/yourapplication-fcgi.sock;
|
|
||||||
}
|
|
||||||
|
|
||||||
This configuration binds the application to ``/yourapplication``. If you
|
|
||||||
want to have it in the URL root it's a bit simpler because you don't
|
|
||||||
have to figure out how to calculate ``PATH_INFO`` and ``SCRIPT_NAME``::
|
|
||||||
|
|
||||||
location / { try_files $uri @yourapplication; }
|
|
||||||
location @yourapplication {
|
|
||||||
include fastcgi_params;
|
|
||||||
fastcgi_param PATH_INFO $fastcgi_script_name;
|
|
||||||
fastcgi_param SCRIPT_NAME "";
|
|
||||||
fastcgi_pass unix:/tmp/yourapplication-fcgi.sock;
|
|
||||||
}
|
|
||||||
|
|
||||||
Running FastCGI Processes
|
|
||||||
-------------------------
|
|
||||||
|
|
||||||
Since nginx and others do not load FastCGI apps, you have to do it by
|
|
||||||
yourself. `Supervisor can manage FastCGI processes.
|
|
||||||
<http://supervisord.org/configuration.html#fcgi-program-x-section-settings>`_
|
|
||||||
You can look around for other FastCGI process managers or write a script
|
|
||||||
to run your `.fcgi` file at boot, e.g. using a SysV ``init.d`` script.
|
|
||||||
For a temporary solution, you can always run the ``.fcgi`` script inside
|
|
||||||
GNU screen. See ``man screen`` for details, and note that this is a
|
|
||||||
manual solution which does not persist across system restart::
|
|
||||||
|
|
||||||
$ screen
|
|
||||||
$ /var/www/yourapplication/yourapplication.fcgi
|
|
||||||
|
|
||||||
Debugging
|
|
||||||
---------
|
|
||||||
|
|
||||||
FastCGI deployments tend to be hard to debug on most web servers. Very
|
|
||||||
often the only thing the server log tells you is something along the
|
|
||||||
lines of "premature end of headers". In order to debug the application
|
|
||||||
the only thing that can really give you ideas why it breaks is switching
|
|
||||||
to the correct user and executing the application by hand.
|
|
||||||
|
|
||||||
This example assumes your application is called `application.fcgi` and
|
|
||||||
that your web server user is `www-data`::
|
|
||||||
|
|
||||||
$ su www-data
|
|
||||||
$ cd /var/www/yourapplication
|
|
||||||
$ python application.fcgi
|
|
||||||
Traceback (most recent call last):
|
|
||||||
File "yourapplication.fcgi", line 4, in <module>
|
|
||||||
ImportError: No module named yourapplication
|
|
||||||
|
|
||||||
In this case the error seems to be "yourapplication" not being on the
|
|
||||||
python path. Common problems are:
|
|
||||||
|
|
||||||
- Relative paths being used. Don't rely on the current working directory.
|
|
||||||
- The code depending on environment variables that are not set by the
|
|
||||||
web server.
|
|
||||||
- Different python interpreters being used.
|
|
||||||
|
|
||||||
.. _nginx: https://nginx.org/
|
|
||||||
.. _lighttpd: https://www.lighttpd.net/
|
|
||||||
.. _cherokee: https://cherokee-project.com/
|
|
||||||
.. _flup: https://pypi.org/project/flup/
|
|
||||||
80
docs/deploying/gevent.rst
Normal file
80
docs/deploying/gevent.rst
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
gevent
|
||||||
|
======
|
||||||
|
|
||||||
|
Prefer using :doc:`gunicorn` or :doc:`uwsgi` with gevent workers rather
|
||||||
|
than using `gevent`_ directly. Gunicorn and uWSGI provide much more
|
||||||
|
configurable and production-tested servers.
|
||||||
|
|
||||||
|
`gevent`_ allows writing asynchronous, coroutine-based code that looks
|
||||||
|
like standard synchronous Python. It uses `greenlet`_ to enable task
|
||||||
|
switching without writing ``async/await`` or using ``asyncio``.
|
||||||
|
|
||||||
|
:doc:`eventlet` is another library that does the same thing. Certain
|
||||||
|
dependencies you have, or other considerations, may affect which of the
|
||||||
|
two you choose to use.
|
||||||
|
|
||||||
|
gevent provides a WSGI server that can handle many connections at once
|
||||||
|
instead of one per worker process. You must actually use gevent in your
|
||||||
|
own code to see any benefit to using the server.
|
||||||
|
|
||||||
|
.. _gevent: https://www.gevent.org/
|
||||||
|
.. _greenlet: https://greenlet.readthedocs.io/en/latest/
|
||||||
|
|
||||||
|
|
||||||
|
Installing
|
||||||
|
----------
|
||||||
|
|
||||||
|
When using gevent, greenlet>=1.0 is required, otherwise context locals
|
||||||
|
such as ``request`` will not work as expected. When using PyPy,
|
||||||
|
PyPy>=7.3.7 is required.
|
||||||
|
|
||||||
|
Create a virtualenv, install your application, then install ``gevent``.
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
$ cd hello-app
|
||||||
|
$ python -m venv venv
|
||||||
|
$ . venv/bin/activate
|
||||||
|
$ pip install . # install your application
|
||||||
|
$ pip install gevent
|
||||||
|
|
||||||
|
|
||||||
|
Running
|
||||||
|
-------
|
||||||
|
|
||||||
|
To use gevent to serve your application, write a script that imports its
|
||||||
|
``WSGIServer``, as well as your app or app factory.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
:caption: ``wsgi.py``
|
||||||
|
|
||||||
|
from gevent.pywsgi import WSGIServer
|
||||||
|
from hello import create_app
|
||||||
|
|
||||||
|
app = create_app()
|
||||||
|
http_server = WSGIServer(("127.0.0.1", 8000), app)
|
||||||
|
http_server.serve_forever()
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
$ python wsgi.py
|
||||||
|
|
||||||
|
No output is shown when the server starts.
|
||||||
|
|
||||||
|
|
||||||
|
Binding Externally
|
||||||
|
------------------
|
||||||
|
|
||||||
|
gevent should not be run as root because it would cause your
|
||||||
|
application code to run as root, which is not secure. However, this
|
||||||
|
means it will not be possible to bind to port 80 or 443. Instead, a
|
||||||
|
reverse proxy such as :doc:`nginx` or :doc:`apache-httpd` should be used
|
||||||
|
in front of gevent.
|
||||||
|
|
||||||
|
You can bind to all external IPs on a non-privileged port by using
|
||||||
|
``0.0.0.0`` in the server arguments shown in the previous section. Don't
|
||||||
|
do this when using a reverse proxy setup, otherwise it will be possible
|
||||||
|
to bypass the proxy.
|
||||||
|
|
||||||
|
``0.0.0.0`` is not a valid address to navigate to, you'd use a specific
|
||||||
|
IP address in your browser.
|
||||||
130
docs/deploying/gunicorn.rst
Normal file
130
docs/deploying/gunicorn.rst
Normal file
|
|
@ -0,0 +1,130 @@
|
||||||
|
Gunicorn
|
||||||
|
========
|
||||||
|
|
||||||
|
`Gunicorn`_ is a pure Python WSGI server with simple configuration and
|
||||||
|
multiple worker implementations for performance tuning.
|
||||||
|
|
||||||
|
* It tends to integrate easily with hosting platforms.
|
||||||
|
* It does not support Windows (but does run on WSL).
|
||||||
|
* It is easy to install as it does not require additional dependencies
|
||||||
|
or compilation.
|
||||||
|
* It has built-in async worker support using gevent or eventlet.
|
||||||
|
|
||||||
|
This page outlines the basics of running Gunicorn. Be sure to read its
|
||||||
|
`documentation`_ and use ``gunicorn --help`` to understand what features
|
||||||
|
are available.
|
||||||
|
|
||||||
|
.. _Gunicorn: https://gunicorn.org/
|
||||||
|
.. _documentation: https://docs.gunicorn.org/
|
||||||
|
|
||||||
|
|
||||||
|
Installing
|
||||||
|
----------
|
||||||
|
|
||||||
|
Gunicorn is easy to install, as it does not require external
|
||||||
|
dependencies or compilation. It runs on Windows only under WSL.
|
||||||
|
|
||||||
|
Create a virtualenv, install your application, then install
|
||||||
|
``gunicorn``.
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
$ cd hello-app
|
||||||
|
$ python -m venv venv
|
||||||
|
$ . venv/bin/activate
|
||||||
|
$ pip install . # install your application
|
||||||
|
$ pip install gunicorn
|
||||||
|
|
||||||
|
|
||||||
|
Running
|
||||||
|
-------
|
||||||
|
|
||||||
|
The only required argument to Gunicorn tells it how to load your Flask
|
||||||
|
application. The syntax is ``{module_import}:{app_variable}``.
|
||||||
|
``module_import`` is the dotted import name to the module with your
|
||||||
|
application. ``app_variable`` is the variable with the application. It
|
||||||
|
can also be a function call (with any arguments) if you're using the
|
||||||
|
app factory pattern.
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
# equivalent to 'from hello import app'
|
||||||
|
$ gunicorn -w 4 'hello:app'
|
||||||
|
|
||||||
|
# equivalent to 'from hello import create_app; create_app()'
|
||||||
|
$ gunicorn -w 4 'hello:create_app()'
|
||||||
|
|
||||||
|
Starting gunicorn 20.1.0
|
||||||
|
Listening at: http://127.0.0.1:8000 (x)
|
||||||
|
Using worker: sync
|
||||||
|
Booting worker with pid: x
|
||||||
|
Booting worker with pid: x
|
||||||
|
Booting worker with pid: x
|
||||||
|
Booting worker with pid: x
|
||||||
|
|
||||||
|
The ``-w`` option specifies the number of processes to run; a starting
|
||||||
|
value could be ``CPU * 2``. The default is only 1 worker, which is
|
||||||
|
probably not what you want for the default worker type.
|
||||||
|
|
||||||
|
Logs for each request aren't shown by default, only worker info and
|
||||||
|
errors are shown. To show access logs on stdout, use the
|
||||||
|
``--access-logfile=-`` option.
|
||||||
|
|
||||||
|
|
||||||
|
Binding Externally
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Gunicorn should not be run as root because it would cause your
|
||||||
|
application code to run as root, which is not secure. However, this
|
||||||
|
means it will not be possible to bind to port 80 or 443. Instead, a
|
||||||
|
reverse proxy such as :doc:`nginx` or :doc:`apache-httpd` should be used
|
||||||
|
in front of Gunicorn.
|
||||||
|
|
||||||
|
You can bind to all external IPs on a non-privileged port using the
|
||||||
|
``-b 0.0.0.0`` option. Don't do this when using a reverse proxy setup,
|
||||||
|
otherwise it will be possible to bypass the proxy.
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
$ gunicorn -w 4 -b 0.0.0.0 'hello:create_app()'
|
||||||
|
Listening at: http://0.0.0.0:8000 (x)
|
||||||
|
|
||||||
|
``0.0.0.0`` is not a valid address to navigate to, you'd use a specific
|
||||||
|
IP address in your browser.
|
||||||
|
|
||||||
|
|
||||||
|
Async with gevent or eventlet
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
The default sync worker is appropriate for many use cases. If you need
|
||||||
|
asynchronous support, Gunicorn provides workers using either `gevent`_
|
||||||
|
or `eventlet`_. This is not the same as Python's ``async/await``, or the
|
||||||
|
ASGI server spec. You must actually use gevent/eventlet in your own code
|
||||||
|
to see any benefit to using the workers.
|
||||||
|
|
||||||
|
When using either gevent or eventlet, greenlet>=1.0 is required,
|
||||||
|
otherwise context locals such as ``request`` will not work as expected.
|
||||||
|
When using PyPy, PyPy>=7.3.7 is required.
|
||||||
|
|
||||||
|
To use gevent:
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
$ gunicorn -k gevent 'hello:create_app()'
|
||||||
|
Starting gunicorn 20.1.0
|
||||||
|
Listening at: http://127.0.0.1:8000 (x)
|
||||||
|
Using worker: gevent
|
||||||
|
Booting worker with pid: x
|
||||||
|
|
||||||
|
To use eventlet:
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
$ gunicorn -k eventlet 'hello:create_app()'
|
||||||
|
Starting gunicorn 20.1.0
|
||||||
|
Listening at: http://127.0.0.1:8000 (x)
|
||||||
|
Using worker: eventlet
|
||||||
|
Booting worker with pid: x
|
||||||
|
|
||||||
|
.. _gevent: https://www.gevent.org/
|
||||||
|
.. _eventlet: https://eventlet.net/
|
||||||
|
|
@ -1,35 +1,80 @@
|
||||||
Deployment Options
|
Deploying to Production
|
||||||
==================
|
=======================
|
||||||
|
|
||||||
While lightweight and easy to use, **Flask's built-in server is not suitable
|
After developing your application, you'll want to make it available
|
||||||
for production** as it doesn't scale well. Some of the options available for
|
publicly to other users. When you're developing locally, you're probably
|
||||||
properly running Flask in production are documented here.
|
using the built-in development server, debugger, and reloader. These
|
||||||
|
should not be used in production. Instead, you should use a dedicated
|
||||||
|
WSGI server or hosting platform, some of which will be described here.
|
||||||
|
|
||||||
If you want to deploy your Flask application to a WSGI server not listed here,
|
"Production" means "not development", which applies whether you're
|
||||||
look up the server documentation about how to use a WSGI app with it. Just
|
serving your application publicly to millions of users or privately /
|
||||||
remember that your :class:`Flask` application object is the actual WSGI
|
locally to a single user. **Do not use the development server when
|
||||||
application.
|
deploying to production. It is intended for use only during local
|
||||||
|
development. It is not designed to be particularly secure, stable, or
|
||||||
|
efficient.**
|
||||||
|
|
||||||
|
Self-Hosted Options
|
||||||
Hosted options
|
|
||||||
--------------
|
|
||||||
|
|
||||||
- `Deploying Flask on Heroku <https://devcenter.heroku.com/articles/getting-started-with-python>`_
|
|
||||||
- `Deploying Flask on Google App Engine <https://cloud.google.com/appengine/docs/standard/python3/runtime>`_
|
|
||||||
- `Deploying Flask on Google Cloud Run <https://cloud.google.com/run/docs/quickstarts/build-and-deploy/python>`_
|
|
||||||
- `Deploying Flask on AWS Elastic Beanstalk <https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/create-deploy-python-flask.html>`_
|
|
||||||
- `Deploying on Azure (IIS) <https://docs.microsoft.com/en-us/azure/app-service/containers/how-to-configure-python>`_
|
|
||||||
- `Deploying on PythonAnywhere <https://help.pythonanywhere.com/pages/Flask/>`_
|
|
||||||
|
|
||||||
Self-hosted options
|
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
.. toctree::
|
Flask is a WSGI *application*. A WSGI *server* is used to run the
|
||||||
:maxdepth: 2
|
application, converting incoming HTTP requests to the standard WSGI
|
||||||
|
environ, and converting outgoing WSGI responses to HTTP responses.
|
||||||
|
|
||||||
wsgi-standalone
|
The primary goal of these docs is to familiarize you with the concepts
|
||||||
uwsgi
|
involved in running a WSGI application using a production WSGI server
|
||||||
mod_wsgi
|
and HTTP server. There are many WSGI servers and HTTP servers, with many
|
||||||
fastcgi
|
configuration possibilities. The pages below discuss the most common
|
||||||
cgi
|
servers, and show the basics of running each one. The next section
|
||||||
asgi
|
discusses platforms that can manage this for you.
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 1
|
||||||
|
|
||||||
|
gunicorn
|
||||||
|
waitress
|
||||||
|
mod_wsgi
|
||||||
|
uwsgi
|
||||||
|
gevent
|
||||||
|
eventlet
|
||||||
|
asgi
|
||||||
|
|
||||||
|
WSGI servers have HTTP servers built-in. However, a dedicated HTTP
|
||||||
|
server may be safer, more efficient, or more capable. Putting an HTTP
|
||||||
|
server in front of the WSGI server is called a "reverse proxy."
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 1
|
||||||
|
|
||||||
|
proxy_fix
|
||||||
|
nginx
|
||||||
|
apache-httpd
|
||||||
|
|
||||||
|
This list is not exhaustive, and you should evaluate these and other
|
||||||
|
servers based on your application's needs. Different servers will have
|
||||||
|
different capabilities, configuration, and support.
|
||||||
|
|
||||||
|
|
||||||
|
Hosting Platforms
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
There are many services available for hosting web applications without
|
||||||
|
needing to maintain your own server, networking, domain, etc. Some
|
||||||
|
services may have a free tier up to a certain time or bandwidth. Many of
|
||||||
|
these services use one of the WSGI servers described above, or a similar
|
||||||
|
interface. The links below are for some of the most common platforms,
|
||||||
|
which have instructions for Flask, WSGI, or Python.
|
||||||
|
|
||||||
|
- `PythonAnywhere <https://help.pythonanywhere.com/pages/Flask/>`_
|
||||||
|
- `Heroku <https://devcenter.heroku.com/articles/getting-started-with-python>`_
|
||||||
|
- `Google App Engine <https://cloud.google.com/appengine/docs/standard/python3/building-app>`_
|
||||||
|
- `Google Cloud Run <https://cloud.google.com/run/docs/quickstarts/build-and-deploy/deploy-python-service>`_
|
||||||
|
- `AWS Elastic Beanstalk <https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/create-deploy-python-flask.html>`_
|
||||||
|
- `Microsoft Azure <https://docs.microsoft.com/en-us/azure/app-service/quickstart-python>`_
|
||||||
|
|
||||||
|
This list is not exhaustive, and you should evaluate these and other
|
||||||
|
services based on your application's needs. Different services will have
|
||||||
|
different capabilities, configuration, pricing, and support.
|
||||||
|
|
||||||
|
You'll probably need to :doc:`proxy_fix` when using most hosting
|
||||||
|
platforms.
|
||||||
|
|
|
||||||
|
|
@ -1,216 +1,105 @@
|
||||||
mod_wsgi (Apache)
|
mod_wsgi
|
||||||
=================
|
========
|
||||||
|
|
||||||
If you are using the `Apache`_ webserver, consider using `mod_wsgi`_.
|
`mod_wsgi`_ is a WSGI server integrated with the `Apache httpd`_ server.
|
||||||
|
The modern `mod_wsgi-express`_ command makes it easy to configure and
|
||||||
|
start the server without needing to write Apache httpd configuration.
|
||||||
|
|
||||||
.. admonition:: Watch Out
|
* Tightly integrated with Apache httpd.
|
||||||
|
* Supports Windows directly.
|
||||||
|
* Requires a compiler to install. Requires Apache installed separately
|
||||||
|
on Windows.
|
||||||
|
* Does not require a reverse proxy setup.
|
||||||
|
|
||||||
Please make sure in advance that any ``app.run()`` calls you might
|
This page outlines the basics of running mod_wsgi-express, not the more
|
||||||
have in your application file are inside an ``if __name__ ==
|
complex installation and configuration with httpd. Be sure to read the
|
||||||
'__main__':`` block or moved to a separate file. Just make sure it's
|
`mod_wsgi-express`_, `mod_wsgi`_, and `Apache httpd`_ documentation to
|
||||||
not called because this will always start a local WSGI server which
|
understand what features are available.
|
||||||
we do not want if we deploy that application to mod_wsgi.
|
|
||||||
|
|
||||||
.. _Apache: https://httpd.apache.org/
|
.. _mod_wsgi-express: https://pypi.org/project/mod-wsgi/
|
||||||
|
.. _mod_wsgi: https://modwsgi.readthedocs.io/
|
||||||
|
.. _Apache httpd: https://httpd.apache.org/
|
||||||
|
|
||||||
Installing `mod_wsgi`
|
|
||||||
---------------------
|
|
||||||
|
|
||||||
If you don't have `mod_wsgi` installed yet you have to either install it
|
Installing
|
||||||
using a package manager or compile it yourself. The mod_wsgi
|
----------
|
||||||
`installation instructions`_ cover source installations on UNIX systems.
|
|
||||||
|
|
||||||
If you are using Ubuntu/Debian you can apt-get it and activate it as
|
On Linux/Mac, the most straightforward way to install mod_wsgi is to
|
||||||
follows:
|
install the ``mod_wsgi-standalone`` package, which will compile an
|
||||||
|
up-to-date version of Apache httpd as well.
|
||||||
|
|
||||||
.. sourcecode:: text
|
Create a virtualenv, install your application, then install
|
||||||
|
``mod_wsgi-standalone``.
|
||||||
|
|
||||||
$ apt-get install libapache2-mod-wsgi-py3
|
.. code-block:: text
|
||||||
|
|
||||||
If you are using a yum based distribution (Fedora, OpenSUSE, etc..) you
|
$ cd hello-app
|
||||||
can install it as follows:
|
$ python -m venv venv
|
||||||
|
$ . venv/bin/activate
|
||||||
|
$ pip install . # install your application
|
||||||
|
$ pip install mod_wsgi-standalone
|
||||||
|
|
||||||
.. sourcecode:: text
|
If you want to use the system-installed version of Apache httpd
|
||||||
|
(required on Windows, optional but faster on Linux/Mac), install the
|
||||||
|
``mod_wsgi`` package instead. You will get an error if Apache and its
|
||||||
|
development headers are not available. How to install them depends on
|
||||||
|
what OS and package manager you use.
|
||||||
|
|
||||||
$ yum install mod_wsgi
|
.. code-block:: text
|
||||||
|
|
||||||
On FreeBSD install `mod_wsgi` by compiling the `www/mod_wsgi` port or by
|
$ pip install mod_wsgi
|
||||||
using pkg_add:
|
|
||||||
|
|
||||||
.. sourcecode:: text
|
|
||||||
|
|
||||||
$ pkg install ap24-py37-mod_wsgi
|
Running
|
||||||
|
-------
|
||||||
|
|
||||||
If you are using pkgsrc you can install `mod_wsgi` by compiling the
|
The only argument to ``mod_wsgi-express`` specifies a script containing
|
||||||
`www/ap2-wsgi` package.
|
your Flask application, which must be called ``application``. You can
|
||||||
|
write a small script to import your app with this name, or to create it
|
||||||
|
if using the app factory pattern.
|
||||||
|
|
||||||
If you encounter segfaulting child processes after the first apache
|
.. code-block:: python
|
||||||
reload you can safely ignore them. Just restart the server.
|
:caption: ``wsgi.py``
|
||||||
|
|
||||||
Creating a `.wsgi` file
|
from hello import app
|
||||||
-----------------------
|
|
||||||
|
|
||||||
To run your application you need a :file:`yourapplication.wsgi` file.
|
application = app
|
||||||
This file contains the code `mod_wsgi` is executing on startup
|
|
||||||
to get the application object. The object called `application`
|
|
||||||
in that file is then used as application.
|
|
||||||
|
|
||||||
For most applications the following file should be sufficient::
|
.. code-block:: python
|
||||||
|
:caption: ``wsgi.py``
|
||||||
|
|
||||||
from yourapplication import app as application
|
from hello import create_app
|
||||||
|
|
||||||
If a factory function is used in a :file:`__init__.py` file, then the function should be imported::
|
|
||||||
|
|
||||||
from yourapplication import create_app
|
|
||||||
application = create_app()
|
application = create_app()
|
||||||
|
|
||||||
If you don't have a factory function for application creation but a singleton
|
Now run the ``mod_wsgi-express start-server`` command.
|
||||||
instance you can directly import that one as `application`.
|
|
||||||
|
|
||||||
Store that file somewhere that you will find it again (e.g.:
|
.. code-block:: text
|
||||||
:file:`/var/www/yourapplication`) and make sure that `yourapplication` and all
|
|
||||||
the libraries that are in use are on the python load path. If you don't
|
|
||||||
want to install it system wide consider using a `virtual python`_
|
|
||||||
instance. Keep in mind that you will have to actually install your
|
|
||||||
application into the virtualenv as well. Alternatively there is the
|
|
||||||
option to just patch the path in the ``.wsgi`` file before the import::
|
|
||||||
|
|
||||||
import sys
|
$ mod_wsgi-express start-server wsgi.py --processes 4
|
||||||
sys.path.insert(0, '/path/to/the/application')
|
|
||||||
|
|
||||||
Configuring Apache
|
The ``--processes`` option specifies the number of worker processes to
|
||||||
|
run; a starting value could be ``CPU * 2``.
|
||||||
|
|
||||||
|
Logs for each request aren't show in the terminal. If an error occurs,
|
||||||
|
its information is written to the error log file shown when starting the
|
||||||
|
server.
|
||||||
|
|
||||||
|
|
||||||
|
Binding Externally
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
The last thing you have to do is to create an Apache configuration file
|
Unlike the other WSGI servers in these docs, mod_wsgi can be run as
|
||||||
for your application. In this example we are telling `mod_wsgi` to
|
root to bind to privileged ports like 80 and 443. However, it must be
|
||||||
execute the application under a different user for security reasons:
|
configured to drop permissions to a different user and group for the
|
||||||
|
worker processes.
|
||||||
|
|
||||||
.. sourcecode:: apache
|
For example, if you created a ``hello`` user and group, you should
|
||||||
|
install your virtualenv and application as that user, then tell
|
||||||
|
mod_wsgi to drop to that user after starting.
|
||||||
|
|
||||||
<VirtualHost *>
|
.. code-block:: text
|
||||||
ServerName example.com
|
|
||||||
|
|
||||||
WSGIDaemonProcess yourapplication user=user1 group=group1 threads=5
|
$ sudo /home/hello/venv/bin/mod_wsgi-express start-server \
|
||||||
WSGIScriptAlias / /var/www/yourapplication/yourapplication.wsgi
|
/home/hello/wsgi.py \
|
||||||
|
--user hello --group hello --port 80 --processes 4
|
||||||
<Directory /var/www/yourapplication>
|
|
||||||
WSGIProcessGroup yourapplication
|
|
||||||
WSGIApplicationGroup %{GLOBAL}
|
|
||||||
Order deny,allow
|
|
||||||
Allow from all
|
|
||||||
</Directory>
|
|
||||||
</VirtualHost>
|
|
||||||
|
|
||||||
Note: WSGIDaemonProcess isn't implemented in Windows and Apache will
|
|
||||||
refuse to run with the above configuration. On a Windows system, eliminate those lines:
|
|
||||||
|
|
||||||
.. sourcecode:: apache
|
|
||||||
|
|
||||||
<VirtualHost *>
|
|
||||||
ServerName example.com
|
|
||||||
WSGIScriptAlias / C:\yourdir\yourapp.wsgi
|
|
||||||
<Directory C:\yourdir>
|
|
||||||
Order deny,allow
|
|
||||||
Allow from all
|
|
||||||
</Directory>
|
|
||||||
</VirtualHost>
|
|
||||||
|
|
||||||
Note: There have been some changes in access control configuration
|
|
||||||
for `Apache 2.4`_.
|
|
||||||
|
|
||||||
.. _Apache 2.4: https://httpd.apache.org/docs/trunk/upgrading.html
|
|
||||||
|
|
||||||
Most notably, the syntax for directory permissions has changed from httpd 2.2
|
|
||||||
|
|
||||||
.. sourcecode:: apache
|
|
||||||
|
|
||||||
Order allow,deny
|
|
||||||
Allow from all
|
|
||||||
|
|
||||||
to httpd 2.4 syntax
|
|
||||||
|
|
||||||
.. sourcecode:: apache
|
|
||||||
|
|
||||||
Require all granted
|
|
||||||
|
|
||||||
|
|
||||||
For more information consult the `mod_wsgi documentation`_.
|
|
||||||
|
|
||||||
.. _mod_wsgi: https://github.com/GrahamDumpleton/mod_wsgi
|
|
||||||
.. _installation instructions: https://modwsgi.readthedocs.io/en/develop/installation.html
|
|
||||||
.. _virtual python: https://pypi.org/project/virtualenv/
|
|
||||||
.. _mod_wsgi documentation: https://modwsgi.readthedocs.io/en/develop/index.html
|
|
||||||
|
|
||||||
Troubleshooting
|
|
||||||
---------------
|
|
||||||
|
|
||||||
If your application does not run, follow this guide to troubleshoot:
|
|
||||||
|
|
||||||
**Problem:** application does not run, errorlog shows SystemExit ignored
|
|
||||||
You have an ``app.run()`` call in your application file that is not
|
|
||||||
guarded by an ``if __name__ == '__main__':`` condition. Either
|
|
||||||
remove that :meth:`~flask.Flask.run` call from the file and move it
|
|
||||||
into a separate :file:`run.py` file or put it into such an if block.
|
|
||||||
|
|
||||||
**Problem:** application gives permission errors
|
|
||||||
Probably caused by your application running as the wrong user. Make
|
|
||||||
sure the folders the application needs access to have the proper
|
|
||||||
privileges set and the application runs as the correct user
|
|
||||||
(``user`` and ``group`` parameter to the `WSGIDaemonProcess`
|
|
||||||
directive)
|
|
||||||
|
|
||||||
**Problem:** application dies with an error on print
|
|
||||||
Keep in mind that mod_wsgi disallows doing anything with
|
|
||||||
:data:`sys.stdout` and :data:`sys.stderr`. You can disable this
|
|
||||||
protection from the config by setting the `WSGIRestrictStdout` to
|
|
||||||
``off``:
|
|
||||||
|
|
||||||
.. sourcecode:: apache
|
|
||||||
|
|
||||||
WSGIRestrictStdout Off
|
|
||||||
|
|
||||||
Alternatively you can also replace the standard out in the .wsgi file
|
|
||||||
with a different stream::
|
|
||||||
|
|
||||||
import sys
|
|
||||||
sys.stdout = sys.stderr
|
|
||||||
|
|
||||||
**Problem:** accessing resources gives IO errors
|
|
||||||
Your application probably is a single .py file you symlinked into
|
|
||||||
the site-packages folder. Please be aware that this does not work,
|
|
||||||
instead you either have to put the folder into the pythonpath the
|
|
||||||
file is stored in, or convert your application into a package.
|
|
||||||
|
|
||||||
The reason for this is that for non-installed packages, the module
|
|
||||||
filename is used to locate the resources and for symlinks the wrong
|
|
||||||
filename is picked up.
|
|
||||||
|
|
||||||
Support for Automatic Reloading
|
|
||||||
-------------------------------
|
|
||||||
|
|
||||||
To help deployment tools you can activate support for automatic
|
|
||||||
reloading. Whenever something changes the ``.wsgi`` file, `mod_wsgi` will
|
|
||||||
reload all the daemon processes for us.
|
|
||||||
|
|
||||||
For that, just add the following directive to your `Directory` section:
|
|
||||||
|
|
||||||
.. sourcecode:: apache
|
|
||||||
|
|
||||||
WSGIScriptReloading On
|
|
||||||
|
|
||||||
Working with Virtual Environments
|
|
||||||
---------------------------------
|
|
||||||
|
|
||||||
Virtual environments have the advantage that they never install the
|
|
||||||
required dependencies system wide so you have a better control over what
|
|
||||||
is used where. If you want to use a virtual environment with mod_wsgi
|
|
||||||
you have to modify your ``.wsgi`` file slightly.
|
|
||||||
|
|
||||||
Add the following lines to the top of your ``.wsgi`` file::
|
|
||||||
|
|
||||||
activate_this = '/path/to/env/bin/activate_this.py'
|
|
||||||
with open(activate_this) as file_:
|
|
||||||
exec(file_.read(), dict(__file__=activate_this))
|
|
||||||
|
|
||||||
This sets up the load paths according to the settings of the virtual
|
|
||||||
environment. Keep in mind that the path has to be absolute.
|
|
||||||
|
|
|
||||||
69
docs/deploying/nginx.rst
Normal file
69
docs/deploying/nginx.rst
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
nginx
|
||||||
|
=====
|
||||||
|
|
||||||
|
`nginx`_ is a fast, production level HTTP server. When serving your
|
||||||
|
application with one of the WSGI servers listed in :doc:`index`, it is
|
||||||
|
often good or necessary to put a dedicated HTTP server in front of it.
|
||||||
|
This "reverse proxy" can handle incoming requests, TLS, and other
|
||||||
|
security and performance concerns better than the WSGI server.
|
||||||
|
|
||||||
|
Nginx can be installed using your system package manager, or a pre-built
|
||||||
|
executable for Windows. Installing and running Nginx itself is outside
|
||||||
|
the scope of this doc. This page outlines the basics of configuring
|
||||||
|
Nginx to proxy your application. Be sure to read its documentation to
|
||||||
|
understand what features are available.
|
||||||
|
|
||||||
|
.. _nginx: https://nginx.org/
|
||||||
|
|
||||||
|
|
||||||
|
Domain Name
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Acquiring and configuring a domain name is outside the scope of this
|
||||||
|
doc. In general, you will buy a domain name from a registrar, pay for
|
||||||
|
server space with a hosting provider, and then point your registrar
|
||||||
|
at the hosting provider's name servers.
|
||||||
|
|
||||||
|
To simulate this, you can also edit your ``hosts`` file, located at
|
||||||
|
``/etc/hosts`` on Linux. Add a line that associates a name with the
|
||||||
|
local IP.
|
||||||
|
|
||||||
|
Modern Linux systems may be configured to treat any domain name that
|
||||||
|
ends with ``.localhost`` like this without adding it to the ``hosts``
|
||||||
|
file.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
:caption: ``/etc/hosts``
|
||||||
|
|
||||||
|
127.0.0.1 hello.localhost
|
||||||
|
|
||||||
|
|
||||||
|
Configuration
|
||||||
|
-------------
|
||||||
|
|
||||||
|
The nginx configuration is located at ``/etc/nginx/nginx.conf`` on
|
||||||
|
Linux. It may be different depending on your operating system. Check the
|
||||||
|
docs and look for ``nginx.conf``.
|
||||||
|
|
||||||
|
Remove or comment out any existing ``server`` section. Add a ``server``
|
||||||
|
section and use the ``proxy_pass`` directive to point to the address the
|
||||||
|
WSGI server is listening on. We'll assume the WSGI server is listening
|
||||||
|
locally at ``http://127.0.0.1:8000``.
|
||||||
|
|
||||||
|
.. code-block:: nginx
|
||||||
|
:caption: ``/etc/nginx.conf``
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://127.0.0.1:8000/;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_set_header X-Forwarded-Host $host;
|
||||||
|
proxy_set_header X-Forwarded-Prefix /;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Then :doc:`proxy_fix` so that your application uses these headers.
|
||||||
33
docs/deploying/proxy_fix.rst
Normal file
33
docs/deploying/proxy_fix.rst
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
Tell Flask it is Behind a Proxy
|
||||||
|
===============================
|
||||||
|
|
||||||
|
When using a reverse proxy, or many Python hosting platforms, the proxy
|
||||||
|
will intercept and forward all external requests to the local WSGI
|
||||||
|
server.
|
||||||
|
|
||||||
|
From the WSGI server and Flask application's perspectives, requests are
|
||||||
|
now coming from the HTTP server to the local address, rather than from
|
||||||
|
the remote address to the external server address.
|
||||||
|
|
||||||
|
HTTP servers should set ``X-Forwarded-`` headers to pass on the real
|
||||||
|
values to the application. The application can then be told to trust and
|
||||||
|
use those values by wrapping it with the
|
||||||
|
:doc:`werkzeug:middleware/proxy_fix` middleware provided by Werkzeug.
|
||||||
|
|
||||||
|
This middleware should only be used if the application is actually
|
||||||
|
behind a proxy, and should be configured with the number of proxies that
|
||||||
|
are chained in front of it. Not all proxies set all the headers. Since
|
||||||
|
incoming headers can be faked, you must set how many proxies are setting
|
||||||
|
each header so the middleware knows what to trust.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from werkzeug.middleware.proxy_fix import ProxyFix
|
||||||
|
|
||||||
|
app.wsgi_app = ProxyFix(
|
||||||
|
app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_prefix=1
|
||||||
|
)
|
||||||
|
|
||||||
|
Remember, only apply this middleware if you are behind a proxy, and set
|
||||||
|
the correct number of proxies that set each header. It can be a security
|
||||||
|
issue if you get this configuration wrong.
|
||||||
|
|
@ -1,71 +1,145 @@
|
||||||
uWSGI
|
uWSGI
|
||||||
=====
|
=====
|
||||||
|
|
||||||
uWSGI is a deployment option on servers like `nginx`_, `lighttpd`_, and
|
`uWSGI`_ is a fast, compiled server suite with extensive configuration
|
||||||
`cherokee`_; see :doc:`fastcgi` and :doc:`wsgi-standalone` for other options.
|
and capabilities beyond a basic server.
|
||||||
To use your WSGI application with uWSGI protocol you will need a uWSGI server
|
|
||||||
first. uWSGI is both a protocol and an application server; the application
|
|
||||||
server can serve uWSGI, FastCGI, and HTTP protocols.
|
|
||||||
|
|
||||||
The most popular uWSGI server is `uwsgi`_, which we will use for this
|
* It can be very performant due to being a compiled program.
|
||||||
guide. Make sure to have it installed to follow along.
|
* It is complex to configure beyond the basic application, and has so
|
||||||
|
many options that it can be difficult for beginners to understand.
|
||||||
|
* It does not support Windows (but does run on WSL).
|
||||||
|
* It requires a compiler to install in some cases.
|
||||||
|
|
||||||
.. admonition:: Watch Out
|
This page outlines the basics of running uWSGI. Be sure to read its
|
||||||
|
documentation to understand what features are available.
|
||||||
|
|
||||||
Please make sure in advance that any ``app.run()`` calls you might
|
.. _uWSGI: https://uwsgi-docs.readthedocs.io/en/latest/
|
||||||
have in your application file are inside an ``if __name__ ==
|
|
||||||
'__main__':`` block or moved to a separate file. Just make sure it's
|
|
||||||
not called because this will always start a local WSGI server which
|
|
||||||
we do not want if we deploy that application to uWSGI.
|
|
||||||
|
|
||||||
Starting your app with uwsgi
|
|
||||||
----------------------------
|
|
||||||
|
|
||||||
`uwsgi` is designed to operate on WSGI callables found in python modules.
|
Installing
|
||||||
|
----------
|
||||||
|
|
||||||
Given a flask application in myapp.py, use the following command:
|
uWSGI has multiple ways to install it. The most straightforward is to
|
||||||
|
install the ``pyuwsgi`` package, which provides precompiled wheels for
|
||||||
|
common platforms. However, it does not provide SSL support, which can be
|
||||||
|
provided with a reverse proxy instead.
|
||||||
|
|
||||||
.. sourcecode:: text
|
Create a virtualenv, install your application, then install ``pyuwsgi``.
|
||||||
|
|
||||||
$ uwsgi -s /tmp/yourapplication.sock --manage-script-name --mount /yourapplication=myapp:app
|
.. code-block:: text
|
||||||
|
|
||||||
The ``--manage-script-name`` will move the handling of ``SCRIPT_NAME``
|
$ cd hello-app
|
||||||
to uwsgi, since it is smarter about that.
|
$ python -m venv venv
|
||||||
It is used together with the ``--mount`` directive which will make
|
$ . venv/bin/activate
|
||||||
requests to ``/yourapplication`` be directed to ``myapp:app``.
|
$ pip install . # install your application
|
||||||
If your application is accessible at root level, you can use a
|
$ pip install pyuwsgi
|
||||||
single ``/`` instead of ``/yourapplication``. ``myapp`` refers to the name of
|
|
||||||
the file of your flask application (without extension) or the module which
|
|
||||||
provides ``app``. ``app`` is the callable inside of your application (usually
|
|
||||||
the line reads ``app = Flask(__name__)``).
|
|
||||||
|
|
||||||
If you want to deploy your flask application inside of a virtual environment,
|
If you have a compiler available, you can install the ``uwsgi`` package
|
||||||
you need to also add ``--virtualenv /path/to/virtual/environment``. You might
|
instead. Or install the ``pyuwsgi`` package from sdist instead of wheel.
|
||||||
also need to add ``--plugin python`` or ``--plugin python3`` depending on which
|
Either method will include SSL support.
|
||||||
python version you use for your project.
|
|
||||||
|
|
||||||
Configuring nginx
|
.. code-block:: text
|
||||||
|
|
||||||
|
$ pip install uwsgi
|
||||||
|
|
||||||
|
# or
|
||||||
|
$ pip install --no-binary pyuwsgi pyuwsgi
|
||||||
|
|
||||||
|
|
||||||
|
Running
|
||||||
|
-------
|
||||||
|
|
||||||
|
The most basic way to run uWSGI is to tell it to start an HTTP server
|
||||||
|
and import your application.
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
$ uwsgi --http 127.0.0.1:8000 --master -p 4 -w hello:app
|
||||||
|
|
||||||
|
*** Starting uWSGI 2.0.20 (64bit) on [x] ***
|
||||||
|
*** Operational MODE: preforking ***
|
||||||
|
mounting hello:app on /
|
||||||
|
spawned uWSGI master process (pid: x)
|
||||||
|
spawned uWSGI worker 1 (pid: x, cores: 1)
|
||||||
|
spawned uWSGI worker 2 (pid: x, cores: 1)
|
||||||
|
spawned uWSGI worker 3 (pid: x, cores: 1)
|
||||||
|
spawned uWSGI worker 4 (pid: x, cores: 1)
|
||||||
|
spawned uWSGI http 1 (pid: x)
|
||||||
|
|
||||||
|
If you're using the app factory pattern, you'll need to create a small
|
||||||
|
Python file to create the app, then point uWSGI at that.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
:caption: ``wsgi.py``
|
||||||
|
|
||||||
|
from hello import create_app
|
||||||
|
|
||||||
|
app = create_app()
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
$ uwsgi --http 127.0.0.1:8000 --master -p 4 -w wsgi:app
|
||||||
|
|
||||||
|
The ``--http`` option starts an HTTP server at 127.0.0.1 port 8000. The
|
||||||
|
``--master`` option specifies the standard worker manager. The ``-p``
|
||||||
|
option starts 4 worker processes; a starting value could be ``CPU * 2``.
|
||||||
|
The ``-w`` option tells uWSGI how to import your application
|
||||||
|
|
||||||
|
|
||||||
|
Binding Externally
|
||||||
|
------------------
|
||||||
|
|
||||||
|
uWSGI should not be run as root with the configuration shown in this doc
|
||||||
|
because it would cause your application code to run as root, which is
|
||||||
|
not secure. However, this means it will not be possible to bind to port
|
||||||
|
80 or 443. Instead, a reverse proxy such as :doc:`nginx` or
|
||||||
|
:doc:`apache-httpd` should be used in front of uWSGI. It is possible to
|
||||||
|
run uWSGI as root securely, but that is beyond the scope of this doc.
|
||||||
|
|
||||||
|
uWSGI has optimized integration with `Nginx uWSGI`_ and
|
||||||
|
`Apache mod_proxy_uwsgi`_, and possibly other servers, instead of using
|
||||||
|
a standard HTTP proxy. That configuration is beyond the scope of this
|
||||||
|
doc, see the links for more information.
|
||||||
|
|
||||||
|
.. _Nginx uWSGI: https://uwsgi-docs.readthedocs.io/en/latest/Nginx.html
|
||||||
|
.. _Apache mod_proxy_uwsgi: https://uwsgi-docs.readthedocs.io/en/latest/Apache.html#mod-proxy-uwsgi
|
||||||
|
|
||||||
|
You can bind to all external IPs on a non-privileged port using the
|
||||||
|
``--http 0.0.0.0:8000`` option. Don't do this when using a reverse proxy
|
||||||
|
setup, otherwise it will be possible to bypass the proxy.
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
$ uwsgi --http 0.0.0.0:8000 --master -p 4 -w wsgi:app
|
||||||
|
|
||||||
|
``0.0.0.0`` is not a valid address to navigate to, you'd use a specific
|
||||||
|
IP address in your browser.
|
||||||
|
|
||||||
|
|
||||||
|
Async with gevent
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
A basic flask nginx configuration looks like this::
|
The default sync worker is appropriate for many use cases. If you need
|
||||||
|
asynchronous support, uWSGI provides a `gevent`_ worker. This is not the
|
||||||
|
same as Python's ``async/await``, or the ASGI server spec. You must
|
||||||
|
actually use gevent in your own code to see any benefit to using the
|
||||||
|
worker.
|
||||||
|
|
||||||
location = /yourapplication { rewrite ^ /yourapplication/; }
|
When using gevent, greenlet>=1.0 is required, otherwise context locals
|
||||||
location /yourapplication { try_files $uri @yourapplication; }
|
such as ``request`` will not work as expected. When using PyPy,
|
||||||
location @yourapplication {
|
PyPy>=7.3.7 is required.
|
||||||
include uwsgi_params;
|
|
||||||
uwsgi_pass unix:/tmp/yourapplication.sock;
|
|
||||||
}
|
|
||||||
|
|
||||||
This configuration binds the application to ``/yourapplication``. If you want
|
.. code-block:: text
|
||||||
to have it in the URL root its a bit simpler::
|
|
||||||
|
|
||||||
location / { try_files $uri @yourapplication; }
|
$ uwsgi --http 127.0.0.1:8000 --master --gevent 100 -w wsgi:app
|
||||||
location @yourapplication {
|
|
||||||
include uwsgi_params;
|
|
||||||
uwsgi_pass unix:/tmp/yourapplication.sock;
|
|
||||||
}
|
|
||||||
|
|
||||||
.. _nginx: https://nginx.org/
|
*** Starting uWSGI 2.0.20 (64bit) on [x] ***
|
||||||
.. _lighttpd: https://www.lighttpd.net/
|
*** Operational MODE: async ***
|
||||||
.. _cherokee: https://cherokee-project.com/
|
mounting hello:app on /
|
||||||
.. _uwsgi: https://uwsgi-docs.readthedocs.io/en/latest/
|
spawned uWSGI master process (pid: x)
|
||||||
|
spawned uWSGI worker 1 (pid: x, cores: 100)
|
||||||
|
spawned uWSGI http 1 (pid: x)
|
||||||
|
*** running gevent loop engine [addr:x] ***
|
||||||
|
|
||||||
|
|
||||||
|
.. _gevent: https://www.gevent.org/
|
||||||
|
|
|
||||||
75
docs/deploying/waitress.rst
Normal file
75
docs/deploying/waitress.rst
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
Waitress
|
||||||
|
========
|
||||||
|
|
||||||
|
`Waitress`_ is a pure Python WSGI server.
|
||||||
|
|
||||||
|
* It is easy to configure.
|
||||||
|
* It supports Windows directly.
|
||||||
|
* It is easy to install as it does not require additional dependencies
|
||||||
|
or compilation.
|
||||||
|
* It does not support streaming requests, full request data is always
|
||||||
|
buffered.
|
||||||
|
* It uses a single process with multiple thread workers.
|
||||||
|
|
||||||
|
This page outlines the basics of running Waitress. Be sure to read its
|
||||||
|
documentation and ``waitress-serve --help`` to understand what features
|
||||||
|
are available.
|
||||||
|
|
||||||
|
.. _Waitress: https://docs.pylonsproject.org/projects/waitress/
|
||||||
|
|
||||||
|
|
||||||
|
Installing
|
||||||
|
----------
|
||||||
|
|
||||||
|
Create a virtualenv, install your application, then install
|
||||||
|
``waitress``.
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
$ cd hello-app
|
||||||
|
$ python -m venv venv
|
||||||
|
$ . venv/bin/activate
|
||||||
|
$ pip install . # install your application
|
||||||
|
$ pip install waitress
|
||||||
|
|
||||||
|
|
||||||
|
Running
|
||||||
|
-------
|
||||||
|
|
||||||
|
The only required argument to ``waitress-serve`` tells it how to load
|
||||||
|
your Flask application. The syntax is ``{module}:{app}``. ``module`` is
|
||||||
|
the dotted import name to the module with your application. ``app`` is
|
||||||
|
the variable with the application. If you're using the app factory
|
||||||
|
pattern, use ``--call {module}:{factory}`` instead.
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
# equivalent to 'from hello import app'
|
||||||
|
$ waitress-serve hello:app --host 127.0.0.1
|
||||||
|
|
||||||
|
# equivalent to 'from hello import create_app; create_app()'
|
||||||
|
$ waitress-serve --call hello:create_app --host 127.0.0.1
|
||||||
|
|
||||||
|
Serving on http://127.0.0.1:8080
|
||||||
|
|
||||||
|
The ``--host`` option binds the server to local ``127.0.0.1`` only.
|
||||||
|
|
||||||
|
Logs for each request aren't shown, only errors are shown. Logging can
|
||||||
|
be configured through the Python interface instead of the command line.
|
||||||
|
|
||||||
|
|
||||||
|
Binding Externally
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Waitress should not be run as root because it would cause your
|
||||||
|
application code to run as root, which is not secure. However, this
|
||||||
|
means it will not be possible to bind to port 80 or 443. Instead, a
|
||||||
|
reverse proxy such as :doc:`nginx` or :doc:`apache-httpd` should be used
|
||||||
|
in front of Waitress.
|
||||||
|
|
||||||
|
You can bind to all external IPs on a non-privileged port by not
|
||||||
|
specifying the ``--host`` option. Don't do this when using a revers
|
||||||
|
proxy setup, otherwise it will be possible to bypass the proxy.
|
||||||
|
|
||||||
|
``0.0.0.0`` is not a valid address to navigate to, you'd use a specific
|
||||||
|
IP address in your browser.
|
||||||
|
|
@ -1,262 +0,0 @@
|
||||||
Standalone WSGI Servers
|
|
||||||
=======================
|
|
||||||
|
|
||||||
Most WSGI servers also provide HTTP servers, so they can run a WSGI
|
|
||||||
application and make it available externally.
|
|
||||||
|
|
||||||
It may still be a good idea to run the server behind a dedicated HTTP
|
|
||||||
server such as Apache or Nginx. See :ref:`deploying-proxy-setups` if you
|
|
||||||
run into issues with that.
|
|
||||||
|
|
||||||
|
|
||||||
Gunicorn
|
|
||||||
--------
|
|
||||||
|
|
||||||
`Gunicorn`_ is a WSGI and HTTP server for UNIX. To run a Flask
|
|
||||||
application, tell Gunicorn how to import your Flask app object.
|
|
||||||
|
|
||||||
.. code-block:: text
|
|
||||||
|
|
||||||
$ gunicorn -w 4 -b 0.0.0.0:5000 your_project:app
|
|
||||||
|
|
||||||
The ``-w 4`` option uses 4 workers to handle 4 requests at once. The
|
|
||||||
``-b 0.0.0.0:5000`` serves the application on all interfaces on port
|
|
||||||
5000.
|
|
||||||
|
|
||||||
Gunicorn provides many options for configuring the server, either
|
|
||||||
through a configuration file or with command line options. Use
|
|
||||||
``gunicorn --help`` or see the docs for more information.
|
|
||||||
|
|
||||||
The command expects the name of your module or package to import and
|
|
||||||
the application instance within the module. If you use the application
|
|
||||||
factory pattern, you can pass a call to that.
|
|
||||||
|
|
||||||
.. code-block:: text
|
|
||||||
|
|
||||||
$ gunicorn -w 4 -b 0.0.0.0:5000 "myproject:create_app()"
|
|
||||||
|
|
||||||
|
|
||||||
Async with Gevent or Eventlet
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
The default sync worker is appropriate for many use cases. If you need
|
|
||||||
asynchronous support, Gunicorn provides workers using either `gevent`_
|
|
||||||
or `eventlet`_. This is not the same as Python's ``async/await``, or the
|
|
||||||
ASGI server spec.
|
|
||||||
|
|
||||||
When using either gevent or eventlet, greenlet>=1.0 is required,
|
|
||||||
otherwise context locals such as ``request`` will not work as expected.
|
|
||||||
When using PyPy, PyPy>=7.3.7 is required.
|
|
||||||
|
|
||||||
To use gevent:
|
|
||||||
|
|
||||||
.. code-block:: text
|
|
||||||
|
|
||||||
$ gunicorn -k gevent -b 0.0.0.0:5000 your_project:app
|
|
||||||
|
|
||||||
To use eventlet:
|
|
||||||
|
|
||||||
.. code-block:: text
|
|
||||||
|
|
||||||
$ gunicorn -k eventlet -b 0.0.0.0:5000 your_project:app
|
|
||||||
|
|
||||||
|
|
||||||
.. _Gunicorn: https://gunicorn.org/
|
|
||||||
.. _gevent: http://www.gevent.org/
|
|
||||||
.. _eventlet: https://eventlet.net/
|
|
||||||
.. _greenlet: https://greenlet.readthedocs.io/en/latest/
|
|
||||||
|
|
||||||
|
|
||||||
uWSGI
|
|
||||||
-----
|
|
||||||
|
|
||||||
`uWSGI`_ is a fast application server written in C. It is very
|
|
||||||
configurable, which makes it more complicated to setup than Gunicorn.
|
|
||||||
It also provides many other utilities for writing robust web
|
|
||||||
applications. To run a Flask application, tell uWSGI how to import
|
|
||||||
your Flask app object.
|
|
||||||
|
|
||||||
.. code-block:: text
|
|
||||||
|
|
||||||
$ uwsgi --master -p 4 --http 0.0.0.0:5000 -w your_project:app
|
|
||||||
|
|
||||||
The ``-p 4`` option uses 4 workers to handle 4 requests at once. The
|
|
||||||
``--http 0.0.0.0:5000`` serves the application on all interfaces on port
|
|
||||||
5000.
|
|
||||||
|
|
||||||
uWSGI has optimized integration with Nginx and Apache instead of using
|
|
||||||
a standard HTTP proxy. See :doc:`configuring uWSGI and Nginx <uwsgi>`.
|
|
||||||
|
|
||||||
|
|
||||||
Async with Gevent
|
|
||||||
~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
The default sync worker is appropriate for many use cases. If you need
|
|
||||||
asynchronous support, uWSGI provides workers using `gevent`_. It also
|
|
||||||
supports other async modes, see the docs for more information. This is
|
|
||||||
not the same as Python's ``async/await``, or the ASGI server spec.
|
|
||||||
|
|
||||||
When using gevent, greenlet>=1.0 is required, otherwise context locals
|
|
||||||
such as ``request`` will not work as expected. When using PyPy,
|
|
||||||
PyPy>=7.3.7 is required.
|
|
||||||
|
|
||||||
.. code-block:: text
|
|
||||||
|
|
||||||
$ uwsgi --master --gevent 100 --http 0.0.0.0:5000 -w your_project:app
|
|
||||||
|
|
||||||
.. _uWSGI: https://uwsgi-docs.readthedocs.io/en/latest/
|
|
||||||
|
|
||||||
|
|
||||||
Gevent
|
|
||||||
------
|
|
||||||
|
|
||||||
Prefer using `Gunicorn`_ with Gevent workers rather than using Gevent
|
|
||||||
directly. Gunicorn provides a much more configurable and
|
|
||||||
production-tested server. See the section on Gunicorn above.
|
|
||||||
|
|
||||||
`Gevent`_ allows writing asynchronous, coroutine-based code that looks
|
|
||||||
like standard synchronous Python. It uses `greenlet`_ to enable task
|
|
||||||
switching without writing ``async/await`` or using ``asyncio``.
|
|
||||||
|
|
||||||
It provides a WSGI server that can handle many connections at once
|
|
||||||
instead of one per worker process.
|
|
||||||
|
|
||||||
`Eventlet`_, described below, is another library that does the same
|
|
||||||
thing. Certain dependencies you have, or other consideration, may affect
|
|
||||||
which of the two you choose to use
|
|
||||||
|
|
||||||
To use gevent to serve your application, import its ``WSGIServer`` and
|
|
||||||
use it to run your ``app``.
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
from gevent.pywsgi import WSGIServer
|
|
||||||
from your_project import app
|
|
||||||
|
|
||||||
http_server = WSGIServer(("", 5000), app)
|
|
||||||
http_server.serve_forever()
|
|
||||||
|
|
||||||
|
|
||||||
Eventlet
|
|
||||||
--------
|
|
||||||
|
|
||||||
Prefer using `Gunicorn`_ with Eventlet workers rather than using
|
|
||||||
Eventlet directly. Gunicorn provides a much more configurable and
|
|
||||||
production-tested server. See the section on Gunicorn above.
|
|
||||||
|
|
||||||
`Eventlet`_ allows writing asynchronous, coroutine-based code that looks
|
|
||||||
like standard synchronous Python. It uses `greenlet`_ to enable task
|
|
||||||
switching without writing ``async/await`` or using ``asyncio``.
|
|
||||||
|
|
||||||
It provides a WSGI server that can handle many connections at once
|
|
||||||
instead of one per worker process.
|
|
||||||
|
|
||||||
`Gevent`_, described above, is another library that does the same
|
|
||||||
thing. Certain dependencies you have, or other consideration, may affect
|
|
||||||
which of the two you choose to use
|
|
||||||
|
|
||||||
To use eventlet to serve your application, import its ``wsgi.server``
|
|
||||||
and use it to run your ``app``.
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
import eventlet
|
|
||||||
from eventlet import wsgi
|
|
||||||
from your_project import app
|
|
||||||
|
|
||||||
wsgi.server(eventlet.listen(("", 5000), app)
|
|
||||||
|
|
||||||
|
|
||||||
Twisted Web
|
|
||||||
-----------
|
|
||||||
|
|
||||||
`Twisted Web`_ is the web server shipped with `Twisted`_, a mature,
|
|
||||||
non-blocking event-driven networking library. Twisted Web comes with a
|
|
||||||
standard WSGI container which can be controlled from the command line using
|
|
||||||
the ``twistd`` utility:
|
|
||||||
|
|
||||||
.. code-block:: text
|
|
||||||
|
|
||||||
$ twistd web --wsgi myproject.app
|
|
||||||
|
|
||||||
This example will run a Flask application called ``app`` from a module named
|
|
||||||
``myproject``.
|
|
||||||
|
|
||||||
Twisted Web supports many flags and options, and the ``twistd`` utility does
|
|
||||||
as well; see ``twistd -h`` and ``twistd web -h`` for more information. For
|
|
||||||
example, to run a Twisted Web server in the foreground, on port 8080, with an
|
|
||||||
application from ``myproject``:
|
|
||||||
|
|
||||||
.. code-block:: text
|
|
||||||
|
|
||||||
$ twistd -n web --port tcp:8080 --wsgi myproject.app
|
|
||||||
|
|
||||||
.. _Twisted: https://twistedmatrix.com/trac/
|
|
||||||
.. _Twisted Web: https://twistedmatrix.com/trac/wiki/TwistedWeb
|
|
||||||
|
|
||||||
|
|
||||||
.. _deploying-proxy-setups:
|
|
||||||
|
|
||||||
Proxy Setups
|
|
||||||
------------
|
|
||||||
|
|
||||||
If you deploy your application using one of these servers behind an HTTP proxy
|
|
||||||
you will need to rewrite a few headers in order for the application to work.
|
|
||||||
The two problematic values in the WSGI environment usually are ``REMOTE_ADDR``
|
|
||||||
and ``HTTP_HOST``. You can configure your httpd to pass these headers, or you
|
|
||||||
can fix them in middleware. Werkzeug ships a fixer that will solve some common
|
|
||||||
setups, but you might want to write your own WSGI middleware for specific
|
|
||||||
setups.
|
|
||||||
|
|
||||||
Here's a simple nginx configuration which proxies to an application served on
|
|
||||||
localhost at port 8000, setting appropriate headers:
|
|
||||||
|
|
||||||
.. sourcecode:: nginx
|
|
||||||
|
|
||||||
server {
|
|
||||||
listen 80;
|
|
||||||
|
|
||||||
server_name _;
|
|
||||||
|
|
||||||
access_log /var/log/nginx/access.log;
|
|
||||||
error_log /var/log/nginx/error.log;
|
|
||||||
|
|
||||||
location / {
|
|
||||||
proxy_pass http://127.0.0.1:8000/;
|
|
||||||
proxy_redirect off;
|
|
||||||
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
If your httpd is not providing these headers, the most common setup invokes the
|
|
||||||
host being set from ``X-Forwarded-Host`` and the remote address from
|
|
||||||
``X-Forwarded-For``::
|
|
||||||
|
|
||||||
from werkzeug.middleware.proxy_fix import ProxyFix
|
|
||||||
app.wsgi_app = ProxyFix(app.wsgi_app, x_proto=1, x_host=1)
|
|
||||||
|
|
||||||
.. admonition:: Trusting Headers
|
|
||||||
|
|
||||||
Please keep in mind that it is a security issue to use such a middleware in
|
|
||||||
a non-proxy setup because it will blindly trust the incoming headers which
|
|
||||||
might be forged by malicious clients.
|
|
||||||
|
|
||||||
If you want to rewrite the headers from another header, you might want to
|
|
||||||
use a fixer like this::
|
|
||||||
|
|
||||||
class CustomProxyFix(object):
|
|
||||||
|
|
||||||
def __init__(self, app):
|
|
||||||
self.app = app
|
|
||||||
|
|
||||||
def __call__(self, environ, start_response):
|
|
||||||
host = environ.get('HTTP_X_FHOST', '')
|
|
||||||
if host:
|
|
||||||
environ['HTTP_HOST'] = host
|
|
||||||
return self.app(environ, start_response)
|
|
||||||
|
|
||||||
app.wsgi_app = CustomProxyFix(app.wsgi_app)
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue