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:
<span># create and go to project folder</span><span>export </span><span>PROJECT_NAME</span><span>=</span>deploy-eb-via-travis-ci<span>mkdir</span> ~/VSCodeProjects/<span>$PROJECT_NAME</span><span>cd</span> ~/VSCodeProjects/<span>$PROJECT_NAME</span><span># create virtual environment and activate it</span>python3 <span>-m</span> venv my_venv<span>source </span>my_venv/bin/activate<span># within virtual environment, install the following:</span>python3 <span>-m</span> pip <span>install </span>flaskpython3 <span>-m</span> pip freeze <span>></span> requirements.txtdeactivate<span># set up file structure</span><span>touch </span>app.py wsgi.py .travis.yml<span>mkdir </span>tests<span>touch </span>tests/test_42.py<span># create and go to project folder</span> <span>export </span><span>PROJECT_NAME</span><span>=</span>deploy-eb-via-travis-ci <span>mkdir</span> ~/VSCodeProjects/<span>$PROJECT_NAME</span> <span>cd</span> ~/VSCodeProjects/<span>$PROJECT_NAME</span> <span># create virtual environment and activate it</span> python3 <span>-m</span> venv my_venv <span>source </span>my_venv/bin/activate <span># within virtual environment, install the following:</span> python3 <span>-m</span> pip <span>install </span>flask python3 <span>-m</span> pip freeze <span>></span> requirements.txt deactivate <span># set up file structure</span> <span>touch </span>app.py wsgi.py .travis.yml <span>mkdir </span>tests <span>touch </span>tests/test_42.py# 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:
<span>from</span> <span>flask</span> <span>import</span> <span>Flask</span><span>,</span> <span>jsonify</span><span>app</span> <span>=</span> <span>Flask</span><span>(</span><span>__name__</span><span>)</span><span>@</span><span>app</span><span>.</span><span>route</span><span>(</span><span>"/"</span><span>)</span><span>def</span> <span>index</span><span>():</span><span>return</span> <span>jsonify</span><span>({</span><span>"hello"</span><span>:</span> <span>"world"</span><span>,</span> <span>"from"</span><span>:</span> <span>"index"</span><span>})</span><span>@</span><span>app</span><span>.</span><span>route</span><span>(</span><span>"/foo"</span><span>)</span><span>def</span> <span>foo</span><span>():</span><span>return</span> <span>jsonify</span><span>({</span><span>"hello"</span><span>:</span> <span>"world"</span><span>,</span> <span>"from"</span><span>:</span> <span>"foo"</span><span>})</span><span>if</span> <span>__name__</span> <span>==</span> <span>'__main__'</span><span>:</span><span>app</span><span>.</span><span>run</span><span>(</span><span>debug</span><span>=</span><span>True</span><span>)</span><span>from</span> <span>flask</span> <span>import</span> <span>Flask</span><span>,</span> <span>jsonify</span> <span>app</span> <span>=</span> <span>Flask</span><span>(</span><span>__name__</span><span>)</span> <span>@</span><span>app</span><span>.</span><span>route</span><span>(</span><span>"/"</span><span>)</span> <span>def</span> <span>index</span><span>():</span> <span>return</span> <span>jsonify</span><span>({</span><span>"hello"</span><span>:</span> <span>"world"</span><span>,</span> <span>"from"</span><span>:</span> <span>"index"</span><span>})</span> <span>@</span><span>app</span><span>.</span><span>route</span><span>(</span><span>"/foo"</span><span>)</span> <span>def</span> <span>foo</span><span>():</span> <span>return</span> <span>jsonify</span><span>({</span><span>"hello"</span><span>:</span> <span>"world"</span><span>,</span> <span>"from"</span><span>:</span> <span>"foo"</span><span>})</span> <span>if</span> <span>__name__</span> <span>==</span> <span>'__main__'</span><span>:</span> <span>app</span><span>.</span><span>run</span><span>(</span><span>debug</span><span>=</span><span>True</span><span>)</span>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:
<span>from</span> <span>app</span> <span>import</span> <span>app</span> <span>as</span> <span>application</span><span>if</span> <span>__name__</span> <span>==</span> <span>"__main__"</span><span>:</span><span>application</span><span>.</span><span>run</span><span>()</span><span>from</span> <span>app</span> <span>import</span> <span>app</span> <span>as</span> <span>application</span> <span>if</span> <span>__name__</span> <span>==</span> <span>"__main__"</span><span>:</span> <span>application</span><span>.</span><span>run</span><span>()</span>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<span>()</span>:assert 42 <span>==</span> 42def test_42<span>()</span>: assert 42 <span>==</span> 42def 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 <span>--version</span>eb <span>--version</span>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:
<span>export </span><span>REGION</span><span>=</span>eu-west-1<span>export </span><span>PYTHON_VERSION</span><span>=</span>python-3.6 <span># latest version supported as of January 2020</span>eb init <span>-p</span> <span>$PYTHON_VERSION</span> <span>-r</span> <span>$REGION</span> <span>$PROJECT_NAME</span><span>export </span><span>REGION</span><span>=</span>eu-west-1 <span>export </span><span>PYTHON_VERSION</span><span>=</span>python-3.6 <span># latest version supported as of January 2020</span> eb init <span>-p</span> <span>$PYTHON_VERSION</span> <span>-r</span> <span>$REGION</span> <span>$PROJECT_NAME</span>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:
<span># default is application.py</span><span># alternative way to configure this -> eb config and then set manually</span><span>mkdir</span> .ebextensions<span>echo</span> <span>"option_settings: - namespace: aws:elasticbeanstalk:container:python option_name: WSGIPath value: wsgi.py"</span> <span>></span> .ebextensions/wsgi.config<span># default is application.py</span> <span># alternative way to configure this -> eb config and then set manually</span> <span>mkdir</span> .ebextensions <span>echo</span> <span>"option_settings: - namespace: aws:elasticbeanstalk:container:python option_name: WSGIPath value: wsgi.py"</span> <span>></span> .ebextensions/wsgi.config# 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 initgit add <span>.</span>git commit <span>-m</span> <span>"First commit"</span>git init git add <span>.</span> git commit <span>-m</span> <span>"First commit"</span>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:
<span>export </span><span>ENVIRONMENT_NAME</span><span>=</span><span>test</span> <span># for example: "test" or "live"</span><span>export </span><span>INSTANCE_TYPE</span><span>=</span>t2.nano <span># specify the instance type</span>eb create <span>$ENVIRONMENT_NAME</span>-<span>$PROJECT_NAME</span> <span>--single</span> <span>-i</span> <span>$INSTANCE_TYPE</span>eb use <span>$ENVIRONMENT_NAME</span>-<span>$PROJECT_NAME</span><span>export </span><span>ENVIRONMENT_NAME</span><span>=</span><span>test</span> <span># for example: "test" or "live"</span> <span>export </span><span>INSTANCE_TYPE</span><span>=</span>t2.nano <span># specify the instance type</span> eb create <span>$ENVIRONMENT_NAME</span>-<span>$PROJECT_NAME</span> <span>--single</span> <span>-i</span> <span>$INSTANCE_TYPE</span> eb use <span>$ENVIRONMENT_NAME</span>-<span>$PROJECT_NAME</span>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 deployeb deployeb 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.gitgit push <span>-u</span> origin mastergit remote add origin https://github.com/owner/repo.git git push <span>-u</span> origin mastergit 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 <span>--version</span>travis <span>--version</span>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 <span>install </span>travisgem <span>install </span>travisgem 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 <span>whoami</span> <span>--pro</span>travis <span>whoami</span> <span>--pro</span>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 <span>--pro</span>travis login <span>--pro</span>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
:
<span>language</span><span>:</span> <span>python</span><span>python</span><span>:</span><span>-</span> <span>3.6</span><span>before_install</span><span>:</span><span>-</span> <span>python --version</span><span>-</span> <span>pip install -U pip</span><span>-</span> <span>pip install -U pytest pytest-cov</span><span>-</span> <span>pip install codecov</span><span>script</span><span>:</span> <span>pytest</span><span>after_success</span><span>:</span><span>-</span> <span>pytest --cov=./</span><span>-</span> <span>codecov</span><span>deploy</span><span>:</span><span>provider</span><span>:</span> <span>elasticbeanstalk</span><span>access_key_id</span><span>:</span><span>secure</span><span>:</span> <span>$AWS_ACCESS_KEY_ID</span><span>secret_access_key</span><span>:</span><span>secure</span><span>:</span> <span>$AWS_ACCESS_KEY_SECRET</span><span>region</span><span>:</span> <span>$AWS_REGION</span><span>app</span><span>:</span> <span>$AWS_EB_APPLICATION</span><span>env</span><span>:</span> <span>$AWS_EB_ENVIRONMENT</span><span>bucket_name</span><span>:</span> <span>$AWS_EB_S3_BUCKET</span><span>language</span><span>:</span> <span>python</span> <span>python</span><span>:</span> <span>-</span> <span>3.6</span> <span>before_install</span><span>:</span> <span>-</span> <span>python --version</span> <span>-</span> <span>pip install -U pip</span> <span>-</span> <span>pip install -U pytest pytest-cov</span> <span>-</span> <span>pip install codecov</span> <span>script</span><span>:</span> <span>pytest</span> <span>after_success</span><span>:</span> <span>-</span> <span>pytest --cov=./</span> <span>-</span> <span>codecov</span> <span>deploy</span><span>:</span> <span>provider</span><span>:</span> <span>elasticbeanstalk</span> <span>access_key_id</span><span>:</span> <span>secure</span><span>:</span> <span>$AWS_ACCESS_KEY_ID</span> <span>secret_access_key</span><span>:</span> <span>secure</span><span>:</span> <span>$AWS_ACCESS_KEY_SECRET</span> <span>region</span><span>:</span> <span>$AWS_REGION</span> <span>app</span><span>:</span> <span>$AWS_EB_APPLICATION</span> <span>env</span><span>:</span> <span>$AWS_EB_ENVIRONMENT</span> <span>bucket_name</span><span>:</span> <span>$AWS_EB_S3_BUCKET</span>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 <span>--pro</span> <span>AWS_ACCESS_KEY_ID</span><span>=</span><span>"YOUR_KEY_ID_HERE"</span> <span>--add</span>travis encrypt <span>--pro</span> <span>AWS_ACCESS_KEY_SECRET</span><span>=</span><span>"YOUR_KEY_SECRET_HERE"</span> <span>--add</span>travis encrypt <span>--pro</span> <span>AWS_REGION</span><span>=</span><span>$REGION</span> <span>--add</span>travis encrypt <span>--pro</span> <span>AWS_EB_APPLICATION</span><span>=</span><span>$PROJECT_NAME</span> <span>--add</span>travis encrypt <span>--pro</span> <span>AWS_EB_ENVIRONMENT</span><span>=</span><span>$ENVIRONMENT_NAME</span>-<span>$PROJECT_NAME</span> <span>--add</span>travis encrypt <span>--pro</span> <span>AWS_EB_S3_BUCKET</span><span>=</span><span>"YOUR_BUCKET_HERE"</span> <span>--add</span> <span># change this as needed – a new bucket will be automatically created if it does not yet exist </span>travis encrypt <span>--pro</span> <span>AWS_ACCESS_KEY_ID</span><span>=</span><span>"YOUR_KEY_ID_HERE"</span> <span>--add</span> travis encrypt <span>--pro</span> <span>AWS_ACCESS_KEY_SECRET</span><span>=</span><span>"YOUR_KEY_SECRET_HERE"</span> <span>--add</span> travis encrypt <span>--pro</span> <span>AWS_REGION</span><span>=</span><span>$REGION</span> <span>--add</span> travis encrypt <span>--pro</span> <span>AWS_EB_APPLICATION</span><span>=</span><span>$PROJECT_NAME</span> <span>--add</span> travis encrypt <span>--pro</span> <span>AWS_EB_ENVIRONMENT</span><span>=</span><span>$ENVIRONMENT_NAME</span>-<span>$PROJECT_NAME</span> <span>--add</span> travis encrypt <span>--pro</span> <span>AWS_EB_S3_BUCKET</span><span>=</span><span>"YOUR_BUCKET_HERE"</span> <span>--add</span> <span># change this as needed – a new bucket will be automatically created if it does not yet exist </span>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.
原文链接:Deploy Flask Apps to AWS Elastic Beanstalk using Travis CI
暂无评论内容