Docker for Inventree

InvenTree is an open-source Inventory Management System that I’m currently using as a way to keep track of console/peripheral revisions down to the component level for Kariohm/Keep Dreaming Project.

Docker Image

To use Inventree with my server setup I needed to create a Docker image for it and set it up to be used in a production environment. Since I also needed to do some work on Inventree itself I also set it up so that I could use it for development.

The Docker image is based on Alpine and is a two-stage build. The first stage gets all the build dependencies for the python packages which are installed into a python virtual environment, this can also be used as a Development container. The second stage creates the production image which copies the python virtual environment from the first stage and runs InvenTree from there. I use a python virtual environment for the Docker image as it simplifies moving the built python packages from the first stage into the second stage.

The Docker image uses my usual setup of scripts, an entrypoint that grabs environment variables to set up the Django config file that also calls a script for using Docker secrets and another for waiting on MariaDB to start before running Inventree. The entrypoint also generates a secret key for Django using a little awk script (I don’t remember why I used awk but I’m sure there was a reason).

BEGIN {
    srand();
    chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
    s = "";
    for(i=0;i<50;i++) {
        s = s "" substr(chars, int(rand()*62), 1);
    }
    print s
}

With Inventree under active development, I decided to have Docker Hub build a new image whenever a commit is made that could be used for testing. I have Kairohm’s Phabricator instance mirroring Inventree from GitHub and whenever there’s a new commit Phabricator fires off a webhook to Docker Hub triggering an automated build.

There’s a bit of setup to support both these automated builds and the release ones that I’ve tested. This is largely done using build hooks. The build hook uses information from the Inventree Docker repo to set up some build arguments, this will grab the tag from the repo for the release versions which is then used to pull the relevant version from the Inventree repo.

docker build \
  --build-arg VERSION=$(git describe --tags --always) \
  --build-arg COMMIT=$(git rev-parse HEAD) \
  --build-arg URL=$(git config --get remote.origin.url) \
  --build-arg BRANCH=$(git rev-parse --abbrev-ref HEAD) \
  --build-arg DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") \
  --build-arg DOCKER_TAG=$DOCKER_TAG \
-t $IMAGE_NAME .

However, when Docker Hub runs the automated build it uses the latest Docker Tag which causes the build to use the newest commit on InvenTree’s master branch. Docker Hub will tag this as zeigren/inventree:latest but that doesn’t give any information about what version of InvenTree was built. To add information about the specific InvenTree commit used in the Docker image I use a post-hook to push a Docker image tag with that information.

The commit image tag was a bit tricky to set up as the post hook runs outside of the image so can’t reference the InvenTree git repository that was pulled. Instead, it relies on using git ls-remote to get that information from GitHub. Newer versions of git make this easier to do, however, Docker Hub uses an old version of Amazon Linux 2 for building Docker images that only has git 2.7.4. On top of that InvenTree has mixed using version numbers with and without a “v” prefix which makes figuring out the latest version a bit more difficult as git ls-remote is unable to get timestamps for any tags or commits. Thankfully they’ve dropped the “v” prefix in the last few versions so I can just ignore any tag with a “v” prefix. This is what the post push currently looks like:

if [ "$DOCKER_TAG" = "latest" ]; then

INVENTREE_MASTER_COMMIT=$(git ls-remote https://github.com/inventree/InvenTree.git master)
COMMIT_STUB=$(echo $INVENTREE_MASTER_COMMIT | cut -c1-7)
INVENTREE_TAG=$(git ls-remote --tags https://github.com/inventree/InvenTree.git | cut --delimiter='/' --fields=3 | sed '/v/d' | sort --version-sort | tail --lines=1)
GIT_VERSION=$(git --version)

echo "INVENTREE_MASTER_COMMIT" $INVENTREE_MASTER_COMMIT
echo "COMMIT_STUB" $COMMIT_STUB
echo "INVENTREE_TAG" $INVENTREE_TAG
echo "DOCKER_TAG" $DOCKER_TAG
echo "GIT_VERSION" $GIT_VERSION
echo "[***] Adding labels"

docker tag $IMAGE_NAME $DOCKER_REPO:$INVENTREE_TAG-$COMMIT_STUB
docker push $DOCKER_REPO:$INVENTREE_TAG-$COMMIT_STUB ; fi

This creates a tag like zeigren/inventree:0.1.7-5a5e76e, so it has the last version tag along with a stub of the specific commit that was used in the build.

Docker Stack

The stack is pretty straight forward there is the InvenTree container which is built on Django and uses Gunicorn as a WSGI HTTP Server, NGINX container which acts as an HTTP proxy and serves up static files, and MariaDB container for the database. I made some examples for using NGINX with and without SSL termination, using Docker Compose for production and development, and using Docker Swarm with or without Traefik as a reverse proxy.

Phabricator

GitHub