Flawless Releases With Docker
Tags: mongodb, docker, devops, python
Published: 2019-10-24 16:01:56

---
description: How to do continuous deployments of a flask app to Heroku 
---

*This is a streamlined remix of my original [Flask and
Heroku](https://semaphoreci.com/community/tutorials/continuous-deployment-of-a-python-flask-application-with-docker-and-semaphore) tutorial.*

Docker containers are such a great way of packaging applications—once we get our app in Docker, we get a lot of benefits:

- ⚖️ Consistency: prod and dev are equal.
- 🚗 Portability: fewer dependencies with the underlying OS; the same image can be deployed on many cloud provider.
- 🗺  Divide and conquer: distribute services among different containers.
- ⚙️ Low overhead: near-native speeds.

However, Docker introduces a new variable to the equation: the code must be baked into an image, then correctly deployed. Also, testing can be more challenging. Here is where [CI/CD](https://semaphoreci.com/cicd) can be of great value to us: by automating all those tasks and giving us a fast, reliable environment to work with.

## Here’s the Plan

We already have a ready-to-use Python Flask demo. The app is a simple task manager, written for [Flask](https://palletsprojects.com/p/flask/); it uses MongoDB as the database backend.

The app is split in two: one container for the database and one for the web app. The CI/CD workflow has to:

1. Build the Docker images and push them to Docker Hub.
3. Test the app inside the container.
4. Deploy it to Heroku as a [Docker Dyno](https://devcenter.heroku.com/articles/container-registry-and-runtime).

We’ll be using the following tools:

- [Heroku CLI](https://devcenter.heroku.com/articles/heroku-cli).
- [Semaphore CLI](https://docs.semaphoreci.com/article/53-sem-reference).

To get started, fork the [semaphore-demo-python-flask](https://github.com/semaphoreci-demos/semaphore-demo-python-flask) repository and clone it. Then, add the project to Semaphore:

``` bash
$ cd semaphore-demo-python-flask 
$ sem init
```

Create an empty application; app names are unique, so be creative 🐴.

``` bash
$ heroku create bright-horses
Creating app... done, ⬢ bright-horses
```

The next thing is setting up the database. Here you can make a choice between two offerings:

- [MongoLab](https://elements.heroku.com/addons/mongolab): super easy to set up, integrates nicely with Heroku, and the sandbox offers 500MB free to use. You need to add a credit card in Heroku to use it, though. I’ll use this alternative.
- [MongoDB Atlas](https://www.mongodb.com/cloud/atlas): the free plan offers 500MB in a three server cluster, which is excellent for production. The setup, however, is more involved. I cover it extensively on the [original tutorial](https://semaphoreci.com/community/tutorials/continuous-deployment-of-a-python-flask-application-with-docker-and-semaphore)

![Which on will you choose?](https://thepracticaldev.s3.amazonaws.com/i/oik8nutzhydetxtoey8x.jpg)
  
MongoLab can be installed in your app with a single command:

``` bash
$ heroku addons:create mongolab:sandbox --app YOUR_APP_NAME
```

## The Way Pipelines Work

A well designed [Continuous Integration](https://semaphoreci.com/continuous-integration) (CI) workflow will help us to:

- ⏲ Spend less time testing and deploying.
- 🤖 Get 100% automated testing.
- 😷 Avoid it-works-on-my-machine syndrome.
    

Shall we take a quick look at the CI pipeline? It can be found at `.semaphore/semaphore.yml`.

[Pipelines](https://semaphoreci.com/blog/cicd-pipeline) begin with the **agent** and **name**. Agents are the [machines](https://docs.semaphoreci.com/article/20-machine-types). where our jobs run.

``` yaml
version: v1.0
name: Semaphore Python / Flask / Docker Example Pipeline
agent:
  machine:    
    type: e1-standard-2
    os_image: ubuntu1804
``` 

A Block defines actions for the pipeline. Each block has a single task, and each task can have one or more jobs. Jobs within a block run concurrently, each one in its own fully isolated machine. Once all jobs in a block complete, the next block begins.

The pipeline starts with the “Build” block:

1. [secret](https://docs.semaphoreci.com/article/66-environment-variables-and-secrets) imports the `$DOCKER_*` variables from the vault.
2. [checkout](https://docs.semaphoreci.com/article/54-toolbox-reference#checkout) clones GitHub repository.
3. Docker login is required for pushing the image to Docker Hub.
4. docker-compose builds the image…
5. …which is tagged, and finally pushed to the registry.

``` yaml
blocks:
  - name: Build
    task:
      secrets:
        - name: pyflask-semaphore
      jobs:
      - name: Docker build
        commands:
          - echo "${DOCKER_PASSWORD}" | docker login -u "${DOCKER_USERNAME}" --password-stdin
          - checkout
          - docker-compose build
          - docker tag pyflasksemaphore:latest "$DOCKER_USERNAME"/pyflasksemaphore:$SEMAPHORE_WORKFLOW_ID
          - docker push "$DOCKER_USERNAME"/pyflasksemaphore:$SEMAPHORE_WORKFLOW_ID
          - docker images
```

The last block spins up the containers for integration testing. The [prologue](https://docs.semaphoreci.com/article/50-pipeline-yaml#prologue) is executed before every job in a block. In this case, the prologue pulls the image and starts the app:

``` yaml
  - name: Run & Test Docker image
    task:
      secrets:
        - name: pyflask-semaphore
      prologue:
        commands:
          - echo "${DOCKER_PASSWORD}" | docker login -u "${DOCKER_USERNAME}" --password-stdin
          - checkout
          - docker pull "$DOCKER_USERNAME"/pyflasksemaphore
          - docker-compose up -d
      jobs:
      - name: Check Running Images
        commands:
          - docker ps
      - name: Run Unit test
        commands:
          - docker exec -it semaphore-pyflask-docker_flasksemaphore_1 python -m unittest
```

The final section specifies how the workflow continues. At this point, we can chain multiple pipelines with promotions to create complex workflows. [Promotions](https://docs.semaphoreci.com/article/50-pipeline-yaml#prologue) can be manual or automatic. In this case, we have a manual connection to the deployment pipeline:

``` yaml
promotions:
  - name: Deploy to Heroku
    pipeline_file: deploy-heroku.yml
```

### The Deploy Pipeline

The last pipeline has the “Deploy to Heroku” block. Open the `.semaphore/deploy-heroku.yml` file; we have to make some changes before using it:

1. Remove the “`atlas-mongodb`” line in the secret section. We won’t be needing that variable.
2. Remove the “`heroku set DB=$MONGODB_URI`” line. Heroku integration will set its Mongo environment for us.
3. Set the `HEROKU_APP` variable to your app name.

This is the end result:

``` yaml
blocks:
  - name: Deploy to Heroku
    task:
      secrets:
        - name: pyflask-semaphore
        - name: heroku
      env_vars:
        - name: HEROKU_APP
          value: YOUR_APP_NAME_GOES_HERE
      jobs:
        - name: Deploy
          commands:
            - checkout
            - echo "${DOCKER_PASSWORD}" | docker login -u "${DOCKER_USERNAME}" --password-stdin
            - docker pull "$DOCKER_USERNAME"/pyflasksemaphore:$SEMAPHORE_WORKFLOW_ID
            - heroku container:login
            - docker tag "$DOCKER_USERNAME"/pyflasksemaphore:$SEMAPHORE_WORKFLOW_ID registry.heroku.com/$HEROKU_APP/web
            - docker push registry.heroku.com/$HEROKU_APP/web
            - heroku stack:set container --app $HEROKU_APP
            - heroku container:release web --app $HEROKU_APP
```

## Ready to Go

Create an authorization token on Heroku; this will allow Semaphore to connect with your account:

- Go to: Account Settings > Application > Create Authorization.

Copy the generated token and store it securely on Semaphore as a secret:

``` bash
$ sem create secret heroku -e HEROKU_API_KEY=YOUR_API_TOKEN
```

Create a second secret to store your [Docker Hub](https://hub.docker.com) username and password:

``` bash
$ sem create secret pyflask-semaphore \
    -e DOCKER_USERNAME=YOUR_DOCKER_HUB_USERNAME \
    -e DOCKER_PASSWORD=YOUR_DOCKER_HUB_PASSWORD
```

Push the changes to GitHub to get the CI/CD going:

``` bash
$ git add .semaphore/*
$ git commit -m "deploy to Heroku"
$ git push origin master
```

The workflow starts automatically on every push:

![CI Pipeline](https://thepracticaldev.s3.amazonaws.com/i/0s538pe1zy97xk3smf1u.png)

Once you are satisfied, push the **Promote** button:

![CI/CD Pipelines](https://thepracticaldev.s3.amazonaws.com/i/1t7diizna5fb1rq6xqin.png)

In a few seconds, the app should be online at http://APPNAME.herokuapp.com

![Flask App](https://thepracticaldev.s3.amazonaws.com/i/msdjx25pzkgqxrndh154.png)


## The Real Hero-Ku Is You

We learned how to use Semaphore to create a pipeline that automates running the tests and the necessary build commands, as well as to deploy the application to Heroku.

Want to deliver continuously your applications made with Docker? Check out Semaphore’s [Docker platform](https://semaphoreci.com/product/docker).

![The Real Hero](https://thepracticaldev.s3.amazonaws.com/i/8lbjxnhfjuajc6bbflgy.jpg)

Did you find the post useful? Hit those ❤️ and 🦄, and leave a comment below. 

Thanks for reading!