From GitLab to Heroku with Docker
Mario García
Posted on April 27, 2020
After writing the code of the project and testing it in a local development environment, you must create a GitLab repository and upload the code and configure to deploy to Heroku.
.gitignore
Add a .gitignore
file to your repository so the temporary files and directories generated during the building process don't be uploaded to GitLab. You can use the templates provided by GitLab or generate this file at gitignore.io.
Just type in the search bar the technologies you're using for the project and it will generate the file for you.
The file will look as follows:
# Created by https://www.gitignore.io/api/rust,python
# Edit at https://www.gitignore.io/?templates=rust,python
### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# pyenv
.python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# Mr Developer
.mr.developer.cfg
.project
.pydevproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
### Rust ###
# Generated by Cargo
# will have compiled files and executables
/target/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk
# End of https://www.gitignore.io/api/rust,python
Heroku
Before configuring the repository, you should create a Heroku app.
Go to dashboard.heroku.com/account and copy the API Key.
Dockerfile
Create a new Dockerfile in your repository. it will have the following content:
FROM docker.io/username/rust-python:latest
WORKDIR /home/admin
USER admin
RUN mkdir app
WORKDIR app
RUN env PYTHON_CONFIGURE_OPTS='--enable-shared' pyenv install 3.7.7
ENV LD_LIBRARY_PATH $HOME/.pyenv/versions/3.7.7/lib/
RUN pyenv global 3.7.7
RUN pyenv rehash
RUN rustup override set nightly
COPY pyproject.toml ./
RUN poetry config virtualenvs.create false \
&& poetry lock \
&& poetry export --without-hashes -f requirements.txt --dev \
| poetry run pip install -r /dev/stdin \
&& poetry debug
COPY . ./
RUN poetry install --no-interaction \
&& cargo build --release
CMD ["sh", "run"]
According to this issue, the container registry address (docker.io) must be specified before the name of the base image, for buildah to pull the image correctly from Docker Hub.
The base image used for the project will be the one created before and available from username/rust-python:latest
.
Then set the WORKDIR
to /home/admin
and the USER
to admin
. Create the app
directory and set it as WORKDIR
.
Install Python 3.7.7 with shared libraries and set the LD_LIBRARY_PATH
environment variable.
Specify the version of Python and Rust to be used.
Copy the pyproject.toml
file. Poetry is being used to manage the dependencies of Python but no virtual environment will be created.
Then lock the dependencies and export to a requirements.txt
file and install them using pip
, this is done to generate a cache of them.
Then install Python and Rust dependencies of the project, and build the project for production.
When the Docker image being created is run, the bash script named run
is executed.
The run
bash script has the following content, so that Heroku knows how to run the app:
ROCKET_PORT=$PORT ROCKET_ENV=prod ./target/release/rust-python-demo
The variable $PORT
is set by Heroku as it dynamically chooses a port for the app, this value is assigned to ROCKET_PORT
. Then the environment is set to production with ROCKET_ENV=prod
and the binary of the app is run.
GitLab CI
Add the value of the API Key of Heroku that you copied before to the settings of the repository by following the steps bellow:
- Go to
Settings
>CI/CD
. - In the
Variables
section click onExpand
. - Add the variable
HEROKU_API_KEY
and assign the value copied before.
Then add the .gitlab-ci.yml
file, this is the configuration file of the Continuous Integration system provided by GitLab.
For this I just follow Alessandro Diaferia's tutorial on Continuous Deployment With GitLab, Docker And Heroku.
The file will look as follows:
stages:
- build
- release
build_image:
only:
- master
image: registry.gitlab.com/majorhayden/container-buildah
stage: build
variables:
STORAGE_DRIVER: "vfs"
BUILDAH_FORMAT: "docker"
before_script:
- dnf install -y nodejs
- curl https://cli-assets.heroku.com/install.sh | sh
- sed -i '/^mountopt =.*/d' /etc/containers/storage.conf
script:
- buildah bud --iidfile iidfile -t rust-python-demo:$CI_COMMIT_SHORT_SHA .
- buildah push --creds=_:$(heroku auth:token) $(cat iidfile) registry.heroku.com/heroku-app-name /web
release:
only:
- master
image: node:10.17-alpine
stage: release
before_script:
- apk add curl bash
- curl https://cli-assets.heroku.com/install.sh | sh
script:
- heroku container:release -a heroku-app-name web
It will only have two stages, build_image
, where the Docker image is built using the Dockerfile created previously and added to the registry of Heroku, and release
, where the image will be pushed to the Heroku app.
Just replace registry.heroku.com/heroku-app-name/web
and heroku container:release -a heroku-app-name web
with the name of the app you created on Heroku.
If everything goes well, the jobs of the pipeline will succeed and your app is deployed to Heroku.
You can go to rust-python-demo.herokuapp.com to see a live demo running.
Posted on April 27, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 29, 2024