Open Menu

    Mastering VS Code Dev Containers with Docker

    Flavio Del Grosso

    Flavio Del GrossoFeb 4, 2024

    7 min read1882 words

    Welcome, fellow developers, to a thorough exploration of the marriage between Visual Studio Code dev containers and Docker. In this comprehensive guide, we'll dissect the customization techniques, utilizing Docker files and Docker Compose to elevate your development environment. Buckle up for an insightful journey into the intricacies of Docker and VS Code synergy.

    Understanding Dev Containers

    Before diving into customization, let's briefly revisit the essence of dev containers. They provide a consistent and reproducible development environment by running a Docker container, ensuring uniformity across machines. While basic setups are straightforward, we'll delve into advanced customization techniques to truly harness the power of Docker within your dev containers.

    Customization with Docker Files

    Docker files act as the architects of container images, offering instructions for building or extending existing images. They empower developers to add packages, make edits, and personalize the development environment. Let's go step by step through incorporating Docker files into your VS Code dev containers.

    1. Initialize the VS Code Environment: Start by installing the Dev Containers extension and run the "Add Dev Container Configuration Files" command within VS Code.

    2. Choose a Base Container: Select a base container, let's say a Python container for a Python development environment.

    3. Explore Generated Files: Navigate to the generated .devcontainer folder and find the devcontainer.json file, a simple configuration file referencing the chosen image.

    4. Craft a Dockerfile: Create a Dockerfile to define the base image and install additional tools. Here's a snippet:

      docker

      FROM python:3.9
       
      # Install FFmpeg
      RUN apt-get update && apt-get install -y ffmpeg
    5. Update devcontainer.json: Modify devcontainer.json to reference the Dockerfile:

      json

      {
          "name": "My Custom Dev Container",
          "build": {
              "dockerfile": "Dockerfile"
          }
      }
    6. Launch Your Dev Container: Start your dev container, and witness the Dockerfile orchestrating the creation of your personalized image.

    Streamlining with Docker Compose

    Docker Compose takes center stage when orchestrating multi-container development environments. It allows developers to focus on essential tools in their dev containers, while Docker Compose handles services. Let's explore seamless integration of Docker Compose into VS Code dev containers.

    1. Start with an Image-Based Dev Container: Begin with an image-based devcontainer.json for a clean slate.

    2. Create docker-compose.yaml: Craft a docker-compose.yaml file to define services. For instance, pairing the dev container with a PostgreSQL database:

      yaml

      version: '3'
       
      services:
        dev-container:
          image: your-custom-image
          volumes:
            - ./workspace:/workspace
          networks:
            - db-network
          command: sleep infinity
       
        db:
          image: postgres
          networks:
            - db-network
          environment:
            POSTGRES_PASSWORD: yourpassword
       
      networks:
        db-network:
    3. Update devcontainer.json: Modify devcontainer.json to reference the Docker Compose file:

      json

      {
          "name": "My Dev Container with Compose",
          "dockerComposeFile": "docker-compose.yaml",
          "service": "dev-container",
          "workspaceFolder": "/workspace"
      }
    4. Enjoy the Simplicity: Revel in the simplicity of keeping your dev container focused on development tools while Docker Compose orchestrates the services.

    Hybrid Approach: Docker Files with Docker Compose

    Combine the strengths of Docker files and Docker Compose for the ultimate customization. This advanced technique allows you to enjoy the benefits of both without compromising simplicity.

    1. Enhance Your Docker Compose Dev Container: If your Docker Compose dev container lacks a certain tool or package (like Gimp), don't worry.

    2. Reference Your Dockerfile: Point to your previously crafted Dockerfile within the docker-compose.yaml:

      yaml

      version: '3'
       
      services:
        dev-container:
          build:
            context: .
            dockerfile: Dockerfile
          volumes:
            - ./workspace:/workspace
          networks:
            - db-network
          command: sleep infinity
       
        # ...rest of the Docker Compose configuration
    3. Replace Image with Build: Within the docker-compose.yaml, replace the image property with build.

    4. Sleek devcontainer.json: Remarkably, no alterations are required in devcontainer.json. It remains sleek and uncomplicated, maintaining its user-friendly appeal.

    Using Prebuilt Images: A Docker-Free Approach

    In some scenarios, you might not need Docker at all for your VS Code dev containers. Take, for instance, the provided example configuration for a Python development environment with FFmpeg integration. The simplicity of this configuration lies in referencing a prebuilt image from Microsoft's container registry (mcr.microsoft.com/devcontainers/python:1-3.12-bullseye). By leveraging the power of features, such as the FFmpeg installation through ghcr.io/devcontainers-contrib/features/ffmpeg-apt-get:1, you can effortlessly set up your development environment without delving into Docker files or Docker Compose. This approach is particularly handy when a preconfigured image meets your requirements, demonstrating the flexibility and convenience of dev containers tailored to your needs. The provided postCreateCommand further showcases how you can execute additional commands after the container is created, enhancing the extensibility of your development environment.

    json

    // For format details, see https://aka.ms/devcontainer.json. For config options, see the
    // README at: https://github.com/devcontainers/templates/tree/main/src/python
    {
     "name": "my-python-dev-container",
     // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
     "image": "mcr.microsoft.com/devcontainers/python:1-3.12-bullseye",
     "features": {
      "ghcr.io/devcontainers-contrib/features/ffmpeg-apt-get:1": {}
     },
     // Features to add to the dev container. More info: https://containers.dev/features.
     // "features": {},
     
     // Use 'forwardPorts' to make a list of ports inside the container available locally.
     // "forwardPorts": [],
     
     // Use 'postCreateCommand' to run commands after the container is created.
     "postCreateCommand": "pip3 install --user -r requirements.txt"
     
     // Configure tool-specific properties.
     // "customizations": {},
     
     // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
     // "remoteUser": "root"
    }

    Advanced Techniques and Best Practices

    As we navigate through these customization scenarios, let's delve into additional techniques and best practices to further enhance your VS Code dev container experience.

    Leveraging Docker Compose Configurations

    Docker Compose offers a myriad of configurations for your services. Explore the vast array of configurations available for services like databases, caching systems, or specialized development servers. Encapsulate these configurations in a single YAML file for a clean and modular setup.

    Reusing Existing Dockerfiles

    The power of Docker lies in its reusability. If you already have a well-crafted Dockerfile for a specific tool or environment, reuse it within your VS Code dev containers. Reference the existing Dockerfile in your devcontainer.json or docker-compose.yaml to save time and ensure consistency across projects.

    Optimizing Docker Build Context

    Efficiency is paramount when working with Docker, especially in a development environment where quick iterations are crucial. Pay attention to the Docker build context – the set of files used during the build process. Keeping the context minimal by specifying only essential files can significantly speed up the build process, particularly in large projects.

    Exploring Prebuilt Images

    Create a Dockerfile that installs and configures all necessary tools to build a prebuilt image. This image can be shared and reused across projects, ensuring a consistent development environment for everyone involved. Streamline onboarding processes and collaboration by sharing prebuilt images.

    Mastering Dev Containers: A Holistic Approach

    As we traverse the diverse landscape of VS Code dev container customization, adopt a holistic approach that balances power and simplicity. While Docker files and Docker Compose open up possibilities, the devcontainer.json file remains a simple entry point. Ensure that the onboarding process for new developers and collaborators remains smooth and hassle-free.

    In the spirit of continuous improvement, the Dev Containers documentation serves as a comprehensive resource, offering detailed guides and documentation on every aspect of dev containers. Whether you're a novice exploring the basics or a seasoned developer seeking advanced customization, the documentation is your go-to source for mastering the art of dev containers.

    Conclusion: A Dockerized Future

    In conclusion, mastering Docker files and Docker Compose within VS Code dev containers empowers developers to create tailored and efficient development environments. The flexibility to customize, reuse, and share configurations enhances collaboration and accelerates project development. As you embark on your Dockerized journey, embrace the possibilities, experiment with configurations, and make the most of the powerful synergy between Docker and VS Code. Cheers!


    Share on