Development Container Base Images
Today I want to introduce one of my repositories regarding Development Containers, something I have been using in my quest for stronger isolation and portability.
What Are Development Containers?
TL;DR - They are portable and reproducible development environments built on containers.
Quoting the project’s homepage:
“A development container (or dev container for short) allows you to use a container as a full-featured development environment. It can be used to run an application, to separate tools, libraries, or runtimes needed for working with a codebase, and to aid in continuous integration and testing. Dev containers can be run locally or remotely, in a private or public cloud, in a variety of supporting tools and editors.” - https://containers.dev/
Containers are great for deploying workloads, but this project took things to a new level by letting you develop inside a container.
My inspiration for using this came from the Flatpak project. I found the bootstrapping of a new flatpak for each environment a little more cumbersome than I would have liked, which led me to dev containers.
Dev containers are a good first step toward getting yourself used to a workflow where things are organized by where they need to be used. Why install unnecessary stuff on your main system when it is only needed during a specific interaction?
The Repository
The project includes numerous default images, but it doesn’t cover my exact use case. I wanted a very specific set of items installed into each image and, for that, I needed to create my own.
The repository consists of three images at the time of writing:
- Arch Linux
ghcr.io/t-c-l-o-u-d/devcontainer-base-images/arch-linux-devcontainer:latest
- Fedora
ghcr.io/t-c-l-o-u-d/devcontainer-base-images/fedora-devcontainer:latest
- Ubuntu LTS
ghcr.io/t-c-l-o-u-d/devcontainer-base-images/ubuntu-lts-devcontainer:latest
These images contain a common set of tools that I would need across nearly any repository. Right now, you won’t find things like python included because that isn’t applicable across all of my repositories. Rebuilds, aka updates, are handled through GitHub Actions. Every part of the process is auditable and automated.
Current list of packages: | Arch Linux | Fedora | Ubuntu LTS | | ————— | ——————— | ——————| | 7zip | 7z | 7zip | | bash-completion | bash-completion | bash-completion | | git | file | curl | | htop | git | file | | man-db | gzip | fonts-ibm-plex | | man-pages | htop | git | | openssh | ibm-plex-mono-fonts | gnu-which | | progress | man-db | htop | | rsync | rootfiles | man-db | | ttf-ibm-plex | rsync | manpages | | unrar | sudo | rsync | | unzip | tar | sudo | | xdg-utils | unrar | unrar-free | | . | unzip | unzip | | . | watch | xdg-utils | | . | which | . | | . | xdg-utils | . |
Feature parity between the images is a goal, but I can’t ensure it right now. I have recently transitioned from Fedora to Arch Linux, so the Arch Linux image is the most battle-tested.
Usage
All of these images are a starting point for my own development projects. Taking the python example from above, let’s take a look at how I integrated it into a project.
After copying Containerfile.template and devcontainer.json.template into my repository, I proceeded to edit them.
Starting with my Containerfile, I found the clearly labeled section to add my repository’s needs. It is just a long list of packages to install.
...
# =======================================
# repository specific commands start here
# =======================================
# install python tools
RUN pacman --sync --noconfirm \
autopep8 \
flake8 \
mpdecimal \
mypy \
python \
python-astroid \
python-autocommand \
python-black \
python-click \
python-colorama \
python-dill \
python-entrypoints \
python-flake8-black \
python-flake8-isort \
python-importlib-metadata \
python-isort \
python-jaraco.collections \
python-jaraco.context \
python-jaraco.functools \
python-jaraco.text \
python-mccabe \
python-more-itertools \
python-mypy_extensions \
python-orjson \
python-packaging \
python-pathspec \
python-platformdirs \
python-pycodestyle \
python-pyflakes \
python-pylint \
python-setuptools \
python-tomli \
python-tomlkit \
python-typing_extensions \
python-wheel \
python-zipp \
ruff \
yapf
# =====================================
# repository specific commands end here
# =====================================
...
The devcontainer.json is an easily editable JSON file, and I like that. Boring and simple are great for helping people unfamiliar with your project to quickly grasp how things come together and how they can effectively contribute. The notable thing here is that extensions and settings for VS Code are all contained within the container! This ensures that anyone else who clones this repository and uses the dev container ends up with an exact replica of my environment.
...
"settings": {
...
// repository specific settings go here
"python.analysis.reportExtraTelemetry": false
},
"extensions": [
"ms-azuretools.vscode-docker",
// repository specific extensions go here
"ms-python.autopep8",
"ms-python.flake8",
"ms-python.isort",
"ms-python.mypy-type-checker",
"ms-python.pylint",
"ms-python.python"
]
...
Once you have your Containerfile and devcontainer.json in a .devcontainer folder at the root of your repository, the rest is handled by VS Code. When you open the repository, VS Code detects the configuration and prompts you to reopen the project inside the container with a small popup. You can also trigger this manually from the command palette with “Dev Containers: Reopen in Container”.
From there, VS Code builds the container image, starts it, and connects to it. Your workspace is mounted inside the container, the extensions you specified are installed automatically, and your settings are applied. Within a few moments, you have a fully configured environment ready to go, no manual setup required.
Ideally, this will eliminate the “It works on my machine.” problem that plagues development teams.
Closing Thoughts
Thank you very much for reading. This has been the culmination of a lot of work on my part. Not everyone will agree with my usage of dev containers in this way, but it works for me and hopefully they will work for you!