DEV Community

Lucas Paganini
Lucas Paganini

Posted on • Originally published at lucaspaganini.com

How to use docker for development

Docker for development environment


See this and many other articles at lucaspaganini.com

Short 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.

Tech 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 NGINX 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 going to 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.

Setup 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.

FROM node:14.15.4
RUN npm install -g npm@7.5.4
RUN curl https://cli-assets.heroku.com/install.sh | sh
WORKDIR /var/www
Enter fullscreen mode Exit fullscreen mode

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.

Connecting to the container

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

docker build --tag dev-container .
docker run --interactive --tty dev-container
# ↑ Same as docker run -it dev-container
Enter fullscreen mode Exit fullscreen mode

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.

root@90967edcf8f1:/var/www node -v
v14.15.4

root@90967edcf8f1:/var/www npm -v
7.5.4

root@90967edcf8f1:/var/www heroku -v
heroku/7.47.12 linux-x64 node-v12.16.2
Enter fullscreen mode Exit fullscreen mode

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.

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

Sharing 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.

# 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
Enter fullscreen mode Exit fullscreen mode

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

root@90967edcf8f1:~ cd /var/www
root@90967edcf8f1:/var/www ls
dist  node_modules  package-lock.json  package.json  src  tsconfig.json
Enter fullscreen mode Exit fullscreen mode

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

VSCode 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.

// 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"]
}
Enter fullscreen mode Exit fullscreen mode

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.

Adding 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.

# 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
Enter fullscreen mode Exit fullscreen mode
// 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"]
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

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 see you soon!

References

  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

Top comments (1)

Collapse
 
nikfp profile image
Nik F P

Thanks for the clear and short guide on this, exactly what's needed! The official docs are pretty verbose in standard Microsoft fashion. (not a complaint unless you are in a hurry)

One thing you can add as well is that for VS Code, Microsoft already has a number of dev containers pre-built so you can try things out. Once the Remote Containers extension is installed, go to the command pallet and look for "Remote-Containers: Try a development container sample". another menu pops up with 8 of the most common so you can just pop into one and start hacking.