I'm pretty new with Docker and i wanted to map the node_modules folder on my computer (for debugging purpose).
This is my docker-compose.yml
web:
build: .
ports:
- "3000:3000"
links:
- db
environment:
PORT: 3000
volumes:
- .:/usr/src/app
- /usr/src/app/node_modules
db:
image: mongo:3.3
ports:
- "27017:27017"
command: "--smallfiles --logpath=/dev/null"
I'm with Docker for Mac. When i run docker-compose up -d
all go right, but it create a node_modules folder on my computer but it's empty. I go into the bash of my container and ls node_modules, all the packages was there.
How can i get the content on the container on my computer too?
Thank you
TL;DR Working example, clone and try: https://github.com/xbx/base-server
You need a node_modules in your computer (outside image) for debugging purposes first (before run the container).
If you want debug only node_modules:
volumes:
- /path/to/node_modules:/usr/src/app/node_modules
If you want debug both your code and the node_modules:
volumes:
- .:/usr/src/app/
Remember that you will need run npm install
at least one time outside the container (or copy the npm_modules that the docker build
generates). Let me now if you need more details.
Edit. So, without the need of npm in OSX, you can:
docker build
and then docker cp <container-id>:/path/to/node-modules ./local-node-modules/
. Then in your docker-compose.yml mount those files and troubleshot whatever you want.
- Or,
docker build
and there (Dockerfile) do the npm install
in another directory. Then in your command (CMD or docker-compose command) do the copy (cp
) to the right directory, but this directory is mounted empty from your computer (a volume in the docker-compose.yml) and then troubleshot whatever you want.
Edit 2. (Option 2) Working example, clone and try: https://github.com/xbx/base-server
I did it all automatically in this repo forked from the yours.
Dockerfile
FROM node:6.3
# Install app dependencies
RUN mkdir /build-dir
WORKDIR /build-dir
COPY package.json /build-dir
RUN npm install -g babel babel-runtime babel-register mocha nodemon
RUN npm install
# Create app directory
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
RUN ln -s /build-dir/node_modules node_modules
# Bundle app source
COPY . /usr/src/app
EXPOSE 1234
CMD [ "npm", "start" ]
docker-compose.yml
web:
build: .
ports:
- "1234:1234"
links:
- db # liaison avec la DB
environment:
PORT: 1234
command: /command.sh
volumes:
- ./src/:/usr/src/app/src/
- ./node_modules:/usr/src/app/node_modules
- ./command.sh:/command.sh
db:
image: mongo:3.3
ports:
- "27017:27017"
command: "--smallfiles --logpath=/dev/null"
command.sh
#!/bin/bash
cp -r /build-dir/node_modules/ /usr/src/app/
exec npm start
Please, clone my repo and do docker-compose up
. It does what you want.
PS: It can be improved to do the same in a better way (ie best practices, etc)
I'm in OSX and it works for me.
First, there's an order of operations. When you build your image, volumes are not mounted, they only get mounted when you run the container. So when you are finished with the build, all the changes will only exist inside the image, not in any volume. If you mount a volume on a directory, it overlays whatever was from the image at that location, hiding those contents from view (with one initialization exception, see below).
Next is the volume syntax:
volumes:
- .:/usr/src/app
- /usr/src/app/node_modules
tells docker-compose to create a host volume from the current directory to /usr/src/app
inside the container, and then to map /usr/src/app/node_modules
to an anonymous volume maintained by docker. The latter will appear as a volume in docker volume ls
with a long uuid string that is relatively useless.
To map /usr/src/app/node_modules
to a folder on your host, you'll need to include a folder name and colon in front of that like you have on the line above. E.g. /host/dir/node_modules:/usr/src/app/node_modules
.
Named volumes are a bit different than host volumes in that docker maintains them with a name you can see in docker volume ls
. You reference these volumes with just a name instead of a path. So node_modules:/usr/src/app/node_modules
would create a volume called node_modules
that you can mount in a container with just that name.
I diverged to describe named volumes because they come with a feature that turns into a gotcha with host volumes. Docker helps you out with named volumes by initializing them with the contents of the image at that location. So in the above example, if the named volume node_modules
is empty (or new), it will first copy the contents of the image at /usr/src/app/node_modules` to this volume and then mount it inside your container.
With host volumes, you will never see any initialization, whatever is at that
location, even an empty directory, is all you see in the container. There's no way to get contents from the image at that directory location to first copy out to the host volume at that location. This also means that directory permissions needed inside the container are not inherited automatically, you need to manually set the permissions on the host directory that will work inside the container.
Finally, there's a small gotcha with docker for windows and mac, they run inside a VM, and your host volumes are mounted to the VM. To get the volume mounted to the host, you have to configure the application to share the folder in your host to the VM, and then mount the volume in the VM into the container. By default, on Mac, the /Users folder is included, but if you use other directories, e.g. a /Projects directory, or even a lower case /users (unix and bsd are case sensitive), you won't see the contents from your Mac inside the container.
With that base knowledge covered, one possible solution is to redesign your workflow to get the directory contents from the image copied out to the host. First you need to copy the files to a different location inside your image. Then you need to copy the files from that saved image location to the volume mount location on container startup. When you do the latter, you should note that you are defeating the purpose of having a volume (persistence) and may want to consider adding some logic to be more selective about when you run the copy. To start, add an entrypoint.sh to your build that looks like:
#!/bin/sh
# copy from the image backup location to the volume mount
cp -a /usr/src/app_backup/node_modules/* /usr/src/app/node_modules/
# this next line runs the docker command
exec "$@"
Then update your Dockerfile to include the entrypoint and a backup command:
FROM node:6.3
# Create app directory
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
# Install app dependencies
COPY package.json /usr/src/app/
RUN npm install -g babel babel-runtime babel-register mocha nodemon
RUN npm install
# Bundle app source
COPY . /usr/src/app
RUN cp -a /usr/src/app/. /usr/src/app_backup
EXPOSE 1234
ENTRYPOINT [ "/usr/src/app/entrypoint.sh" ]
CMD [ "npm", "start" ]
And then drop the extra volume from your docker-compose.yml:
volumes:
- .:/usr/src/app
change:
volumes:
- .:/usr/src/app
- /usr/src/app/node_modules
TO:
volumes:
- .:/usr/src/app
And it will put the node_modules in your local mapped volume. The way you have it, the /usr/src/app/node_modules
will be stored in different volume that you would need to docker inspect {container-name}
to find the location. If you do want to specify the location, specify it like:
- /path/to/my_node_modules:/usr/src/app/node_modules