×
Community Blog Setting Up a Flask Application on Alibaba Cloud ECS Ubuntu 16.04

Setting Up a Flask Application on Alibaba Cloud ECS Ubuntu 16.04

In this guide, we will be installing Flask and running a sample application on Alibaba Cloud ECS with Ubuntu 16.04.

By Tonino Jankov, Alibaba Cloud Tech Share Author. Tech Share is Alibaba Cloud's incentive program to encourage the sharing of technical knowledge and best practices within the cloud community.

Flask is a minimalistic and modular web framework written in Python. It initially started as an April fools joke by Armin Ronacher, about python micro frameworks in 2010, but then it took a life of its own, becoming one of the most widely adopted python frameworks - most popular on GitHub in 2016.

It started, basically as a 160 lines of code thrown together to create a very basic WSGI application. Its built on two foundational libraries - Werkzeug, which is a wsgi / http utility library (or middleware, if you will) - and Jinja2 - fast python templating engine.

The whole flask stack is BSD licensed.

Alibaba Cloud Elastic Compute Service (ECS) is a flexible cloud product that allows us to deploy many different server technologies on a scalable, secure and easy to use platform. In this guide, we will be installing Flask and running a sample application on an Alibaba Cloud ECS instance with Ubuntu 16.04.

Setting Up the System

For the purpose of this guide, we will setup an ECS instance with Ubuntu 16.04 LTS. The procedure itself is pretty straightforward - after we log into Alibaba Cloud web management console, we select Elastic Compute Service in the leftmost main menu, and choose Instances in the sub-menu. We will then be able to start the process of creating new instance by clicking the Create Instance button in the top-right corner of the screen.

setup_os

In the process, we will choose Ubuntu 16.04 LTS image, and select security enhancements option - this includes intrusion detection, brute-force login protection, email alerts, etc.

os_image

After we specify all the options, we proceed with our instance creation. After about 5 minutes, we should be able to see our instance public ('internet') IP, which allows us to login to our server via ssh using access details we specified at creation (either key or password access).

Setting Up the Environment

Provided that we created our instance successfully, we will connect to it's public IP, and update repositories and, optionally, upgrade the system: apt-get update && apt-get upgrade. If we have logged in as root, we don't need to use sudo.

Flask docs recommend that we use the latest version of Python, which is 3.7.0, but the default version shipped with Ubuntu is 2.7 - so we will fix this. Instead of removing the 2.7 and reinstalling 3.7.0 version, we will use pyenv. Pyenv is python version manager, and it will allow us to install and use any version of python. To install pyenv, we will first install git and curl as prerequisites: apt-get install git curl -y. Now we can use the following one-liner to install pyenv:

curl -L https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer | bash

Pyenv will warn us, upon installation, to add following lines to ~/.bash_profile if we haven't done it yet:

export PATH="/root/.pyenv/bin:$PATH"
eval "$(pyenv init -)"
eval "$(pyenv virtualenv-init -)"

(we will create the file if it doesn't exist yet) - and when we add it, instead of logging out and logging back in to restart the shell session, we will source ~/.bash_profile to load our new command. Now we will install dependencies for building our version of python (and other things):

apt-get install build-essential python-dev python-setuptools python-pip python-smbus libncursesw5-dev libgdbm-dev libc6-dev zlib1g-dev libsqlite3-dev tk-dev libssl-dev openssl libffi-dev zlib1g-dev libbz2-dev
libreadline-dev -y

The python-pip package will make the pip command available, in case it is missing - this is the default python package manager.

To list all python versions available in pyenv for installation, we use pyenv install -l - but this will throw out an unwieldy list of versions, so we will pipe it to grep - a notorious linux utility, to filter output to exactly what we need:

pyenv install -l | grep 3.7

pyenv_install

This shows us all the versions containing "3.7" - and we continue installing version 3.7.0 - pyenv install 3.7.0

After python 3.7.0 is installed, we make it the default global version:

pyenv global 3.7.0

We can check the current version with pyenv version

Installing Flask and Server Software

We install flask by issuing command pip install Flask. Next thing we do is create a minimal Flask Hello World app. We will create a directory to contain our app under /var/www:

cd /var/www
mkdir flaskapp

And then we put the following code in app.py file in that directory:

  python
  from flask import Flask
  application = Flask(__name__)
  
  @application.route('/')
  def hello_world():
      return 'Hello World!'

For the purpose of deployment, we also install uwsgi - which is a server interface software that enables servers like NGINX to serve python applications:

pip install uwsgi

We also, obviously, install nginx:

sudo apt-get install nginx -y

Now that the nginx is installed, we will add our virtual host file in /etc/ngind/sites-enabled/ - and remove the default virtual host that is created when we install nginx. We add following into our somedomain file:

nginx
# the upstream component nginx needs to connect to
upstream flask {
    server unix:///tmp/flaskapp.sock;
}

# configuration of the server
server {
    # the port your site will be served on
    listen      80;
    # the domain name it will serve for
    server_name myxydomain.com; # substitute with your domain name
    charset     utf-8;

    # max upload size
    client_max_body_size 75M;   # adjust to taste

    # Finally, send all non-media requests to the Django server.
    location / {
        uwsgi_pass  flask;
        include     /var/www/flaskapp/uwsgi_params; # the uwsgi_params file you installed
    }
}

Here we will replace the myxydomain.com in the code with the actual name of our domain, whose domain records we have directed to the public IP address of our ECS server instance.

Nginx virtual host file here is referring to uwsgi_params file - we will simply download this file from nginx github repo - it describes how to pass request parameters from nginx to uwsgi. We put this file in our flaskapp directory

Now if we restart nginx after we create virtual host - service nginx restart - and start uwsgi on the defined socket, like

uwsgi --socket /tmp/flaskapp.sock --wsgi-file /var/www/flaskapp/flaskapp.py --chown-socket=www-data:www-data --logto /var/log/uwsga.log - we should see the result "Hello World!!!" in our browser when we load our website url.

This way we can debug our uwsgi setup before installing it as a service. We change owner of the socket file on every start, because of permission issues, and we output all logging to a designated file under /var/log. Our basic Hello World app is being served by nginx, which passes requests to our python application through uwsgi.

If this step succeeded, we can proceed with setting up our flask app as a service.

Setting Up UWSGI App as a Service

First thing we will do is to put our runtime settings for uwsgi into an ini file:

bash
[uwsgi]


chdir           = /var/www/flaskapp
module          = flaskapp
# process-related settings
# master
master          = true
# maximum number of worker processes
processes       = 10
# the socket (use the full path to be safe)
socket          = /tmp/flaskapp.sock
# ... with appropriate permissions - may be needed
# chmod-socket    = 664
chown-socket    = www-data:www-data
daemonize       = /var/log/uwsgi.log
vacuum          = true

Here we added master / worker processes, daemonize instead of logto, and some other details that may come handy, like the commented chmod line in case we have problems with permissions.

Now, the next thing we do is to, again, test these settings on a command line, running uwsgi behind nginx:

uwsgi --ini /var/www/flaskapp/uwsgi.ini - we don't need to pass any other parameters to uwsgi, because all the parameters are in our new ini file (for the simplicity sake, we put it into /var/www/flaskapp directory, but normally it would probably be under /etc/uwsgi or something like that).

Now if we get back "Hello World!" in our browser, that means we succeeded.

To make sure that our app will be up even upon the server restart, we will add the startup script to our rc.local.

First thing we do is make uwsgi available globally, because, currently, we created it under root account, using pyenv, and whereis uwsgi shows us that it is at /root/.pyenv/shims/uwsgi. As a precaution against possible issues, we create a global link to it:

ln -T /root/.pyenv/shims/uwsgi /usr/local/bin/uwsgi

Then we put the startup script into /etc/rc.local, just before the exit 0 line:

bash
#!/bin/sh -e
#
# rc.local
#
# This script is executed at the end of each multiuser runlevel.
# Make sure that the script will "exit 0" on success or any other
# value on error.
#
# In order to enable or disable this script just change the execution
# bits.
#
# By default this script does nothing.

/usr/local/bin/uwsgi --ini /var/www/flaskapp/uwsgi.ini

exit 0

We should also make sure that the rc.local file is executable - a sudo chmod 755 /etc/rc.local command will do.

We can try out our startup script by simply calling it on the command line - /etc/rc.local, and it will show us if there are any issues. uwsgi logs are in /var/log/uwsgi.log, so if there are any issues with flask or uwsgi, that file should give us clues. If, however, we encounter issues with nginx, like those generic nginx error pages with codes like 503, /var/log/nginx/error.log holds latest nginx errors, so we can draw some conclusions from there.

Finally, if everything went well, we will reboot the server, and after couple of seconds, we should be able to see Hello World! in our browser - meaning that our setup was successful.

Conclusion

In this guide we have covered the setup and deployment of a Flask application to Alibaba Cloud Elastic Compute Service (ECS) instance, addressing potential issues along the way. Real, production setup will also, probably, use more subtleties and features of both Nginx (caching, for example), and Flask, and possibly SSL encryption, but this is, hopefully, a useful starting point.

1 1 1
Share on

Alibaba Clouder

2,599 posts | 762 followers

You may also like

Comments

Arman Ali May 26, 2021 at 8:41 am

Great!