Deploy Flask app to Heroku with Docker
This story will show how to install deploy a simple Flask app to Heroku via Docker and Gitlab CI.
Prerequisites
I'm assuming that you are familiar with the basics of Docker, Heroku, Flask and Gitlab.
Also, you have:
1 Heroku account with an app created
2 Heroku cli installed locally
3 Gitlab account with a repository created (clean)
4 Private gitlab runner to execute Docker builds
5 Docker and docker-compose installed locally
(I'm using ubuntu 19.04)
Setup Flask application
Setup virtualenv
Create a python3 virtualenv and install Flask and gunicorn:
$ virtualenv venv -p python3
$ source venv/bin/activate
$ pip install flask gunicorn
$ pip freeze > requirements.txt
(If you are on ubuntu, you might need to enter the requirementes.txt file and remove the pkg-resources==0.0.0
from that file)
Create Flask application
In your gitlab repository, create a main.py file:
from flask import Flask, escape, request
app = Flask(__name__)
@app.route('/')
def hello():
name = request.args.get("name", "World")
return f'Hello, {escape(name)}!'
You can test it locally by running:
$ export FLASK_APP=main.py
$ flask run
Create docker container and run via docker-compose
Create the Dockerfile and add this:
FROM python:3.8-alpine
WORKDIR /code
# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1 # Dont write pyc files
ENV PYTHONUNBUFFERED 1 # Dont buffer stdout and stderr
ENV FLASK_APP main.py
ENV FLASK_RUN_HOST 0.0.0.0
RUN apk add --no-cache gcc musl-dev linux-headers
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
COPY . .
CMD ["flask", "run"]
and create the docker-compose.yml file like this:
version: '3.7'
services:
flaskapp:
build: .
ports:
- "5000:5000"
Now, you can run your application by typing:
$ docker-compose up
Heroku container registry (deploy the container directly)
(much of this comes from https://devcenter.heroku.com/articles/container-registry-and-runtime)
Login to the heroku container registry:
$ heroku container:login
Build and push your docker image to the registry:
$ heroku container:push web --app YOUR_APP_NAME
After this, the docker image will be in the heroku docker registry and you can now push it to your dymo:
$ heroku container:release web --app YOUR_APP_NAME
Gitlab CI
Create a .gitlab-ci file with this:
variables:
HEROKU_APP_NAME: YOUR_APP_NAME
stages:
- deploy
deploy:
stage: deploy
tags:
- deploy
script:
- apt-get update -qy
- apt-get install -y ruby-dev
- gem install dpl
- dpl --provider=heroku --app=$HEROKU_APP_NAME --api-key=$HEROKU_AUTH_TOKEN
Under your Gitlab settings (settings/CI CD/) 'runners' and 'variables' you need to do two things. Under 'runner' you should enable the private docker runner, and under 'variables' you should set the HEROKU_AUTH_TOKEN
to the value you get when running: heroku auth:token
Also notes, the gitlab runner we are enabling should be tagged with 'deploy'. The .gitlab-ci file specifies 'under 'tags:' which type of runner to use for this particular stage, in this case 'deploy'. If this is not specified, a runner without the correct dependencies might be used and the deployment fail.
Heroku yml file
We will also create a heroku.yml file to tell Heroku what to do with our Dockerfile
build:
docker:
web: Dockerfile
run:
web: gunicorn main:app
If we include a 'run' section in the heroku.yml file, then the CMD section of the Dockerfile is ignored.
Push to Gitlab and observe deployment in action
Now, push to gitlab:
git push origin master
And if all goes well, your application will be deployed to Heroku