Using Docker build args to customize the build
I wasn't really happy with the current approaches of dealing with different Dockerfiles and docker-compose.yaml files for development and production containers. I don't really see the point of managing multiple configuration files, building a few intermediate containers when the only difference between a development image and a production image is that the code is copied into the image during build. Adding files on every build is also not an ideal solution as you could potentially ship an old version of the application when you miss running a docker build after you made your final changes.
By accident I ran across this post on askubuntu.com which covers the usage of build args to pass the build context as parameter while building the image. By defining a build argument you can do the following in your Dockerfile:
ARG package
ADD $package /var/www/html
This would take the files passed to the docker build command and store them in the /var/www/html folder while building the image. Unfortunately, if a build argument is not passed as an command-line argument, the build outputs an error. Luckily there is an easy fix for this: Simply define a default value for the build argument. In the example below we set the default to .gitignore:
ARG package=.gitignore
ADD $package /var/www/html
This basically means that whenever the container is built e.g. via docker build or via docker-compose, the local .gitignore file will be added to the image. Since this is usually a very small file, adding the file to the image is very fast and will not occupy a lot of disk space in the image.
During the container build for staging and production the following happens in our GitLab CI pipeline:
tar cvfz /tmp/package.tgz --exclude=data --exclude=docker --exclude=.git .
mv /tmp/package.tgz $CI_PROJECT_DIR && \
docker build -t customer/project --build-arg package=package.tgz .
docker tag customer/project:latest registry.loc/customer/project:latest
docker push registry.loc/customer/project:latest
rm $CI_PROJECT_DIR/package.tgz
We simply package the whole project - excluding folders which we do not need inside the container - and pass the tarred package via the build arg to the docker build command. After the build we tag and push the image to our local docker registry and we are done.