
Docker & VSCode DevContainers
"It works on my machine!"
Contents
- Introduction to Docker
- What are the components of Docker?
- How is this useful to me?
- Introducing DevContainers
- What are the components of a DevContainer?
- Conclusion
Introduction to Docker
One common challenge for DevOps teams is managing an application’s dependencies and technology stack across various cloud and development environments. They need to keep an application operational despite the underlying hardware and dependencies it sits on.
Software devs primary focus is on feature improvements and changes. Unfortunately, these often compromise the application’s stability by deploying code that introduce environment-dependent bugs.
Consider another example, where you’d like to run multiple applications on the same host, but you need them to be isolated for security reasons. You either need to move the applications into separate hosts, which is not cost-efficient, or you’d run two different virtual machines in the host, which would give you the isolation but the resources will be consumed by the VMs mostly rather than your application, which is still not the best way.
How does Docker solve this problem?
Docker is an open-source application for packaging up and shipping “containers” with dependencies, libraries, and configuration files that the application needs to launch and operate efficiently---into a standalone executable unit that can be run anywhere — from machine to machine.
Tom: The DevOps Guy v.s. John: The Developer
What are the components of Docker?
Images
Images are like blueprints containing instructions for creating a Docker
container. These are built up as a dockerfile
and are a stateful
configuration of what you want your container to run and have inside it.
Containers
Containers are running copies of images, isolated, yet loosely coupled with the system they are running on.
Registries
A Docker registry is like a repository of images. This can be private but there are many open public registries to take images from. The default source for images if no registry is specified is DockerHub
Docker Engine
The Docker Engine is the runner for your containers and provides the interface between your local system and the containers.
How a container differs from a virtual machine
VMs vs Docker Containers
I can hear the thoughts like “this is the same thing as virtual machines”, but there are some differences:
-
Docker containers share the same system resources, they don’t have separate, dedicated hardware-level resources for them to behave like completely independent machines.
-
They don’t need to* *have a full-blown OS inside.
-
Since they mostly include application-level dependencies, they are pretty lightweight and efficient. A machine where you can run 2 VMs, you can run tens of Docker containers without any trouble, which means fewer resources = less cost = less maintenance = happy people.
How is this useful to me?
“But… it works on my machine!”
A classic IT saying. Even in the blissful world of cloud computing we still find ourselves with hundreds of tools, apps, libraries to be able to do what we do on a daily basis. Just today I’ve used the following tools:
-
zsh
-
brew
-
jq
-
awk
-
python
-
awscli
-
gcloud sdk
-
sed
-
grep
The list goes on.
All of this is well and good… if everyone on your team is running the same version of these tools, and every version of every dependency as well.
Why is it that if I run sed
on MacOS and sed
on a Windows Subsystem
for Linux machine I all of a sudden get vastly different results?
What if I need to get a new engineer onboarded and they have to spend a day finding the right version of every tool to install?
Introducing DevContainers
DevContainers are a set of instructions that tell VSCode to run all commands inside a Docker Container with a specified set of VSCode Extensions and all. This means that when you open up VSCode or clone that Git repo that you want to work on your VSCode editor will do things like: launch your terminal and all commands, run builds, tests, perform linting all from within the context of a Docker container.
Suddenly the script that ran on one machine but not the other is no longer an issue as you and your buddies machine’s are the same docker container environment.
What are the components of a DevContainer?
In short, a Docker image (typically a dockerfile
) and a json
file
(typically devcontainer.json
).
-
The
json
tells VSCode which container to use and which extensions to load up -
The
dockerfile
defines the Docker container image and which tools you want loaded up inside it.
Sample devcontainer.json
:
{ "name": "Aaron's blog", "build": { "dockerfile":"Dockerfile", "args": { "VARIANT": "5.0", "INSTALL_NODE":"true", "NODE_VERSION": "lts/*", "INSTALL_AZURE_CLI": "false"} }, "settings": { "terminal.integrated.shell.linux": "/bin/bash","files.defaultLanguage": "markdown" }, "extensions": ["ms-dotnettools.csharp", "dbaeumer.vscode-eslint","esbenp.prettier-vscode", "editorconfig.editorconfig","streetsidesoftware.code-spell-checker" ], "forwardPorts":[4014], "remoteUser": "vscode", "postCreateCommand": "npminstall" }
Sample DevContainer dockerfile
:
# [Choice] .NET version: 5.0, 3.1, 2.1ARG VARIANT=3.1FROM mcr.microsoft.com/vscode/devcontainers/dotnet:dev-${VARIANT}#[Optional] Install Node.jsARG INSTALL_NODE="true"ARG NODE_VERSION="lts/*"RUN if [ "${INSTALL_NODE}" = "true" ]; then su vscode -c "umask 0002 && . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi
Conclusion
Devcontainers are awesome, we can use them to define an isolated
development environment within Docker that has all that we need, and
only what we need, installed in it. This helps simplify people getting
into a new codebase by removing the barrier of unknown around what to
setup before then can start working. While I talked about this from the
standpoint of OSS, the same pattern can be applied to internal company
projects. You don’t even have to ship a Dockerfile
, you can point
the devcontainer.json
to a Docker image and speed up the process.