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:
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" <fred.lackey@gmail.com>
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" <fred.lackey@gmail.com>
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 thenode_modules
folder within the root of your project, on the host file system. It simply will not write to it.