How to use a Dev Container with VS Code?

Viewed 782

Developing go applications inside of a container with vscode.

Prerequisites:


Create a .devcontainer folder in the root of your project directory:

.devcontainer
├── .env
├── devcontainer.json
├── docker-compose.yml
└── Dockerfile

The .devcontainer folder will contain four files.


The .env file:

REDIS_HOST=redis
REDIS_PORT=6379

This file can be used to define any number of env variables. These environment variables will be automatically loaded and made available from within the dev container when it starts. Use the container's terminal to confirm the environments are set by using the printenv command. Once the container has been created you'll need to rebuild it for changes made to this file to be seen.


The .devcontainer.json file:

{
    "name": "godev",
    "workspaceFolder": "/workspace",
    "dockerComposeFile": [
        "docker-compose.yml"
    ],
    "service": "godev",
    "postStartCommand": "go version",
    "customizations": {
        "vscode": {
            "settings": {
                "workbench.colorTheme": "Solarized Dark"
            },
            "extensions": [
                "golang.go",
                "ms-azuretools.vscode-docker"
            ]
        }
    }
}

This file contains the settings that vscode will use to create the dev container. It references a standard docker compose file that's used to create any services required by your project. In addition to the standard docker features it will also configure vscode to use any extensions required by your project. You can think of this as a throw away environment that is created from scratch when you're ready to work and thrown away when you're done with it.

To learn more about the options available visit the containers.dev website.


The docker-compose.yml file:

version: '3'

services:
  godev:
    container_name: godev
    build:
      context: ../
      dockerfile: ./.devcontainer/Dockerfile
    env_file:
      - .env
    volumes:
      - ${HOME}/.ssh:/root/.ssh:ro
      - ../:/workspace:cached
      - /var/run/docker.sock:/var/run/docker.sock
  redis:
    container_name: redis
    image: redis:7.2-alpine

This is a standard docker compose file. It creates two containers for our project. The main go container and redis. You may not need redis, but it's here for sake of the example and shows how to use multiple services. The dev container will only attach to the service referenced within the devcontainer.json file when it starts. Additional services defined within the docker compose file will be available on your docker network as usual.

To learn more about the options available visit the docker compose file website.


The Dockerfile file:

FROM golang:1.22

RUN apt-get update && \
    # Go tools:
    # - https://github.com/golang/tools
    # - https://github.com/golang/vscode-go/blob/HEAD/docs/tools.md
    go install -v golang.org/x/tools/gopls@latest && \
    go install github.com/cweill/gotests/gotests@latest && \
    go install github.com/fatih/gomodifytags@latest && \
    go install github.com/josharian/impl@latest && \
    go install github.com/haya14busa/goplay/cmd/goplay@latest && \
    go install github.com/go-delve/delve/cmd/dlv@latest && \
    go install honnef.co/go/tools/cmd/staticcheck@latest && \
    go install golang.org/x/tools/cmd/godoc@latest && \
    go install gotest.tools/gotestsum@latest && \
    # Go vulnerability checker:
    # https://go.dev/security/vuln/
    go install golang.org/x/vuln/cmd/govulncheck@latest && \
    # Graphviz for visualizing go test pprof profiles.
    apt-get -y install graphviz && \
    # Docker cli
    curl -fsSL https://get.docker.com -o get-docker.sh && \
    sh get-docker.sh

ADD . /workspace
WORKDIR /workspace

CMD ["sleep", "infinity"]

This file will create the golang container. It will pre-install tooling that is used by the vscode go extension. It will also install the docker cli within the container which allows you to access docker containers running on the host machine from within the container by using the docker extension or the docker cli.

To learn more about available options visit the docker file reference website.


If you've made it this far you're now able to open your project directory within vscode and when doing so will be prompted to reopen it within the devcontainer. It may take a few minutes the first time, but on subsequent loads it will be faster since the container image(s) will already be available within the docker cache.

Now you're ready to get going with go!

What's the point of this? All the benefits of docker alongside single click initialization of your entire dev environment. When you’re done working throw it all away until you need to work on the project again. It’s decoupled from the local machine and isolated.

It’s a game changer if you work on multiple projects, have multiple workstations, use a remote machine to work, or just want to share a consistent workspace with your team. Also makes onboarding easy, because they just open the project and immediately get the same exact tooling used by the existing team.

It goes beyond just having services start with a docker compose file. It’s all the additional project specific settings that are available which can be committed to a repository. Also having the ability to define post create, post start, post attach, shutdown commands, ide extensions that only exists on the remote instead of being installed locally and persisting on your machine when you bounce between projects that don’t need them, port labels and attributes; even less important things like themes.

The way I use vscode is with no extensions installed. I define everything in the devcontainer.json file per project this way I have what I need, but only when it’s needed. I do this for go, php, javascript, etc as it varies. The other advantage comes from when you have multiple workstations. Everything stays in sync.

Been using devcontainers for years now and love it. I’m convinced it will catch on as JetBrains makes progress on implementing it as well. They were trying to create a competing spec, but seems they have finally caved and decided to support it.

1 Answers