NGinx + Gunicorn + Flask-SocketIO based deployment.

It's been a long time I wrote a blog post!. Recently I realized I should at least write about my unwarranted account of solving problems which were hard, to just get some information online.

So this post provides the details we need to know how to deploy the Flask-SocketIO app using Gunicorn and NGinx. Here are the links for NGinx, it is a web server with a focus on high concurrency, performance and low memory usage. Gunicorn 'Green Unicorn' is a Python WSGI HTTP Server for UNIX and Flask-SocketIO gives Flask applications access to low latency bi-directional communications between the clients and the server. The client-side application can use the SocketIO Javascript library or any compatible client to establish a permanent connection to the server. 

Version's I used:
Nginx: 1.6.0 (Websocket support in Nginx was introduced in version 1.4 and above)
Gunicorn: 17.5
Flask-SocketIO: 0.6.0

If you go through the document's of the tools, you will come to know that, flask-socketio uses gevent-socketio which intern uses gevent and gevent-websocket. 

The biggest problem I faced was, when I deployed the normal way of handling socketio via Nginx on Amzon's EC2 server, it was never giving a 100% connection (was not giving event type of connect once the handshake was done), but when i changed the server listen port from 80 (the default http) to 8180 (random), every connection was getting established. (Kind of weird but never understood the root cause of it, still finding it out and I had tried almost every other timeout settings and to ensure the connection was not getting timed out.)

Here is the configuration for Nginx config used:

http {  
      sendfile on;  
      tcp_nopush on;  
      tcp_nodelay on;  
      keepalive_timeout 65;  
      types_hash_max_size 2048;  

      include /etc/nginx/mime.types;  
      default_type application/octet-stream;  

      access_log /var/log/nginx/access.log;  
      error_log /var/log/nginx/error.log;  

      gzip on;  
      gzip_disable "msie6";  
      gzip_vary on;  
      gzip_proxied any;  
      gzip_comp_level 6;  
      gzip_buffers 16 8k;  
      gzip_http_version 1.1;  
      gzip_types text/plain text/css application/json
                 application/x-javascript text/xml application/xml 
                 application/xml+rss text/javascript;  

      include /etc/nginx/conf.d/*.conf;  

   server {  

           listen 8180;  #Custom listener port pointed to 8180 and not 80
           server_name track.mydomain.in;  
           root <path_to_my_project_root>;  
           access_log /var/log/nginx/track.log;  
      
           location / {  
             proxy_pass http://127.0.0.1:5000;  
             proxy_redirect off;  
             proxy_buffering 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;  
           }    
      
           location /socket.io {  
             proxy_pass http://127.0.0.1:5000/socket.io;  
             proxy_redirect off;  
             proxy_buffering 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_http_version 1.1;  
             proxy_set_header Upgrade $http_upgrade;  
             proxy_set_header Connection "upgrade";  #Tells the Nginx to treat the incoming
           }  
      }    
 }  

We run Gunicorn as following:

#gunicorn --worker-class socketio.sgunicorn.GeventSocketIOWorker -w 1
track:app -b 0.0.0.0:5000 --log-file /tmp/gunicorn.log

Explaining the above, the syntax is as following:
#gunicorn --worker-class socketio.sgunicorn.GeventSocketIOWorker -w 1 
module_file_name:the_app_variable -b 0.0.0.0:5000 --log-file /tmp/gunicorn.log

Now, it is recommended we run the gunicorn with one worker and hence we have -w as 1 and pointed the worker class to GeventSocetIOWorker and the log file to wherever we want the log to be printed.

To explain the flask-socektio application, I had created a track based socketio app, which would listen to specific event types and then perform the action. So the project structure is as follows:

TrackProject
    |
    |__
       track.py

The track.py would have the flask application and socketio initialized as following:

app = Flask(__name__)
socketio = SocketIO(app)

Currently the Flask-SocketIO v0.6.0 that supports JavaScript library SocketIO client is v0.9.16. There is some recent development that is going on with Flask-SocketIO v1.0 which would support client library v1.x.

Comments

Popular posts from this blog

SSH using Chrome Secure Shell app with SSH identity (private key and public key)

Load Testing using Apache Bench - Post JSON API