Relative paths in Flask - python

Given the configuration below for : Nginx, Uwsgi and Flask.
If I move the Flask application from /test/ to production I must update the Nginx configuration, and preferably only that configuration. So a solution would by if the Flask #app.route('/test/') would be relative, so in a non existing syntax: #app.route('[root]'). I can't find a way to accomplish this. That being said, I presume there 'is' a way because if I must alter all the paths in Flask seems so impracticable.
Nginx:
location /test/ {
uwsgi_pass 127.0.0.01:3031;
include uwsgi_params;
}
Uwsgi:
uwsgi --socket 127.0.0.1:3031 --wsgi-file myflaskapp.py --callable app --proces$
Flask:
from flask import Flask
app = Flask(__name__)
#app.route('/test/')
def index():
return "<span style='color:red'>I am app 1</span>"
I'm trying to accomplish to move my application to any sub-path of the domain (site.com/apps, site.com/congres/, and so forth) and only to update the NGINX configuration.

You're probably thinking of #app.route('/'). The route URL appears to be absolute, but it is actually relative to the root URL of your application.
This is actually covered in Flask's documentation. You only specify the URL to bind your application to in the nginx configuration; Flask should be able to detect this location from the WSGI environment and build its routes accordingly.

Related

How to run django + email handler in App Engine

I try to run django and email handler app together in Google App Engine. I use code as google's doc and it must be run with python27. When I converted to code for python37 got script must be set to "auto" error. Can anyone help me? My code as below. Thanks in advance
app.yaml:
runtime: python37
entrypoint: gunicorn -b :$PORT myproject.wsgi
env_variables:
...
inbound_services:
- mail
- mail_bounce
handlers:
- url: /static
static_dir: static
- url: /_ah/mail/.+
script: handle_incoming_email.app
login: admin
handle_incoming_email.py:
import logging
from google.appengine.ext.webapp.mail_handlers import InboundMailHandler
class IncomingMailHandler(View, InboundMailHandler):
def receive(self, mail_message):
logging.info("Received a message from: " + mail_message.sender)
when I run this error raises from 'script: handle_incoming_email.app' script must be set 'auto'. How can I get handle_incoming_email.py to app.yaml if I set script: auto.
change '.+' as '.*' have tried.
In App Engine Standard, when using the Python3.7 runtime, the script tag under the handlers must be set to auto. You can check the documentation on this here on how the app.yaml must be configured, note that it's different than in the Python2.7 runtime, where you had to specify the script to run.
You can solve this by modifying your app.yaml file, like so:
runtime: python37
entrypoint: gunicorn -b :$PORT handle_incoming_email.app
env_variables:
...
inbound_services:
- mail
- mail_bounce
handlers:
- url: /static
static_dir: static
- url: /_ah/mail/
script: auto
login: admin
Notice how the required change was to set the script under /_ah/mail/ to auto, instead of specifying the path to the script to run. The handler then should automatically find the script to execute, from the files you deployed in App Engine.
Next, in your handle_incoming_email.py file you are not defining any entrypoint to handle your url /_ah/mail, you can solve this by adding the following, for example:
import webapp2
app = webapp2.WSGIApplication([
('/_ah/mail/', IncomingMailHandler),
], debug=True)
Notice now, how I changed the entrypoint in your app.yaml file to match the newly created WSGI entrypoint in your handle_incoming_email.py file.
Also I'm not sure about the '/.+' regex for the handler, you should leave it at '/.*'.
The handler parameter is used to route requests to static files, then the remaining routes are all routed to your main app (the auto value is the only option for the script element, as mentioned in the doc) at your entrypoint. That app must handle requests routing. You can't define another app in the same service.
I'd suggest to deploy your email app as a separate service in your App Engine app. This will allow you to specify specific resources or scaling for each one. This will follow the micro-services architecture principles enforced in App Engine.

How to deploy Django/React/Webpack app on Digital Ocean through Passenger/Nginx

I'm trying to deploy a web app built with Django/Redux/React/Webpack on a Digital Ocean droplet. I'm using Phusion Passenger and Nginx on the deployment server.
I used create-react-app to build a Django app which has a frontend that uses React/Redux, and a backend api that uses django-rest-framework. I built the frontend using npm run build.
The Django app is configured to look in the frontend/build folder for its files and everything works as expected, including authentication. It's based on this tutorial: http://v1k45.com/blog/modern-django-part-1-setting-up-django-and-react/
In settings.py:
ALLOWED_HOSTS = ['*']
TEMPLATES = [
...
'DIRS': [
os.path.join(BASE_DIR, 'frontend/build'),
],
...
]
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'frontend/build/static'),
]
On my development machine, I activate a Python 3.6 virtual environment and run ./manage.py runserver, and the app is displayed at localhost:3000.
On the deployment server, I've cloned the files into a folder in var/www/ and built the frontend.
I've set up Passenger according to the docs with a file passenger_wsgi.py:
import myapp.wsgi
application = myapp.wsgi.application
And the wsgi.py file is in the djangoapp folder below:
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myapp.settings')
application = get_wsgi_application()
The Passenger docs only cover a single-part app:
https://www.phusionpassenger.com/library/walkthroughs/start/python.html
https://www.phusionpassenger.com/library/walkthroughs/deploy/python/digital_ocean/nginx/oss/xenial/deploy_app.html
https://www.phusionpassenger.com/library/deploy/wsgi_spec.html
I've tried cloning the tutorial part 1 code directly onto my server and following the instructions to run it. I got this to work on the server by adding "proxy": "http://localhost:8000" to frontend/package.json. If I run the Django server with ./manage.py runserver --settings=ponynote.production_settings xxx.x.x.x:8000
then the app is correctly served up at myserver:8000. However Passenger is still not serving up the right files.
I have changed wsgi.py to say this:
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myapp.production_settings")
application = get_wsgi_application()
The page served by Passenger at URL root now appears to have the right links to the js files such as "text/javascript" src="/static/bundles/js/main.a416835a.js, but the links don't work: the expected js is not present. Passenger is failing to serve the js files from static/bundles/js, even though the Django server can find them.
Very grateful for any help or ideas.
Create-react-app has a fairly opinionated setup for local and production environments.
Locally, running npm start will run a webpack-dev-server, which you would typically access on port 3000. It runs a local nodejs web server to serve the files. You can route requests to your local Django server via the proxy setting.
It's worth noting that at this point there is little or no connection between your React app and Django. If you use the proxy setting, the only thing connecting the two apps is the routing of any requests not handled by your React app to your Django app via the port.
By default in create-react-app (and as noted in the tutorial you mentioned you are following) in production you would run npm run build which will process your create-react-app files into static JS and CSS files, which are then accessed in Django like static files any other Django app.
One thing Django is missing in order to access the static files is a way to know what files are generated when running npm run build. Running a build will typically result in files output like this:
- css
|- main.e0c3cfcb.css
|- main.e0c3cfcb.css.map
- js
|- 0.eb5a2873.chunk.js
|- 0.eb5a2873.chunk.js.map
|- 1.951bae33.chunk.js
|- 1.951bae33.chunk.js.map
A random hash is added to filenames to ensure cache busting. This is where webpack-bundle-tracker and django-webpack-loader come in. When build files are generated, an accompanying file is also created called manifest.json, listing the files created for the build. This is generated in Webpack and picked up by django-webpack-loader so that Django can know which files to import.
It is possible to run a nodejs server in production, or to use server-side rendering, but if you're following the tutorial you mentioned and using create-react-app default settings, then running npm run build and deploying the static files is the simplest, safest option.
Nothing in any of the Passenger deployment links you mention cover anything beyond deploying a Python/Django app - you would need to manage two apps and deployments to have both Django and React running as servers in production.
Note that the tutorial you mention covers how to get your build files into Django in production, but you will need to ensure that you have webpack-bundle-tracker, django-webpack-loader and your Django staticfiles configuration all configured to work together.
The key missing setting was the 'location' setting in the Passenger config file.
Although the Django server serves up the static files, including the build files for your React app, Nginx doesn't see any static files except those in a 'public' directory.
So to deploy a Django app built with Webpack to production, you need to tell Nginx about those files. If you're using Passenger, these settings are probably in a separate Passenger config file. 'alias' is the command to use in this case where the folder has a different name from 'static' (which is where the web page links point).
If you use a virtual environment for your app, you need to specify where Passenger can find the right Python executable.
/etc/nginx/sites-enabled/myapp.conf
server {
listen 80;
server_name xx.xx.xx.xx;
# Tell Passenger where the Python executable is
passenger_python /var/www/myapp/venv36/bin/python3.6;
# Tell Nginx and Passenger where your app's 'public' directory is
# And where to find wsgi.py file
root /var/www/myapp/myapp/myapp;
# Tell Nginx where Webpack puts the bundle folder
location /static/ {
autoindex on;
alias /var/www/myapp/myapp/assets/;
}
# Turn on Passenger
passenger_enabled on;
}
Passenger uses the wsgi.py file as an entry point to your app. You need a passenger_wsgi.py file one level above the wsgi.py file. This tells Passenger where to find the wsgi.py file.
/var/www/myapp/myapp/passenger_wsgi.py
import myapp.wsgi
application = myapp.wsgi.application
/var/www/myapp/myapp/myapp/wsgi.py
If you are using a separate production_settings.py file, make sure this is specified here.
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myapp.production_settings")
application = get_wsgi_application()

Deploy flask in production with GeventWSGI and Nginx

i actually have a rest api written in python with flask and flaskrestful extension.
I use gevent WSGI
def runserver():
api.debug = True
http_server = WSGIServer(('', 5000), api)
http_server.start()
All works like a charm on my machine.
I want go in production on a linux vm,on the internet i searched for hours,i don't choosed mod_wsgi because gevent doesn't work properly with it,so i prefer use nginx.
On the internet i saw flask apps hosted with uWSGI,my answer is i need to use uWSGI?
Even i use geventWSGI in my flask application?
How to work with this?
In case i don't need uWSGI,i only need to config nginx sites to pass the request properly to my flask app?
I'm newbie to all this so i'm a little confused.
Thanks in advance
You can run Uwsgi in Gevent mode http://uwsgi-docs.readthedocs.org/en/latest/Gevent.html and then route all flask requests to it via nginx.
server {
listen 80;
server_name customersite1.com;
access_log /var/log/customersite1/access_log;
location / {
root /var/www/customersite1;
uwsgi_pass 127.0.0.1:3031;
include uwsgi_params;
}
see http://uwsgi-docs.readthedocs.org/en/latest/Nginx.html for more details

Python module path (relative in flask, absolute in celery)

I am using beaker cache to cache the output of a function.
When I invalidate the cache from flask uwsgi app, it did not reflect in celery app and vice versa.
On further investigation found that beaker using inspect.getsourcefile(func) for unique key to store in redis.
Now the problem is:
In flask uwsgi app, the path to load function shows up as
./myproject/db_api.py
while in celery it shows up as:
/opt/myproject/db_api.py.
How do I make sure that the inspect.getsourcefile(func) function returns same path in both cases?
Either making celery set the path as ./myporject/db_api.py or flask load the path as /opt/myproject/db_api.py is fine.
Celery is being run as a daemon with CELERYD_CHDIR='/opt' in /etc/default/celeryd. In celeryconfig.py I have CELERY_IMPORTS = ('myproject.controllers.celerytasks.cache_invalidate')
Flask is being run by uwsig with an .ini file on ubuntu with following config:
[uwsgi]
module = myproject
callable = app
chdir = /opt

Running Python Eve Rest API in Production

It is no time to move my Python Eve Api into a production environment. There are several ways to do this and the most common requirements are:
Error Logging
Automatic Respawn
Multiple Processes (if possible)
The best solution I found is to have a nginx server as frontend server.
With python eve running on the uWSGI middleware.
The problem: I have a custom __main__ which is not called by uwsgi.
Does anyone have this configuration running or another proposal? As soon as it works, I will share a running configuration.
thank you.
Solution (Update):
Based on the proposial below I moved the Eve() Method to the init.py and run the app with a sperate wsgi.py.
Folder structure:
webservice/ init.py
webservice/modules/...
settings.py
wsgi.py
Where init.py contains
app = Eve(auth=globalauth.TokenAuth)
Bootstrap(app)
app.config['X_DOMAINS'] = '*'
...
and wsgi.py contains
from webservice import app
if __name__ == "__main__":
app.run()
wsgi.ini
[uwsgi]
chdir=/var/www/api/prod
module=wsgi:app
socket=/tmp/api.sock
processes=1
master=True
pidfile=/tmp/api.v1.pid
max-requests=5000
daemonize=/var/www/api/logs/prod.api.log
logto=/var/www/api/logs/uwsgi.log
nginx.conf
location = /v1 { rewrite ^ /v1/; }
location /v1 { try_files $uri #apiWSGIv1; }
location #apiWSGIv1 {
include uwsgi_params;
uwsgi_modifier1 30;
uwsgi_pass unix:/tmp/digdisapi.sock;
}
start command:
uwsgi --ini uwsgi.ini
WSGI containers expect a callable/function to run, they do not execute your 'main' entry. With run:Eve you are asking uWSGI to execute (at every request) the "Eve" function in the "run" module (that is obviously wrong)
Move
app = Eve(auth=globalauth.TokenAuth)
out of the __main__ check and tell uWSGI to use the 'app' callable in the "run" module with
module = run:app

Categories

Resources