Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to Increase the Speed of Docker Builds While Using Mounted Volumes

Docker is slow writing to the host file system when using volumes. This makes tasks like npm install, in NodeJS, incredibly painful. How can I exclude the node_modules folder from the volume so the build is faster?

like image 705
Fred Lackey Avatar asked Jan 18 '17 02:01

Fred Lackey


People also ask

How can I make Docker build faster?

The easiest way to increase the speed of your Docker image build is by specifying a cached image that can be used for subsequent builds. You can specify the cached image by adding the --cache-from argument in your build config file, which will instruct Docker to build using that image as a cache source.

Why is my Docker build taking too long?

If your Docker image builds takes a long time downloading dependencies, it's a good idea to check whether you're installing more than you need to. First, check if you might be downloading development dependencies which are not needed in your image at all.

Can you mount a volume while building your Docker image?

When building an image, you can't mount a volume. However, you can copy data from another image! By combining this, with a multi-stage build, you can pre-compute an expensive operation once, and re-use the resulting state as a starting point for future iterations.

Why is my Docker container so slow?

When you experience slow Docker performance, check your CPU, memory usage, and available disk space. Consider upgrading your system if a component does not perform as expected. When dealing with a specific container that is performing worse than expected, it may be helpful to check container-specific metrics.


Video Answer


1 Answers

It's common knowledge that Docker's mounted volume support on macOS is pathetically slow (click here for more info). For us Node developers, this means starting up your app is incredibly slow because of the requisite node install command. Well, here's a quick lil' trick to get around that slowness.

First, a quick look at the project:

uber-cool-microservice example
(source: fredlackey.com)

Long story short, I'm mapping everything in my project's root (./) to one of the container's volumes. This allows me to use widgets, like gulp.watch() and nodemon to automagically restart the project, or inject any new code, whenever I modifiy a file.

This is 50% of the actual problem!

Because the root of the project is being mapped to the working directory within the container, calling npm install causes node_modules to be created in the root... which is actually on the host file system. This is where Docker's incredibly slow mounted volumes kick the project in the nads. As is, you could spend as long as five minutes waiting for your project to come up once you issue docker-compose up.

"Your Docker setup must be wrong!"

As you'll see, Docker is quite vanilla for this lil' project.

First, ye 'ole Dockerfile:

FROM ubuntu:16.04

MAINTAINER "Fred Lackey" <[email protected]>

RUN mkdir -p /var/www \
    && echo '{ "allow_root": true }' > /root/.bowerrc \
    && apt-get update \
    && apt-get install -y curl git \
    && curl -sL https://deb.nodesource.com/setup_6.x | bash - \
    && apt-get install -y nodejs \
    && npm install -g bower gulp gulp-cli jshint nodemon npm-check-updates

VOLUME /var/www

EXPOSE 3000

And, of course, the beloved docker-compose.yml:

version: '2'

services:

  uber-cool-microservice:
    build:
      context: .
    container_name: uber-cool-microservice
    command:
      bash -c "npm install && nodemon"
    volumes:
      - .:/var/www
    working_dir: /var/www
    ports:
      - "3000"

As you can see, as-is this test project is lean, mean, and works as expected.... except that the npm install is sloooooooooow.

At this point, calling npm install causes all of the project's dependencies to be installed to the volume which, as we all know, is the host filesystem. This is where the pain comes in.

"So what's the 'trick' you mentioned?"

If only we could benefit from having the root of the project mapped to the volume but somehow exclude node_modules and allow it to be written to Docker's union file system inside of the container.

According to Docker's docs, excluding a folder from the volume mount is not possible. Which, makes sense I guess.

However, it is actually possible!

The trick? Simple! An additional volume mount!

By adding one line to the Dockerfile:

FROM ubuntu:16.04

MAINTAINER "Fred Lackey" 

RUN mkdir -p /var/www \
    && echo '{ "allow_root": true }' > /root/.bowerrc \
    && apt-get update \
    && apt-get install -y curl git \
    && curl -sL https://deb.nodesource.com/setup_6.x | bash - \
    && apt-get install -y nodejs \
    && npm install -g bower gulp gulp-cli jshint nodemon npm-check-updates

VOLUME /var/www
VOLUME /var/www/node_modules

EXPOSE 3000

... and one line to the docker-compose.yml file ...

version: '2'

services:

  uber-cool-microservice:
    build:
      context: .
    container_name: uber-cool-microservice
    command:
      bash -c "npm install && nodemon"
    volumes:
      - .:/var/www
      - /var/www/node_modules
    working_dir: /var/www
    ports:
      - "3000"

That's it!

In case you missed it, we added:

VOLUME /var/www/node_modules

and

- /var/www/node_modules

Say what!?!?

In short, the additional volume causes Docker to create the internal hooks within the container (folder, etc.) and wait for it to be mounted. Since we are never mounting the folder, we basically trick Docker into just writing to the folder within the container.

The end result is we are able to mount the root of our project, take advantage of tools like gulp.watch() and nodemon, while writing the contents of node_modules to the much faster union file system.

Quick Note re: node_modules:
For some reason, while using this technique, Docker will still create the node_modules folder within the root of your project, on the host file system. It simply will not write to it.

The original article is on my blog.

like image 99
Fred Lackey Avatar answered Oct 05 '22 01:10

Fred Lackey