Deploy Flask Apps to AWS Elastic Beanstalk using Travis CI
Tags: flask, python, aws, devops
Published: 2020-02-09 16:01:46

In this article, we are going to deploy a Flask application to AWS Elastic Beanstalk via a GitHub and Travis CI deployment pipeline. Our goal: After pushing changes to your code to GitHub, Travis CI should pull our code, perform all tests, and if they pass, deploy the application to AWS Elastic Beanstalk. Let's go!


## Step 1: Set up project directory and install dependencies


First, we need to install all dependencies on our local machine. Because AWS Elastic Beanstalk requires context about which environment our application needs to run in, we will create a Python virtual environment and install all dependencies within the virtual environment. Run the following commands in your terminal to get started and set up the project directory:

```bash
# create and go to project folder
export PROJECT_NAME=deploy-eb-via-travis-ci
mkdir ~/VSCodeProjects/$PROJECT_NAME
cd ~/VSCodeProjects/$PROJECT_NAME
# create virtual environment and activate it
python3 -m venv my_venv
source my_venv/bin/activate
# within virtual environment, install the following:
python3 -m pip install flask
python3 -m pip freeze > requirements.txt
deactivate
# set up file structure
touch app.py wsgi.py .travis.yml 
mkdir tests
touch tests/test_42.py
```


Then, open your project directory with your favorite code editor. You can use your command line: For example for PyCharm you can just type `charm .` or for VSCode you can type `code .`  Learn more about how to set up command line interfaces for PyCharm [here](https://www.jetbrains.com/help/pycharm/opening-files-from-command-line.html) or VSCode [here](https://code.visualstudio.com/docs/editor/command-line).


## Step 2: "Hello World" Flask Application


We need to create a basic "Hello World" Flask application, which we can then use for the purposes of setting up our continuous deployment pipeline scenario. Assume that this is a placeholder before you are able to deploy your (real) Flask application. The deployment of a real-world application likely follows very similar steps as described here. As seen above, we created four empty files.


In `app.py` paste the following code:

```python
from flask import Flask, jsonify
app = Flask(__name__)
@app.route("/")
def index():
    return jsonify({"hello": "world", "from": "index"})
@app.route("/foo")
def foo():
    return jsonify({"hello": "world", "from": "foo"})
if __name__ == '__main__':
    app.run(debug=True)
```


Your `wsgi.py` should look like this:

```python
from app import app as application
if __name__ == "__main__":
    application.run()
```


In `tests/test_42.py` add a sample test (which will always pass):

```bash
def test_42():
    assert 42 == 42
```


We will revisit the fourth required file, `.travis.yml`, when we are setting up the Travis CI integration. As for now, we are ready to (manually) deploy our first version to AWS Elastic Beanstalk. 


## Step 3: Deploy to AWS Elastic Beanstalk


Before we can deploy our application, make sure that the AWS Elastic Beanstalk CLI `eb` is installed. If it's not installed on your local machine, you can follow the instructions [here](https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/eb-cli3-install.html). You can check whether your local machine has `eb` installed by running the following command:

```bash
eb --version
```


The following commands will set up AWS Elastic Beanstalk with an empty application. Make sure to provide your AWS region (in my case `eu-west-1`) and required Python version (in my case `python-3.6` is the latest Python version supported by EB at the time of writing) accordingly:

```bash
export REGION=eu-west-1
export PYTHON_VERSION=python-3.6 # latest version supported as of January 2020
eb init -p $PYTHON_VERSION -r $REGION $PROJECT_NAME
```


Next, we need to configure the default WSGI path. This will tell AWS Elastic Beanstalk where the entry point of our Flask application is located at. In our case, it's the file `wsgi.py`. Run the following commands in your terminal:

```bash
# default is application.py
# alternative way to configure this -> eb config and then set manually
mkdir .ebextensions
echo "option_settings:
  - namespace: aws:elasticbeanstalk:container:python
    option_name: WSGIPath
    value: wsgi.py" > .ebextensions/wsgi.config
```


Our continuous deployment pipeline will be git-based, since we push our changes to GitHub. Initialize `git` with the following commands:

```bash
git init
git add .
git commit -m "First commit"
```


Note: AWS Elastic Beanstalk will create `.zip` archives for application deployment based on your changes that are __*committed*__ to git. Therefore, remember to commit your changes before manually deploying via `eb deploy`, otherwise your changes will not be deployed!


In a real-world context, you would have more than one environment for your application, the most basic scenario being a live deployment (where users are interacting with your app) and a test deployment (where you will deploy new releases to test them). You can create an environment on AWS Elastic Beanstalk with the following commands:

```bash
export ENVIRONMENT_NAME=test # for example: "test" or "live"
export INSTANCE_TYPE=t2.nano # specify the instance type
eb create $ENVIRONMENT_NAME-$PROJECT_NAME --single -i $INSTANCE_TYPE
eb use $ENVIRONMENT_NAME-$PROJECT_NAME
```


Now, you can trigger a manual deployment to AWS Elastic Beanstalk:

```bash
eb deploy
```


If you can confirm your Flask application is working as expected, we can move on and integrate Travis CI to implement our continuous deployment pipeline.


## Step 4: Setting up Travis CI pipeline


Before we can proceed, you need to push your changes to GitHub:

```bash
git remote add origin https://github.com/owner/repo.git
git push -u origin master
```


Make sure to enable Travis CI in the repository settings, either in your GitHub account or your Travis CI account. This will make Travis CI listen to changes of your repository automatically trigger a build for this repository whenever a push is registered. Your Travis CI account needs to have the required permissions to access your GitHub account and your repositories. 


Make sure the Travis command line interface `travis` is installed:

```bash
travis --version
```


If it's not installed, you can run the following command in order to install it:

```bash
gem install travis
```


There are two versions of Travis CI: A free version at `travis-ci.org` and a paid version `travis-ci.com`. If you are using the paid version instead of the open source version, you need to login first, and use the argument `--pro` with every command. In this article, we will use `travis-ci.com`. Ensure you are logged in to `travis-ci.com` by running:

```bash
travis whoami --pro
```


If your username shows up, you're all set. Otherwise, sign in from your terminal using:

```bash
travis login --pro
```


The repository is automatically inferred from your git remote (which is GitHub). To tell Travis CI more about our application, what needs to be tested, and where our application should be deployed to, add the following configuration to **`.travis.yml`** :

```yaml
language: python
python:
  - 3.6
before_install:
  - python --version
  - pip install -U pip
  - pip install -U pytest pytest-cov
  - pip install codecov
script: pytest
after_success:
  - pytest --cov=./
  - codecov
deploy:
  provider: elasticbeanstalk
  access_key_id:
    secure: $AWS_ACCESS_KEY_ID
  secret_access_key:
    secure: $AWS_ACCESS_KEY_SECRET
  region: $AWS_REGION
  app: $AWS_EB_APPLICATION
  env: $AWS_EB_ENVIRONMENT
  bucket_name: $AWS_EB_S3_BUCKET
```


Then add the necessary encrypted environment variables automatically to your `.travis.yml` file (see references above):

```bash
travis encrypt --pro AWS_ACCESS_KEY_ID="YOUR_KEY_ID_HERE" --add
travis encrypt --pro AWS_ACCESS_KEY_SECRET="YOUR_KEY_SECRET_HERE" --add
travis encrypt --pro AWS_REGION=$REGION --add
travis encrypt --pro AWS_EB_APPLICATION=$PROJECT_NAME --add
travis encrypt --pro AWS_EB_ENVIRONMENT=$ENVIRONMENT_NAME-$PROJECT_NAME --add
travis encrypt --pro AWS_EB_S3_BUCKET="YOUR_BUCKET_HERE" --add # change this as needed – a new bucket will be automatically created if it does not yet exist  
```


It's strongly recommended to encrypt environment variables with sensititve information, such as  `AWS_ACCESS_KEY_ID` or `AWS_ACCESS_KEY_SECRET`. Sensitive information should never end up in source control. Check out [this resource](https://docs.travis-ci.com/user/encryption-keys#usage) for more information on how to encrypt environment variables for Travis CI. An alternative to the above step (`travis encrypt ...`) is to configure the environment variables using the Travis CI web application and adding them to the repository settings. An alternative to setting encrypted environment variables is to use the following command: `travis env set VARIABLE_NAME "value"`


And... we're done! From now on, you can make __changes to your application__ and __simply push them to GitHub__. Everything else is set up and done automatically for you: Travis CI will pull your code, perform the tests, and if they pass, deploy the application to AWS Elastic Beanstalk. Should the tests not pass, the application version will not be deployed, and you will be able to investigate further as to why.


## Conclusion

You now have gone through the required steps to create a continuous deployment pipeline for your Flask application. Newly pushed changes are automatically analyzed and tested by Travis CI. If all tests pass, new versions of your Flask application will be continuously deployed to the specified environment(s) within AWS Elastic Beanstalk.


I hope you enjoyed this article –  please let me know what you think, or if you have any questions.