Docker & VSCode DevContainers

Docker & VSCode DevContainers

"It works on my machine!"

Contents

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.

image-20211022-083241.png

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.

image-20211022-035842.png

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:

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": "npm
install" }

Sample DevContainer dockerfile:

# [Choice] .NET version: 5.0, 3.1, 2.1
ARG VARIANT=3.1
FROM mcr.microsoft.com/vscode/devcontainers/dotnet:dev-${VARIANT}
#[Optional] Install Node.js
ARG 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.

References: