- Consistency: Production and development environments are equal.
- Portability: fewer dependencies with the underlying OS; the same image can be deployed on any cloud provider.
- Low overhead: better performance than virtual machines.
- Divide and conquer: distribute services among different containers.
- Continuous integration: build and test the app image.
- Continuous deployment: send the image to Heroku to run online
Enter the Application
- Build a Docker image with our application.
- Push the image to Docker Hub.
- Test the application inside the container.
- Deploy it to Heroku.
Continuous Integration
- Spend less time testing and deploying.
- Get 100% automated testing.
- Avoid it-works-on-my-machine syndrome.
repository. The best way of storing sensitive data is by using Secrets, which are automatically encrypted and made available to jobs when called:
- Under Configuration, go to Secrets.
- Click the Create New Secret button.
- Name your secret
and type your Docker Hub credentials:pyflask-semaphore
- mongodb: references a public MongoDB image.
- flasksemaphore: the custom-built app container.
Integrate
$ touch some_file
$ git add some_file
$ git commit -m "first run of the integration pipeline"
$ git push
good news is that the integration pipeline is all green. If everything
went according to plan you should have a new image in your Docker Repository.
Building an Image
flask.Dockerfile
. Take a look at the contents:FROM python:3.7
ADD . ./opt/
WORKDIR /opt/
EXPOSE 5000
RUN pip install --upgrade pip
RUN pip install -r requirements.txt
CMD ["python","run.py"]
ADD
the app files into the /opt dir. RUN
invokes pip, Python's package manager, to install all the app dependencies. The final CMD
defines how the app starts.version: '3.5'
services:
mongodb:
image: mongo:3.4.20
container_name: "mongodb"
ports:
- 27017:27017
command: mongod --smallfiles --logpath=/dev/null
flasksemaphore:
image: pyflasksemaphore
container_name: semaphore-pyflask-docker_flasksemaphore_1
build:
context: .
dockerfile: ./flask.Dockerfile
ports:
- "5000:5000"
volumes:
- .:/opt/
environment:
- DB=mongodb://mongodb:27017/tasks
- PORT=5000
depends_on:
- mongodb
docker-compose up
The CI Pipeline
version: v1.0
name: Semaphore Python / Flask / Docker Example Pipeline
agent:
machine:
type: e1-standard-2
os_image: ubuntu1804
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:latest
- docker tag pyflasksemaphore:latest "$DOCKER_USERNAME"/pyflasksemaphore:$SEMAPHORE_WORKFLOW_ID
- docker push "$DOCKER_USERNAME"/pyflasksemaphore:latest
- docker push "$DOCKER_USERNAME"/pyflasksemaphore:$SEMAPHORE_WORKFLOW_ID
- docker images
- 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
promotions:
- name: Deploy to Heroku
pipeline_file: deploy-heroku.yml
auto_promote_on:
- result: passed
- secrets imports the Docker Hub variables.
- checkout clones GitHub repository.
- docker login is required for pushing the image to Docker Hub.
- docker-compose builds the image…
- …which is tagged, and finally pushed to the registry.
- Run unit test: start a test script inside the container.
- Check running images: lists docker containers running.
promotions:
- name: Deploy to Heroku
pipeline_file: deploy-heroku.yml
Continuous Delivery
- Heroku: to run the application.
- MongoDB Atlas: to get a managed MongoDB database.
Heroku
- Click on your account profile, on the top right corner.
- Select Account Settings.
- Go to the Applications tab.
- Press the Create Authorization button.
- Set the description as:
semaphore-demo-python-flask
- Set your application name, or leave blank for a random one.
- Select your preferred zone: US or Europe.
- Sign up for a MongoDB Atlas Account
- Select AWS as a provider.
- Choose the region that matches your Heroku app. For the US
, and for Europeus-east-1
eu-west-1
- On Cluster Tier, select the M0 Sandbox
- You may set a name to describe your cluster. You can leave the rest of the settings alone.
- Click on Create cluster. Give it a few minutes to provision.
- On the left side navigation bar, open Security.
- Click on the +Add New User button.
- Add the user
. Choose a secure password and set the privileges to "Read and write any database"semaphore-demo-python-flask
- Back in Security, select the IP Whitelist tab.
- Click on the +Add IP Address button.
- Choose Allow access from anywhere and Confirm.
- Tag the image with Heroku Registry URL.
- Send the URI variable for the MongoDB connection.
- Set the stack mode to container.
- Release: gets the app started.
heroku
to store the authorization token:MongoDB Atlas
Meanwhile, MongoDB Atlas offers a 500MB cluster for free. Not bad, not bad at all. However, the setup process is rather lengthy, so please bear with me:
mongodb-atlas
secret:The CD Pipeline
.semaphore/deploy-heroku.yml
version: v1.0
name: Deploy to Heroku
agent:
machine:
type: e1-standard-2
os_image: ubuntu1804
blocks:
- name: Deploy to Heroku
task:
secrets:
- name: mongodb-atlas
- name: pyflask-semaphore
- name: heroku
env_vars:
- name: HEROKU_APP
value: <YOUR_APP_NAME>
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 config:set DB="$MONGODB_URI"
- heroku stack:set container --app $HEROKU_APP
- heroku container:release web --app $HEROKU_APP
$HEROKU_APP
, which should point to your Heroku app name. Once you've done this, go ahead and replace the value with it.Deploy
$ git add .semaphore
$ git commit -m "ready to deploy!"
$ git push