Developing inside Remote Development Containers with VS Code

Developing inside Remote Development Containers with VS Code

As software developers, to start working on a new or an existing project, we need to setup a development environment. Setting up development environment includes downloading, installing, configuring the required dependencies needed to run our project. It also includes configuring different tools that help us during development. Some examples of such tools are webpack which is javascript module bundler and eslint which is a linter for javascript code.

Setting up the development environment can be a complicated task, specially if it requires the installation and configuration of a lots of different tools and technologies. It can be even more challenging if the steps of setting up a development environment are not properly documented.

Wouldn't it be nice if we could have a pre-built development environment which doesn't requires us to install or configure anything and allows us to immediately start working on the project? We can do this using containers and VS Code.

Idea is to create a development environment inside a container and then connect to that container using VS Code. One major advantage of this approach is that we can share the development environment with others and they can get up and running without installing and configuring all the dependencies needed to start working on the project.

Pre-Requisites

To follow this tutorial, you need to have the following tools and technologies installed on your machine:

Following are the exact versions that i will be using for this tutorial:

  • Docker version 20.10.3, build 48d30b5
  • VS Code Version: 1.53.0
  • NodeJS v15.8.0

You don't need to be a Docker expert to follow this tutorial but this tutorial will make more sense to you if you have a basic understanding of Docker and containers in general. As we will be creaing a simple nodejs application, basic understanding of nodejs will help as well.

Setting up a Sample Project

Let's create a simple nodejs application using express framework.

Create a directory/folder that will contain all the project files. I will use command-line to create a directory and then navigate into that directory.

mkdir my-node-app
cd my-node-app

Open the newly created project directory inside VS Code.

Once inside the project directory, run the following command to create the package.json file.

npm init -y

Now install the express framework using the following command:

npm install express

We will also setup eslint in our project, so run the following command to install the required packages from NPM.

npm i -D eslint eslint-plugin-import eslint-config-airbnb-base

Create a configuration file for eslint named .eslintrc.js in the root directory of the project and copy the following code inside this file:

module.exports = {
  env: {
    browser: true,
    commonjs: true,
    es2021: true
  },
  extends: ["airbnb-base"],
  parserOptions: {
    ecmaVersion: 12
  },
  rules: {
    quotes: ["warn", "double"],
    "comma-dangle": ["warn", "never"],
    "no-console": "off"
  }
};

(Note: installing and configuring eslint is optional, you can skip it if you want to.)

Now create a javascript file that will contain the code of our simple express application. You can name the file whatever you want, i will name it "index.js" and will use the following command to create the file.

touch index.js

Copy the following code into the javascript file created above.

const express = require("express");

const app = express();

app.use((req, res, next) => {
  res.setHeader("Content-Type", "text/html");
  next();
});

app.get("/", (req, res) => {
  res.status(200).send("<h1>Home Page</h1>");
});

app.get("/about", (req, res) => {
  res.status(200).send("<h1>About Page</h1>");
});

app.listen(3000, () => {
  console.log("Server started on PORT: 3000");
});

It's a simple express application that contains two routes that return a HTML string. "/" route returns "Home Page" inside h1 element and the route "/about" returns "About Page" inside h1 element.

Inside the package.json file, add a start script that will be used to start the express application.

// other stuff
"scripts": {
    "start": "node index.js"
 }
// other stuff

Setting up a Development Container

We have a sample nodejs application to work with. Now we can move onto creating a development environment inside a container.

First thing we need is Remote - Container extension for VS Code. This extension allows us to use a Docker container as a development environment. Using this extension, we can open any folder, on our local machine, inside a container and take full advantage of development using VS Code. Using this extension, we can install and run VS Code's extensions inside a container and we will also be able to define different settings we want for VS Code like theme, default code formatter, font size, etc.

Creating Dockerfile and devcontainer.json File

To create a development environment, we need to create two files:

  • devcontainer.json - this file will contain the container configuration that will be used by VS Code to create and launch a container with required configurations.

  • Dockerfile - this file will be used inside devcontainer.json file and VS Code will use it to to create a base container image with additional softwares that we might need inside the container to help us during development.

    In our case, for a nodejs application, we need a base container with nodejs and npm installed.

Both of the above files need to be created inside .devcontainer directory. Create this folder in root directory of the project. Running the following command inside the root directory of our project will create the required folder.

mkdir .devcontainer

(folder name starts with "." character: .devcontainer)

Inside the .devcontainer folder, create the two files mentioned previously. Running the following commands from the root directory of the project will create the required files inside the .devcontainer folder.

touch .devcontainer/devcontainer.json 
touch .devcontainer/Dockerfile

At this point, following is the structure of our project:

- my-node-app
--- .devcontainer
------ Dockerfile
------ devcontainer.json
--- node_modules
--- index.js
--- .eslintrc.js
--- package-lock.json
--- package.json

Copy the following text inside Dockerfile:

FROM node:15-stretch

Our Dockerfile contains only one step and it specifies the base image to use which in our case is node's official image, node:15-stretch to be specific. That's all we need for the Dockerfile.

Inside the devcontainer.json file, we need to define the configuration for our development container.

Copy the following inside the devcontainer.json file:

{
  // name of the container
  "name": "My Node App",

  // path to Dockerfile that defines the
  // contents of the container
  "dockerFile": "Dockerfile",

  // settings to use for VS Code inside the container
  "settings": {
    "terminal.integrated.shell.linux": "/bin/bash",
    "files.autoSave": "onFocusChange",
    "editor.defaultFormatter": "esbenp.prettier-vscode",
    "eslint.run": "onSave",
    "workbench.colorTheme": "Midnight City"
  },

  // ports that should be forwarded from
  // inside the container to the local machine
  "forwardPorts": [3000],

  // VS Code extensions to install inside the container
  // array should contain the extension IDs
  "extensions": [
    "esbenp.prettier-vscode", // prettier
    "dillonchanis.midnight-city", // Midnight City theme
    "dbaeumer.vscode-eslint" // eslint
  ],

  // command to run after container is created
  "postCreateCommand": "npm install"
}

Running our Project inside a Container

At this point, we have everything we need to start working on our project inside a container. Now, we need to close VS Code and re-open our project again. To avoid closing and re-opening it again, we can reload VS Code, which can be done using the ctrl + R on windows or cmd + R on a mac.

When you reopen or reload VS Code, you should see the following notification at the bottom right corner in VS Code:

Screenshot from 2021-02-09 18-51-04.png

Click on "Reopen in Container" button. You can also open the project inside a container using the Remote - Container extension button in the bottom left corner in VS Code. It will be a green colored button with two arrows. Clicking on it will open the following interface:

Screenshot from 2021-02-09 21-21-27.png

Click on "Reopen in Container" option to open your project inside a container that will contain the environment and settings we defined inside the devcontainer.json file.

When we open our project inside a container, VS Code will create a container with pre-defined settings. Once the container is started, we can open the integrated terminal of VS Code and run the following command to start our express server:

npm start

Once the server is started, open the browser and go to the following URL:

localhost:3000

Following image shows VS Code connecting to a development container:

Peek 2021-02-09 23-35.gif

Notice the change of VS Code's theme. This happens when VS Code has created and connected to a development container and the settings we specified inside devcontainer.json file have been applied.

Making Changes to Code inside the Container

It is important to note that our project folder, that we opened inside a container through VS Code, is shared between our local machine and the container. This means that any changes you that we make inside the index.js file will be reflected in our project folder on our local machine.

Our project folder exists on our local machine but the development environment exists inside the container that VS Code created for us.

Benefits of VS Code's Dev Containers

Using a Dev Container is a compelling workflow to work on a codebase. Any time someone open's our project inside VS Code with Docker and Remote - Container extension installed), they will have the option to open the project in a container. If they open the project in a container, VS Code will set up the development environment for them automatically.

Another major advantage of using this workflow is that we can seamlessly switch our development environment by just connecting to a different development container.

How to Exit a Development Container?

Once you are inside a development container, you can exit out of it by clicking the Remote - Container extension button in bottom right corner of VS Code. Clicking it will open a window for you which will contain a "Close Remote Connection" option. Clicking on it will get you out of the development container.

Where to go from Here?

In this article you learned how to setup a development container with a simple nodejs application. To dive deeper into creating development containers and different configuration options that you can set for development containers, visit the following links:

Did you find this article valuable?

Support Yousaf Khan by becoming a sponsor. Any amount is appreciated!