Skip to content

Instantly share code, notes, and snippets.

@oleksis
Last active November 17, 2023 08:30
Show Gist options
  • Save oleksis/05f47ebc736a5c311c2bbef0bd5ca1ad to your computer and use it in GitHub Desktop.
Save oleksis/05f47ebc736a5c311c2bbef0bd5ca1ad to your computer and use it in GitHub Desktop.
DevContainers and Codespaces

DevContainers and Codespaces

In this conversation, I explained the following topics to you:

  • What is a Dockerfile and how to write one for a Jekyll project that uses Python and Playwright.
  • How to speed up the build and run the container when using Codespaces, by using multi-stage builds, .dockerignore files, prebuilds, and usage monitoring.
  • How to update the devcontainer.json file to use the prebuild feature on Codespaces, and how to define different variants of the dev container image.

Dockerfile

A Dockerfile is a text file that contains instructions for building a Docker image. A Docker image is a snapshot of a containerized application that can be run on any platform that supports Docker. A Docker image consists of layers, each of which represents a change from the previous layer. The Dockerfile defines the base image, the files to copy, the commands to run, and other settings for the image.

Brief explanation of each line in the Dockerfile

  • FROM mcr.microsoft.com/devcontainers/jekyll:2-bullseye This line specifies the base image for your Dockerfile. You are using a pre-built image from Microsoft that contains Jekyll, a static site generator, and other tools. The 2-bullseye tag indicates that the image is based on Debian 11 (Bullseye).
  • ARG PYTHON_VERSION="3.11.6" This line defines an argument that can be passed to the Dockerfile at build time. You are setting the default value of the argument to 3.11.6, which is the version of Python you want to install.
  • ENV VERSION=${PYTHON_VERSION} This line sets an environment variable that can be used by other commands in the Dockerfile. You are setting the value of the variable to the value of the argument PYTHON_VERSION.
  • USER root This line changes the user that executes the following commands. By default, the user is jekyll, which is defined in the base image. You are switching to the root user, which has full privileges on the system.
  • ADD --chmod=0755 https://raw.githubusercontent.com/devcontainers/features/main/src/python/install.sh /tmp/ This line copies a file from a URL to a destination in the image. You are downloading a shell script that installs Python and its dependencies from a GitHub repository. The --chmod=0755 option sets the permissions of the file to executable.
  • RUN apt-get update && export DEBIAN_FRONTEND=noninteractive && /tmp/install.sh This line executes a command in the image. You are updating the package lists, setting the environment variable DEBIAN_FRONTEND to noninteractive to avoid prompts, and running the shell script that installs Python.
  • COPY requirements-dev.txt /tmp/requirements-dev.txt This line copies a file from your local directory to a destination in the image. You are copying a text file that lists the Python packages you need for development.
  • RUN /usr/local/python/${PYTHON_VERSION}/bin/python3 -m pip install --no-cache-dir -r /tmp/requirements-dev.txt && /usr/local/python/${PYTHON_VERSION}/bin/python3 -m playwright install --with-deps This line executes another command in the image. You are using the Python executable that you installed to run the pip module, which installs the Python packages from the text file. You are also using the same Python executable to run the playwright module, which installs a browser automation tool and its dependencies.
  • COPY --chmod=0755 .devcontainer/post-create-bpdevs.sh /usr/local/post-create-bpdevs.sh This line copies another file from your local directory to a destination in the image. You are copying a shell script that runs some custom commands after the codespace is created.

Speed up the build

  • Use multi-stage builds to reduce the size of your final image. Multi-stage builds allow you to create intermediate images that perform specific tasks, and then copy only the necessary files to the final image. For example, you can create a separate image that installs Python and its dependencies, and then copy only the Python executable and the site-packages directory to the final image. This way, you can avoid including unnecessary files and packages that are not needed for running your application. For more information, see Overview of best practices for writing Dockerfiles.
  • Exclude files and directories that are not needed for building or running your application with a .dockerignore file. A .dockerignore file tells Docker which files and directories to ignore when copying files from your local directory to the image. This can speed up the build process and reduce the size of the image. For example, you can exclude the .git directory, the .vscode directory, and any temporary or cache files that are not relevant for your application. For more information, see Exclude with .dockerignore.
  • Configure your project to prebuild codespaces for specific branches in specific regions. Prebuilding codespaces allows you to create codespaces in advance, so that they are ready to use when you need them. You can specify which branches and regions you want to prebuild codespaces for, and how often you want to update them. This can save you time and resources when creating and connecting to codespaces. For more information, see Prebuilding your codespaces.
  • Monitor your usage and quotas for codespaces and take steps to reduce them if necessary. Codespaces consume CPU core-hours and storage, which are limited by your plan and your spending limit. You can check your usage and quotas in the billing settings page, and use the usage report feature to get a detailed breakdown of your consumption. You can also delete codespaces that you no longer need, avoid enabling pre-builds that consume storage, and use granular reporting to identify areas for improvement. For more information, see How to Optimize your Codespaces: Pro-tips for managing quotas.

Codespaces

To use the pre-build feature on Codespaces, you need to update your devcontainer.json file with the following settings:

  • Add a "prebuild": true property to the root of the devcontainer.json file. This tells Codespaces to pre-build the dev container image for the current branch and commit.
  • Optionally, add a "prebuildCommand" property to specify a command to run after the dev container image is built, but before the codespace is created. This can be used to install dependencies, run tests, or perform other tasks that are needed for the codespace to be ready. The command can be a string or an array of strings, and it will be executed in the dev container's default shell.
  • Optionally, add a "prebuildVariants" property to define different variants of the dev container image for different scenarios. For example, you can have a variant for development, testing, and production. Each variant can have its own "prebuildCommand", "build" arguments, and other properties. You can specify which variant to use when creating a codespace by adding a query parameter to the URL, such as ?variant=testing.

Here is an example of a devcontainer.json file that uses the pre-build feature:

{
  "image": "ghcr.io/oleksis/blackpythondevs.com:latest",
  "prebuild": true,
  "prebuildCommand": "bundle install",
  "prebuildVariants": [
    {
      "name": "development",
      "prebuildCommand": "bundle install && JEKYLL_ENV=development bundle exec jekyll build"
    }
  ]
}

External links

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment