Deploying Python Web App to Alibaba ECS with Fabric
Tags: python, fabric, flask
Published: 2020-06-22 14:28:29

Fabric is a Python library  used to streamline and automate ssh workflows. It can be very useful in automating and scripting tedious, repetitive or complex tasks, deployments, installations and configurations on remote servers.

In this article we introduce Fabric, and show how to automate deployment of a Flask web app to Alibaba Cloud Elastic Compute Service.



## Fabric - the introduction

Fabric is a tool which we can use to deploy or sync our local projects to remote server. We can also automate all operations we can achieve through SSH - we can configure our server, install or update our software stack, start, stop, update or restart our application.

This article presumes that the reader already created and configured a server on Alibaba Cloud to serve a Flask web app, and that it is operational.

In [this blog post](https://www.alibabacloud.com/blog/setting-up-a-flask-application-on-alibaba-cloud-ecs-ubuntu-16-04_594502?spm=5176.11423382.detail.11.146c3f5dc4fEDd) we can find a full guide to set up Ubuntu server on Alibaba ECS to serve a Flask app. If we follow this guide, our app will live under /var/www/flaskapp (we will use `flaskapp` for the directory name here, but it can be changed to anything else).

According to the linked guide, our flaskapp will contain two files - uwsgi.ini and app.py.

## Deployment Setup

App.py file will contain something basic, like this:

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



Now, to streamline our deployment, we will presume the same document structure both on our local development machine and on the server.  We also presume that on our local machine we are using bash command line or something similar - on MS Windows we will use WSL shell.

If we start our (very basic) app locally, by doing `flask run` in our `flaskapp` directory, we will get a simple `Hello World!` web page on our localhost:

![hello world!](https://dev-to-uploads.s3.amazonaws.com/i/xnh1u3ly2o2b69cg59bc.png)



Now we need to install fabric - we need only to install it locally, our production or development servers don't need anything installed:

```bash
pip install fabric
```

Depending on the OS we are running, we may also be able to install it with

```bash
sudo apt-get install fabric
```

Fabric dependencies are the Python versions 2.7 or 3.4+; Invoke task-execution library;
and Parmiko SSH library. Once we install fabric, we should be able to run the  `fab` command in our terminal:

![Fab Command](https://dev-to-uploads.s3.amazonaws.com/i/fsueqr1ipwa9z3vifx4e.png)

As we can see, `fab` command will immediately complain about the (lack of) presence of the `fabfile` in the current directory, which should contain configuration and instructions for fabric to run its commands.

We will `cd` into the parent directory of our `flaskapp` project, and create our `fabfile` . Fabfile is a simple python file with some imports, variables, and function definitions:

```python
import sys
from fabric2 import Connection, task
from fabric2.config import Config

PROJECT_NAME = "fabtut"
REMOTE_PATH = "/var/www/flaskapp"
LOCAL_PATH = "/var/www/flaskapp"


def get_connection(ctx):
    try:
        with Connection(ctx.host, ctx.user, connect_kwargs=ctx.connect_kwargs) as conn:
            return conn
    except Exception as e:
        return None


@task
def staging(ctx):
    ctx.user = "root"
    ctx.host = "xxx.xxx.xxx.xxx"
    ctx.connect_kwargs.key_filename = "/home/user/.ssh/id_rsa"

@task
def production(ctx):
    ctx.user = "root"
    ctx.host = "yyy.yyy.yyy.yyy"
    ctx.connect_kwargs.key_filename = "/home/user/.ssh/id_rsa_2"


@task
def push(ctx):
    ctx.run("rsync -avzh --exclude '.git' -e 'ssh -i {}' {}/ {}@{}:{}/".format(ctx.connect_kwargs.key_filename, LOCAL_PATH, ctx.user, ctx.host, REMOTE_PATH))

@task
def pull(ctx):
    ctx.run("rsync -avzh -e 'ssh -i {}' {}@{}:{}/ {}/".format(ctx.connect_kwargs.key_filename, ctx.user, ctx.host, REMOTE_PATH, LOCAL_PATH))
```

Once we save this file,  in the same directory we can run  `fab staging push` and our local flaskapp directory will be synced with the one on our staging ECS server  from Alibaba.



### Some Notes About the Setup

For this guide to work, our `fabfile` will need to contain real IP addresses in `ctx.host` variables in the `staging` and `production` functions.

In this fabfile example we show one of the Fabric features - we can set up multiple environments, from development to staging and production.

We can also use multiple SSH keys for multiple server environments.

This setup also demonstrates how we can chain and combine commands, so in our case we can use `staging` and `production` interchangeably and combine them with `pull` or `push` as needed.

We could also define other tasks (in the fabfile we decorate them with `@task` decorator), whether to preform some preliminary tasks localy or on the server, or to do some cleanup after the main task.

If we were working with Django framework, we could here define tasks to perform migrations, or, if our tasks alter the web / application server  configuration, we could restart or reload the server after our task is finished.

Chaining of tasks comes handy here, and allows for defining very declarative and high-level workflows.

Since we rely on ssh authentication in this guide, we will need to have loaded our ssh public key  on the ECS server. Alibaba Cloud has more documentation on how to set up SSH authentication [here](https://www.alibabacloud.com/help/doc-detail/51792.htm).



## Conclusion

In this short guide, we set up Fabric deployment tool to define deployment workflows to Alibaba ECS server, and we explain how to work with Fabric. We use bash syntax combined with `rsync` here, but we could be using any other linux commands like awk, sed, etc.

Fabric itself allows even more declarative workflows, but we find that using linux & bash commands allows for much greater flexibility and more sophisticated workflows.

Do you have anything to add to this? Let us know in the comments.