Deploy Flask app to Heroku with Docker and Gitlab

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

References

https://github.com/travis-ci/dpl#heroku-api

(testdriven.io) Deploying Django to Heroku With Docker