How to use docker for development

Docker for development environment

LinkShort Version

To fix environment compatibility issues, you could set up a docker environment for development. To do that, create a dockerfile and put all of your project environment dependencies there, things like NodeJS, Git, Python, so on… You can also use docker-compose if you depend on other containers.

Now create a docker volume mapping to your source code and use your development container to run all your commands.

If you're using VSCode, there's an extension that makes all that integrate much better, I'll leave a link for it in the references.

If that short version was enough for you, just leave a like and have a great day! Stick around if you want a deep dive.

LinkTech Introduction

We love Docker, but apparently, we hate our coworkers. Think about it. It's frustratingly common to join a project and go through a full day of onboarding.

But hey, we're getting paid, right? Unless it's Open Source, in that case we just give up trying to contribute.

Sure, it's good to have a guide to the codebase, but it shouldn't take that long. It's just an introduction, after all, you'll only really get familiar with the codebase when you start working on it.

And that's the frustrating part, "working on it". It takes too long to set up the development environment.

You need that specific Node version, and that specific Python version, and an NGIX server running and a Mongo DB populated, and so on… Switching machines becomes infeasible.

Now, imagine joining a project and the only thing you need to have installed is Docker. Then you run a single command and it's all ready.

That's what we're gonna do today.

I'll show you how to set up the development environment for a project that uses Node, Heroku, and MongoDB. When it's done, we'll be running npm install, npm run build, and npm start from inside a container.

LinkSetup for a Node project

So, our project requires Node. But it's not any Node, it's Node version 14.15.4. And NPM 7.5.4 and the latest Heroku CLI available.

Ok, let's write our dockerfile.

dockerfile
FROM node:14.15.4
RUN npm install -g [email protected]
RUN curl https://cli-assets.heroku.com/install.sh | sh
WORKDIR /var/www

Ok, done.

NOTE: That would take at least 15 minutes for any new developers to join your team. And they would constantly run your project with the wrong Node version. Don't thank me later, thank me now.

LinkConnecting to the container

Now let's build that container and run it interactively and with TTY.

bash
docker build --tag dev-container .
docker run --interactive --tty dev-container
# ↑ Same as docker run -it dev-container

NOTE: By the way, I don't know what TTY stands for, I'm sure someone will send a tweet enlightening us. But if you don't use TTY, your container will run, and die... instantly… Unless the command from the image that you're running keeps it alive. But images like Ubuntu don't stay alive by default, so you need TTY for those cases.

Now we're inside the container's shell and we can verify that Node, NPM, and the Heroku CLI are correctly installed.

bash
[email protected]:/var/www node -v
v14.15.4

[email protected]:/var/www npm -v
7.5.4

[email protected]:/var/www heroku -v
heroku/7.47.12 linux-x64 node-v12.16.2

If you kill your process, the container will die too. If that's not what you want, you can run the container detached and use docker exec to connect to it. That way, it won't die when you kill your process.

bash
docker run --detach --tty dev-container
# Use `docker ps` to grab the container ID
docker exec --interactive --tty { container_id } bash

LinkSharing files with volumes

Our container has the tools that we need but it doesn't have access to our source code yet.

To fix this, we'll create a docker volume when we run it.

bash
# Use `docker ps` to grab the container ID
docker kill { container_id }
docker run --detach --tty --volume $(pwd):/var/www dev-container
# Use `docker ps` to grab the new container ID
docker exec --interactive --tty { new_container_id } bash

We can now access our project files inside the container in /var/www.

bash
[email protected]:~ cd /var/www
[email protected]:/var/www ls
dist  node_modules  package-lock.json  package.json  src  tsconfig.json

Let's run npm clean-install and then npm run build, just for the fun of seeing it work.

LinkVSCode integration

Nice, it works!

But running the container and attaching it to it is a pain in the ass.

Also, if you try to use Git inside the container, you'll notice that you're not authenticated to your remote repository. That's another huge turn-off.

The good news is that if you're using VSCode, as all the other cool kids are, you can avoid those issues and make the whole process almost seamless.

Install the Remote - Containers extension. That VSCode extension will automatically build, run and attach the VSCode terminal to your development container shell. It will also handle your git credentials, kill the container when you close VSCode and do some other nice things to make your life easier.

To configure that extension, we need to create a devcontainer.json file inside the .devcontainer folder and also move our dockerfile to that folder. The properties are very clear and the complex ones are better explained in their documentation, so I won't attempt to explain them.

JSON
// For format details, see https://aka.ms/vscode-remote/devcontainer.json or this file's README at:
// https://github.com/microsoft/vscode-dev-containers/tree/v0.134.0/containers/javascript-node
{
  "name": "lucas-paganini-api",

  "build": {
    "dockerfile": "./Dockerfile",
    "context": ".."
  },
  "forwardPorts": [3000],

  "workspaceMount": "source=${localWorkspaceFolder},target=/var/www,type=bind",
  "workspaceFolder": "/var/www",

  "postStartCommand": "npm clean-install",
  "shutdownAction": "stopContainer",

  "settings": {
    "terminal.integrated.shell.linux": "/bin/bash"
  },

  // Add the IDs of extensions you want to be installed when the container is created.
  "extensions": ["ms-vscode-remote.remote-containers", "esbenp.prettier-vscode"]
}

NOTE: You can also declare an array of VSCode extensions that you want to have installed in the development environment. And you can also declare a command to run when the container is ready! Honestly, isn't that awesome?!

When you open the project, it will show a popup message asking if you want to open it inside the dev container. If that doesn't happen, open the VSCode command palette and search for "Remote-Containers: Open Folder in Container", then select the project root folder.

LinkAdding MongoDB with Docker Compose

But what if you depend on other containers? For example, a mongo DB. In that case, you'd need a docker-compose file and a few changes to the devcontainer.json.

yml
# Docker compose for development

version: '3.8'

services:
  mongo-db:
    image: mongo:4.4.3
    networks:
      - main-net

  main:
    build:
      dockerfile: ./.devcontainer/Dockerfile
      context: ..
    image: lucas-paganini-api/main
    ports:
      - '3000:3000'
    networks:
      - main-net
    environment:
      DB_HOST: mongo-db:27017
      PORT: 3000
    depends_on:
      - mongo-db
    tty: true
    volumes:
      - ..:/var/www
    command: bash

networks:
  main-net:
    driver: bridge
JSON
// For format details, see https://aka.ms/vscode-remote/devcontainer.json or this file's README at:
// https://github.com/microsoft/vscode-dev-containers/tree/v0.134.0/containers/javascript-node
{
  "name": "lucas-paganini-api",

  "dockerComposeFile": "./docker-compose.yml",
  "forwardPorts": [3000],
  "service": "main",

  "workspaceFolder": "/var/www",
  "postStartCommand": "npm clean-install",
  "shutdownAction": "stopCompose",

  "settings": {
    "terminal.integrated.shell.linux": "/bin/bash"
  },

  // Add the IDs of extensions you want to be installed when the container is created.
  "extensions": ["ms-vscode-remote.remote-containers", "esbenp.prettier-vscode"]
}

LinkConclusion

I'm sure your projects will benefit from having a normalized development experience, and now you have no excuse to not do it.

I'll leave a link to the repository in the references below.

As always, have a great day and I'll see you soon!

LinkReferences

  1. Repository
  2. How to Setup Your Local Node.js Development Environment Using Docker
  3. How To Setup Your Local Node.js Development Environment Using Docker – Part 2
  4. Why and How To Use Docker for Development
  5. Using Containers for Development
  6. VSCode Extension for Containers
  7. devcontainer.json Reference

Join the newsletter and be the first to know when I launch a course, post a video or write an article.

I don't SPAM, one email per week tops.