Docker Setup

Docker lets you run a guest OS on a host, where the host is your Windows, macOS, or Linux computer. The guest OS is run in a lightweight container, which uses much fewer resources than an entire Virtual Machine (VM). A docker container is a standard way of shipping services that combines a needed environment (installed packages, OS) with potentially multiple coordinated applications.

Outside of DevOps, here are some scenarios where this can be useful to you:

Further information:

Install Docker

  1. Create a free Docker ID:

    Docker SignUp

  2. Download Docker Desktop for Mac or Windows.

  3. Install the download. If you are on Windows, refer to WSL Containers. There is a tutorial for creating Docker images. Skip it.

  4. Using your Docker ID, login to the Docker app.

Basic Usage

Docker uses images to create containers. The image srcml/codespaces is already available. To create a container of this image to work in:

The image downloads (cached for later use), a container is created and started, and a prompt appears, similar to the following:

The prompt is for a bash shell so that you can use bash commands. The numeric part of the prompt, e.g., 0597c29420, is your container id (docker container ls).

When you are done, exit the container:

Notes:

Scenario: Build from a Public Git Repository

  1. Create a container
  2. Inside the container, clone the repository:
  3. Build and check the program:
  4. When finished, exit the container:

Scenario: Build from Local Files

If you have a private repository, then using git inside the Docker container can take some setup with authorization and so on. It is easier to share your current source directory with the Docker container.

  1. Move to the source directory of your code. This is not the build directory, as the build directory is inside the docker container.
  2. Create a container, but this time we are going to:
    • Share our source directory on the host with the docker container. The directory will be in /Source in the docker container.
    • Create a build directory, i.e., /Build in the container, and make this our default directory in the container.
  3. Build and check the program:
  4. When finished, exit the container:

Scenario: Build from a Specific Branch, Tag, or Commit

The previous scenario works well, but what if you want to build and run the code at a particular tag, from another branch, or even from a specific commit?

  1. Move to the source directory of your code. This is not the build directory, as the build directory is inside the docker container.
  2. Create a container, but don't run it yet. We will specify a build directory (i.e., /Build in the container) and give the container a name so we can refer to it later.
  3. Copy the code from a branch, tag, or commit into the container. We use the git archive command and send it to the docker cp command (a docker copy), placing the code from tag v3a into the directory /Source in the container:
  4. Start the container:
  5. Start a shell in the container:
  6. Build and check the program:
  7. When finished, exit the container:
  8. Stop the container and remove it:

Notes:

  • The container's name (e.g., linux) can be anything. You can use the id part of your campus email or the name of the tag/branch/commit you are building.
  • Instead of v3a, you can use any tag, branch, or commit id. It doesn't matter what branch or commit you are on in your host machine.

Docker and Architectures

We have two main CPU architectures for applications:

  • x86, often referred to as amd64, e.g., linux/amd64. Almost all Windows PCs, older Macs, and cloud.
  • ARM, often referred to as arm64 or aarch64, e.g., linux/arm64. Very few Windows PCs, M1/M2 Macs, and cloud. ARM is now rare for (non-Mac) PCs, but is expected to grow to 30% of PCs and 50% of cloud by 2026

There are others, but understanding these two leads to understanding the others. So, whatever your application's target, it will be a mixed x86/ARM world.

Most Linux distributions are available in x86, e.g., linux/amd64, and ARM, e.g., linux/arm64, versions. They are often available in more variations than that

So, how does Docker deal with this? Both in creating docker images for the various architectures and running different architectures? Let's look at Docker on macOS as an example.

Docker on macOS runs on x86 Macs and ARM M1/M2 Macs. On both of these platforms, it can:

  • Run linux/amd64 (x86) images
  • Run linux/arm64 images

Which it runs by default depends on your host architecture. To run on a specific architecture, you can specify either platform, e.g., x86:

E.g., ARM:

Docker can do this via QEMU. Not only can it run images of different architectures, but it can also build images for different architectures or multiple architectures at the same time.

Let's look at an example—a 2020 M1 Macbook Air. Building an application in Docker using Ubuntu linux/arm64 takes less than twice as long, and building with linux/amd64 takes 15 times as long. However, a beta feature in Docker uses Rosetta2 (Apple translation layer for x86 to ARM), which only takes less than 4 times as long and is 4.5 times faster than x86 on QEMU. The table below shows the times for building srcML:

Build Platform Clean Build Time Native Ratio ARM Ubuntu Ratio Notes
M1 Mac Native 12s 1.0x 0.7x  
Docker ARM Ubuntu (OrbStack) 15s 1.25x 0.8x  
Docker ARM Ubuntu 18s 1.5x 1.0x  
Docker x86 Ubuntu (OrbStack) 35s 2.9x 1.9x Uses Rosetta2
Docker x86 Ubuntu (Rosetta2) 40s 3.3x 2.2x Beta Option
Docker x86 Ubuntu (QEMU) 180s 15.0x 10.0x Default

The table also includes timings using OrbStack, which only uses Rosetta2 and improves the timings even more for ARM and x86 emulation. OrbStack is a drop-in replacement for Docker Desktop on Macs and seems to have speed and memory advantages over Docker Desktop.

Note that this applies in reverse. On an x86 Mac, we can build and run ARM images. However, we don't have the Rosetta2 option and can only use QEMU. The following times are from a MacBook Pro (16-inch, 2019) 2.4 GHz 8-Core Intel Core i9 with 64 GB of memory:

Build Platform Clean Build Time Native Ratio ARM Ubuntu Ratio Notes
x86 Mac Native 19s 1.0x 0.6x  
Docker x86 Ubuntu 30s 1.6x 1.0x  
Docker ARM Ubuntu (QEMU) 266s 14.0x 8.6x Default