Archive

Posts Tagged ‘gunicorn’

gunicorn from upstart fails to start

March 17, 2016 1 comment

Today I was trying to put together these: ubuntu + nginx + gunicorn + start gunicorn from upstart + flask + virtualenv. After a whole afternoon, I managed to do it. I will write a detailed post about it, but now I just want to concentrate on one part, which was the most problematic.

Problem
I wanted to start gunicorn from upstart. So I issued the following command:

$ sudo service gunicorn start 
gunicorn start/running, process 21415
$ sudo service gunicorn status
gunicorn stop/waiting

Each time I tried to launch it, it failed.

Solution
First of all, I didn’t know where the upstart log files were located. I know, it’s lame, but after a few hours I relized that it’d be a good idea to google it… So, the log files are in this folder:

/var/log/upstart

The log of gunicorn is here:

/var/log/upstart/gunicorn.log

Now, I started to monitor the end of this file in another terminal with “tail -f /var/log/upstart/gunicorn.log“. With this information I could easily find the error. I put here my solution and then I add some explanation:

# /etc/init/gunicorn.conf
#
description "Gunicorn daemon for a Flask project"

start on (local-filesystems and net-device-up IFACE=eth0)
stop on runlevel [!12345]

# If the process quits unexpectadly trigger a respawn
respawn

setuid demo
setgid demo

script
. "/home/demo/.virtualenvs/myproj/bin/activate"
cd /home/demo/projects/myproj/api
exec gunicorn --name="my project" --bind=127.0.0.1:9000 --config /etc/gunicorn.d/gunicorn.py api:app --pid=gunicorn_from_nginx.pid
end script

And you know what caused the problem? Instead of “.” I tried to use “source”. This is what I found in every tutorial. But as it turned out, this script is executed with “/bin/sh“, not with “/bin/bash“, and sh doesn’t know source. Geez…

The normal user on this machine is called “demo“. My Flask project is within a virtual environment. You can create a script block and the steps within are executed with “sh”. So first we “source” the virtual environment, then enter the directory where the Python script to be executed is located (here “api” is the name of the folder, and in there I have a file called “api.py“, which contains the “app” variable). gunicorn is also installed in the virtual environment! You don’t need to install it globally with “sudo“!

Nginx is listening on port 80 and every connection that arrives there are forwarded to this gunicorn instance on port 9000. But I’ll write about it a bit later.

Appendix
The content of “/etc/gunicorn.d/gunicorn.py“:

"""gunicorn WSGI server configuration."""
from multiprocessing import cpu_count
from os import environ


def max_workers():
    return cpu_count() * 2 + 1

max_requests = 1000
worker_class = 'gevent'
workers = max_workers()
Categories: flask, python Tags: , , , ,

Getting started with Django on a DigitalOcean VPS

Django is a high-level Python framework for developing web applications rapidly. DigitalOcean’s Django One-Click app quickly deploys a preconfigured development environment to your VPS employing Django, Nginx, Gunicorn, and Postgres.

More info here.

Flask: cannot fetch a URL on localhost

November 2, 2013 Leave a comment

Problem
I had a simple Flask application that included a web service, i.e. calling an address returns some value (a JSON result for instance). I wanted to reuse this service inside the app. by simply calling it (via the HTTP protocol) and getting the return value. However, this call never finished. The browser was loading and I got no result.

What happened?
I posted the problem here and it turned out that “the development server is single threaded, so when you call a url served by that application from within the application itself, you create a deadlock situation.” Hmm…

My first idea was to replace the dev. server with a more serious one. With gunicorn I could make it work:

gunicorn -w 4 -b 127.0.0.1:5000 hello:app

However, I deploy the app. on Heroku, where you have just 1 worker for free, so it behaves just like the dev. server!

Solution
I had to rewrite the code to eliminate this extra call. (Or, I could have kept this call if I had had at least 2 worker threads.)

Example
Here is a simplified code that demonstrates the problem:

#!/usr/bin/env python

# hello.py

from flask import Flask
from flask import url_for
import requests

app = Flask(__name__)

@app.route('/')
def hello_world():
    return "Hello, World!"

@app.route('/get')
def get():
    url = url_for("hello_world", _external=True)    # full URL
    print '!!!', url    # debug info
    r = requests.get(url)    # it hangs at this point
    return "from get: " + r.text

if __name__ == "__main__":
    app.run(debug=True)