,

Support for Dev Containers Features on macOS and Windows

Last month, we improved Podman’s support for Dev Containers on Windows. Dev Containers is a popular specification for containerized developments that is becoming a standard. In this blog post, we review the concept of “Feature” in Dev Containers and how Podman currently supports it on different OSes.

Development Containers

Development Containers https://containers.dev/, also known as Dev Containers, is a file format for specifying a containerized development environment. With a devcontainer.json file, a team can declare the tools and configurations to build and run a project. It can be versioned in the project’s git repository so anyone can start a development environment in seconds. The only requirement is a container engine such as Docker or Podman.

Microsoft has created the standard. Visual Studio Code, the JetBrains IDEs, and many other tools support it.

Dev Containers Images

Microsoft maintains a list of pre-built dev container images. Every image ships the development tools for a specific programming language or tech stack. The list of Microsoft pre-built images can be found at https://github.com/devcontainers/images. For example, the image for Go development is ghcr.io/devcontainers/templates/go.

{
    "image": "mcr.microsoft.com/devcontainers/go"
}

These images have a predefined set of tools for a given language. Nevertheless, more is needed for real-world projects. It’s possible to use a custom image instead of the Microsoft pre-build ones, but Dev Containers features are a popular way to customize Dev Containers.

Dev Containers Features

Features are extra tools that can be included on top of an image, and the list of available features (official and community-supported) is published here: https://containers.dev/features. In the following example, tools such as bats, pandoc, pre-commit, shellcheck, modern-shell-utils, and skopeo are added to the tools of the Microsoft Go image:

{
   "image": "mcr.microsoft.com/devcontainers/go",
   "features": {
       "ghcr.io/edouard-lopez/devcontainer-features/bats:0": {},
       "ghcr.io/rocker-org/devcontainer-features/pandoc:1": {},
       "ghcr.io/prulloac/devcontainer-features/pre-commit:1": {},
       "ghcr.io/marcozac/devcontainer-features/shellcheck:1": {},
       "ghcr.io/mikaello/devcontainer-features/modern-shell-utils:2": {},
       "ghcr.io/jsburckhardt/devcontainer-features/skopeo:1": {}
    }
}

Features are container-based, too, and, unlike images, are built at the startup of the development environment.

Features internals and the use of additional build contexts

The way features are installed is particular; we will detail it here. It will be helpful to clarify how Podman supports them. 
Features are distributed as an OCI artifact with the type “application/vnd.devcontainers“. When the Devcontainer is started, using the VisualStudio Code, for example, the OCI artifact is pulled, and an installation script is extracted in a local folder (this is what the installation script for the skopeo feature looks like).

$ skopeo inspect --raw docker://ghcr.io/jsburckhardt/devcontainer-features/skopeo:1 | jq
{
  "schemaVersion": 2,
  "mediaType": "application/vnd.oci.image.manifest.v1+json",
  "config": {
    "mediaType": "application/vnd.devcontainers",
    "digest": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
    "size": 0
  },
...
}

The next step in the Dev Container startup is to build the custom image. This is done on the fly, based on the devcontainer.json image (`mcr.microsoft.com/devcontainers/go` in the example above), and executing the features installation scripts. Here is a (simplified) example of a generated Dockerfile for building a Dev Container custom image:

FROM mcr.microsoft.com/devcontainers/go

COPY --from=features-context /tmp/build-features/ /tmp/dev-container-features

RUN cd /tmp/dev-container-features/skopeo_3 \
 && chmod +x ./devcontainer-features-install.sh \
 && ./devcontainer-features-install.sh

The interesting part here is the COPY –from instruction. It uses the additional build context feature-context and not the default one. The folder tmp/build-features/ exists in the build context feature-context. This is an elegant way to isolate the build context of the Dev Container image and the features folders to avoid collision problems (e.g. a build context subfolder and the feature folder may have the same name).

That means that to support Dev Containers features, a container engine needs to support additional build contexts.

Podman support for additional build contexts

Additional build contexts are a reasonably recent feature. They have been introduced in Podman v4.2 and Buildah v1.26.

podman build \
     --build-context context=./alt-ctx \
     ./main-ctx

The current implementation of the additional build contexts works fine when podman runs locally. But it isn’t when podman runs on a remote machine. In fact, unlike the main build context, additional contexts aren’t sent over to the remote machine, and the build fails because the additional build contexts’ local paths don’t exist in the remote machine.

This is an annoying limitation and will hopefully be addressed soon. But for now, Podman Machine can support additional build contexts (and Dev Containers features) with some default conventions.

Podman Machine default conventions to support additional build contexts

First, Podman Machine mounts some default folders into the guest OS.

LinuxmacOSWindows
Host PathGuest PathHost PathGuest PathHost PathGuest Path
$HOME$HOME/Users/UsersC:\/mnt/C
/private/private
That means that if an additional build context is within the mounted Host path, it also exists in the Podman Machine 🎉.

Second, Windows host paths (e.g. C:/Users/user1) are automatically transformed into Linux guest paths (e.g. /mnt/c/Users/user1). The Podman client for Windows uses this trick when mounting volumes, too (e.g. podman run -v C:/Users/user1).

📝 These conventions are supported on Windows WSL starting with Podman 5.3.0 (there is a separate issue for Hyper-V).

The following table summarizes the current Dev Containers Features (and Additional Build Context) support for the different OSes.

Linux localLinux remotemacOS (vfkit)macOS (libkrun)Windows (WSLv2)Windows (Hyper-V)
🟢🟠🟠🟠
Dev Containers Features support

🟢 Works

🟠 Works with default Podman machine configuration

Doesn’t work yet

Inception: Building Podman with a Podman-backed Dev Container

Now that Podman has decent support for Dev Containers features let’s make the most of it. Let’s build Podman itself from sources! The only requirements should be having locally installed:

  • Podman
  • The devcontainer CLI (VisualStudio Code with the Dev Containers extension works, too).

Once these two requirements are installed, this simple devcontainer.json and Dockerfile are enough to build Podman.

{
	"name": "podman-devcontainer",
	"build": {
		"dockerfile": "Dockerfile",
		"context": ".."
	},
	"features": {
		"ghcr.io/devcontainers/features/go:1": {}
	},

	"containerEnv": {
		"HOME": "/home/vscode"
	}
}
FROM quay.io/podman/stable
COPY /rpm/podman.spec /rpm/podman.spec
RUN dnf update -y && dnf -y install 'dnf-command(builddep)'
RUN dnf -y \
    builddep /rpm/podman.spec

These two files should be copied into a new folder named .devcontainer at the root of the podman repository (or checkout the branch named inception of my podman fork). 

The following command, run from a terminal at the root of the Podman repository, starts the Dev Container.

$ devcontainer up --docker-path podman --workspace-folder .

Then, the command make podman can be executed in the Dev Container.

$ devcontainer exec --workspace-folder . --docker-path podman \
    make podman

Alternatively, the same Dev Container can be started with VisualStudio Code.

Screenshot

📝 Running other make targets of the Podman Makefile may fail. That may be related to some missing tools in the Dev Container or to the Podman Machine’s insufficient resources (RAM and CPU). Either way, this devcontainer.json should be easy to improve.

Leave a Reply

Subscribe

Sign up with your email address to receive updates by email from this website.


Search