Get started with Linux containers in Docker on WSL 2

When Microsoft launched its Windows Subsystem for Linux (WSL) back in 2018, it was very clear why: It wanted to provide tools for developers building modern cloud applications. Microsoft needed a way to offer container development, with an eye on Azure-hosted distributed applications running on its Azure Kubernetes Service.

However, there were issues with the original WSL platform. It wasn’t easy to run Linux containers natively on Windows. Combined with ensuring WSL’s emulation layer could support Linux applications and services, it was clear that an emulation-based approach couldn’t meet the needs of most developers. That led to a redesign of the WSL platform and the resulting launch of WSL2 with a full Linux kernel earlier in 2019.

Microsoft’s WSL 2 previews are currently rolling out with Windows Insider 20H1 builds. As I noted in a previous article on WSL2, much of how it works is based on a collaboration between Microsoft and Docker engineers: LCOW (Linux Containers On Windows), which was intended to simplify running Linux containers directly on Windows. By using a hypervisor to host the relevant elements of the Linux OS and Docker platform, Windows development tools have access to the contents of the hosted containers. That thin-hypervisor approach now hosts the entire WSL environment, allowing Linux applications and containers to run natively on Windows systems.

Alongside the launch of WSL2, Docker announced a new version of its Docker Desktop for Windows, with a new version of the Docker application that would work with WSL2, supporting native Linux containers on Windows at long last.

That new WSL2 version of Docker Desktop is now available as a beta from Docker, requiring a Docker account. I signed up shortly after the new release was announced and last week finally got access to the new build. I’ve now installed it on my Fast Ring test PC, ready to try it out alongside Visual Studio Code’s new remote development tool as part of putting together an end-to-end development toolchain for Linux container applications.

Getting Docker running on WSL2

There are some prerequisites before running the new Docker Desktop. First, and most importantly, Docker Desktop expects Ubuntu to be your default WSL distribution, so use the wsl command line tool to make sure that Ubuntu is first running in WSL2, and that it’s the default. If it’s running in WSL1, use the command wsl –set-version Ubuntu 2 to switch, and use wsl -l to check that it’s default. To set it as default type wsl -s Ubuntu. One important note: You need to be running Ubuntu 18.04, so either download it from the Windows Store directly or use sudo do-release-upgrade to upgrade an existing Ubuntu WSL install to the latest release version.

With Ubuntu ready to go, you can now set up Docker Desktop. Once you’ve downloaded the preview Docker Desktop installer, run it. You’ll be prompted to choose whether to use Linux or Windows containers (with the option to change after installation). The default is to use Linux containers, so click OK. The installer then unpacks all the necessary files and sets up the appropriate mappings to WSL2’s file system. When the install is complete it’ll log you out of Windows, and should be ready to go once you log back in.

Running Docker Desktop with WSL2

When you launch Docker Desktop, there’s a new Docker icon in your Windows taskbar (if it’s not visible, check the notification area). Right-click on it and choose WSL Tech Preview to launch the Docker WSL2 installation tool. This checks for the correct Windows version, makes sure that you’re running Ubuntu as a WSL2 instance, and that it’s the default. If any prerequisites are missing, the installer will notify you what needs to be changed; you won’t be able to install the WSL 2 Docker tools until you’ve corrected the issue.

Once everything is ready, you can start the Docker Desktop and enable the WSL 2 Docker daemon. To check it’s running, open an Ubuntu terminal and check the Docker context that should have automatically been created, using docker context ls. This should show that your default Docker instance is running in Ubuntu, with details of the current end point and the orchestrator being used. By default, the orchestrator is Docker’s swarm, as Kubernetes support is not yet available for WSL2 containers.

You can now start installing and running Docker containers from your own or from Docker’s own repository. A useful quick test is to download and run Docker’s own “hello-world” container from an Ubuntu WSL2 terminal: sudo docker run hello-world. This will display a brief console message to show that it’s run successfully.

With Docker running on WSL2, you’re now ready to start building a full Linux development toolchain under Windows. I started with Visual Studio Code, which now offers remote development tooling that connects a Windows VS Code UI to debugging and file system access components running in Linux, in this case in WSL2’s Ubuntu. There’s no configuration required. From Visual Studio Code you open a remote connection to WSL2 and you have full access to the virtual hard drive that contains the WSL2 Linux file system, as well as using VS Code’s built-in terminal to access the WSL2 command line.

Building code on Windows for Linux containers

One of the interesting aspects of using WSL2 for container development is the ability to mix and match development environments. I’ve got the .Net command line tooling on my WSL2 test machine, so I used it to make a simple .Net Core MVC (Model-View-Controller) application in the Windows file system. From a VS Code terminal that’s running in WSL2 using remoting, I could then use an Ubuntu bash prompt to copy the .Net project from the Windows file system into the Linux file system, before creating an appropriate dockerfile and .dockerignore in VS Code and saving them directly into the WSL2 Linux file system.

With a dockerfile in place and a .Net Core application built and ready to go, all I needed to do was use the command line Docker tool to assemble the container, pulling in the appropriate images from Microsoft’s Docker repository to ensure that the .Net Core runtime was in place. Once Docker had created my application’s container, I was able to launch it, giving it a name and mapping it to an appropriate port. As recent builds of WSL2 have improved network address mapping from external applications it was easy enough to fire up a browser on my development system and connect to the resulting ASP.Net Core app running on Linux.

The combination of Docker for Windows and WSL2 certainly simplifies the process of building Linux container applications on a Windows development system. It’s not perfect, and there are still issues when crossing the boundaries between the two Windows personalities. However, the improved productivity, reduced overhead, and increased speed is worth the minor speed bumps. It’ll be interesting to watch how both Docker Desktop and WSL2 evolve in the next few months, though even in these early stages the combination is ready to use.