Naresh
Naresh

Reputation: 1251

How volume mounting works in docker-compose

I have a very simple node js app and the project structure looks like this.

index.js

package.json

package-lock.json

Dockerfile

FROM node:12.18.2-alpine
WORKDIR /test-app
COPY package.json package-lock.json ./
RUN npm i
COPY . ./
EXPOSE 3000
ENTRYPOINT [ "node", "index.js" ]

docker-compose.yml

version: '3.2'

services:
  test-app:
    build: .
    ports:
      - "3000:3000"
    volumes:
      - .:/test-app
      - "test_app_node_modules:/test-app/node_modules"
volumes:
  test_app_node_modules:
    driver: local

If you look at the volumes section in docker-compose.yml file, first I'm bind mounting my current directory on the host machine to the test-app directory on the container. This means :

  1. whatever files or directories that were inside my current dir will get reflected on the container dir and any changes made to the container dir will also get reflected back to the host dir.
  2. this means node_modules that were installed in the test-app dir of the container, during docker build, were overwritten as well.

and the next step in the volumes section is named volumes. This means:

  1. It should copy everything from test-app/node_modules inside container to test_app_node_modules volume. But the test-app/node_modules is empty because step 1 overwrote it.
  2. which means we created an empty volume and mounted it to the container.

If this is so, it should be causing missing dependency error but my app is running properly. I'm not sure where I'm getting node_modules from.

Also, I see an empty node_modules folder in the host directory. I assume the reason behind this is "test_app_node_modules:/test-app/node_modules" looks for the node_modules in the container but it doesn't exist so it creates one and as a result, it gets reflected back to the host dir.

I'm not able to grasp the idea of volume mounting. What is the flow here? How node_modules are begin stored into the volumes when there are none?

Upvotes: 0

Views: 2216

Answers (3)

David Maze
David Maze

Reputation: 158908

At a purely mechanical level, a couple of things happen here.

Docker sorts the volume mounts. It knows that /test-app/node_modules is a subdirectory of /test-app. So Docker will

  1. Create the bind mount from the host directory to the container directory
  2. Create an empty node_modules directory if required to be a mount point
  3. Mount the Docker volume on that mount point

This is where the empty node_modules directory on the host comes from: Docker creates it to be a mount point, after it's done the bind mount, so the changes there are reflected in the host content.

When the named volume is mounted in the container, if and only if the named volume is empty, Docker will copy the content from the image into the volume; this specific step ignores that there's already a volume mounted in the container. (This also works if the volume is an anonymous volume, which you see in other Node examples; it does not work for host bind mounts, if older content is in the volume, or on Kubernetes.)

So that's why this apparently works. As the other answers to this question note, this isn't an especially effective use of Docker. I'd also recommend just deleting these volumes:, and directly running node index.js on the host if you need a live development setup.

Upvotes: 1

Hugh
Hugh

Reputation: 380

I'm not really sure why you would want to set up a docker container in this way, but the reason it's not working is due to a misunderstanding of the direction in which volumes and bind-mounts work. You say:

It should copy everything from test-app/node_modules inside container to test_app_node_modules volume. But the test-app/node_modules is empty because step 1 overwrote it.

This is back to front. When you use a volume, the volume is copied into the target. This is the whole point of volumes - they're designed to allow you to persist data even if you rebuild the container. If you use a bind mounted volume then the host directory is copied into the target in the docker container. So your test_app_node_modules directory on the host machine is copied into /test-app/node_modules in the container. Presumably test_app_node_modules contains all your node modules, hence you get no errors about missing modules.

It's only once your container is actually running that the code running in the container can update/delete data in the volume - not when you're building the container.

Upvotes: 0

Harsh Manvar
Harsh Manvar

Reputation: 30113

in your docker file you have first created WORKDIR /test-app inside it you have added a package.json file and installed dependencies RUN npm i so now there is already node_module present inside docker image itself.

after that using COPY . ./ you are adding extra file like index and other all to the docker image.

if you will remove whole volume part then also it will work as your docker image contain code and it's dependencies.

version: '3.2'

services:
  test-app:
    build: .
    ports:
      - "3000:3000"

Upvotes: 0

Related Questions