DEV Community

Cover image for Deploy Flask Apps to AWS Elastic Beanstalk using Travis CI
Camillo Visini
Camillo Visini

Posted on • Originally published at camillovisini.com

Deploy Flask Apps to AWS Elastic Beanstalk using Travis CI

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:

# 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
Enter fullscreen mode Exit fullscreen mode

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 or VSCode here.

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:

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)
Enter fullscreen mode Exit fullscreen mode

Your wsgi.py should look like this:

from app import app as application
if __name__ == "__main__":
    application.run()
Enter fullscreen mode Exit fullscreen mode

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

def test_42():
    assert 42 == 42
Enter fullscreen mode Exit fullscreen mode

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. You can check whether your local machine has eb installed by running the following command:

eb --version
Enter fullscreen mode Exit fullscreen mode

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:

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
Enter fullscreen mode Exit fullscreen mode

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:

# 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
Enter fullscreen mode Exit fullscreen mode

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

git init
git add .
git commit -m "First commit"
Enter fullscreen mode Exit fullscreen mode

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:

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
Enter fullscreen mode Exit fullscreen mode

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

eb deploy
Enter fullscreen mode Exit fullscreen mode

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:

git remote add origin https://github.com/owner/repo.git
git push -u origin master
Enter fullscreen mode Exit fullscreen mode

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:

travis --version
Enter fullscreen mode Exit fullscreen mode

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

gem install travis
Enter fullscreen mode Exit fullscreen mode

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:

travis whoami --pro
Enter fullscreen mode Exit fullscreen mode

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

travis login --pro
Enter fullscreen mode Exit fullscreen mode

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 :

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
Enter fullscreen mode Exit fullscreen mode

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

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  
Enter fullscreen mode Exit fullscreen mode

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 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.

Top comments (1)

Collapse
 
visini profile image
Camillo Visini

Let me know what you think – I'm looking forward to your comments!