Docker Compose with Remote Containers

My last post detailed developing inside containers with Visual Studio, making it possible to use existing docker images as a fully fledged development environment.

This post will dive a bit deeper, looking at how we can use Docker Compose to spin up multiple containers to support scenarios where we might want to make use of additional services or APIs.

Step 1: Move to Docker Compose

First, we will update our .devcontainer configuration to use docker-compose, instead of just a straight DockerFile. There are three components to this:

DockerFile

In this case the docker file defines the container inside which we will do our development; this remains unchanged in this simple example:

ARG VARIANT="14-buster"
FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:0-${VARIANT}
devcontainer.json

This file controls how Visual Studio Code will handle remote containers for development. It is slightly different to the previous example as it will refer to a docker-compose file:

{
	"name": "Node.js",
	"dockerComposeFile": "./docker-compose.yml",
	"service": "app",
	"workspaceFolder": "/workspace",

	// Set *default* container specific settings.json values on container create.
	"settings": { 
		"terminal.integrated.shell.linux": "/bin/bash"
	},

	// Add the IDs of extensions you want installed when the container is created.
	"extensions": [
		"dbaeumer.vscode-eslint"
	],
	"remoteUser": "node"
}

Some of the important items to note:

  • dockerComposeFile: path to the docker-compose file
  • service: name of the container from the docker-compose file which will be used as the dev container
  • remoteUser: required when container is configured with non-root user
docker-compose.yaml

Finally, the docker-compose.yaml file defines the containers and services we want to spin up. To start this is just replicating the dev container:

version: '3'

services:
  app:
    build: 
      context: .
      dockerfile: Dockerfile
      args:
        VARIANT: 14

    volumes:
      - ..:/workspace:cached

    # Overrides default command so things don't shut down after the process ends.
    command: sleep infinity

    # Use a non-root user for all processes.
    user: node

With these elements in place, it should be possible to execute a container rebuild:

Step 2: Add Additional Services

Now that we are using docker compose, we can simply update docker-compose.yaml to include the additional containers we want, like this:

version: '3'

services:
  app:
    build: 
      context: .
      dockerfile: Dockerfile
      args:
        VARIANT: 14

    volumes:
      - ..:/workspace:cached

    # Overrides default command so things don't shut down after the process ends.
    command: sleep infinity

    # Runs app on the same network as the database container, allows "forwardPorts" in devcontainer.json function.
    network_mode: service:deepstack-ai

    # Use a non-root user for all processes.
    user: node

  deepstack-ai:
    image: deepquestai/deepstack:latest
    volumes:
      - localstorage:/datastore
    environment:
      - VISION-DETECTION=True

volumes:
  localstorage:

After executing another rebuild, you should see VS Code pull down the appropriate images, and spin everything up, leaving you with both containers running and ready to use:

Summary

Docker-compose can be used with Visual Studio Code’s remote containers to make it possible to spin up multiple containers as needed. This is useful when want to build on existing services, such as a database or AI API etc.