How volume mounting works in docker-compose - node.js

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 :
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.
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:
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.
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?

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
Create the bind mount from the host directory to the container directory
Create an empty node_modules directory if required to be a mount point
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.

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"

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.

Related

Specifying where Docker stores/places files on Linux VM

Relatively new to Docker so trying to understand how to accomplish my task.
Locally, I:
Build the image
Push the image to some URL
SSH into Linux VM
docker pull image from URL
docker run image_name
This image, when run, downloads 2 fairly large csv.gz's. When unzipped, the two CSV's are about 15GB each.
I set up /app on the Linux VM to have 200GB available. So, in short, I need to have the Docker image download those 2 CSV's there. However no matter what I've tried within my Dockerfile, I see
'No space left on device' when it gets to the part to download the CSVs.
I've tried to set WORKDIR to /app, but that does not help.
Do I need to use a daemon.json file? Does some sort of Docker setting need to be changed on the Linux VM? Do I need to look into Docker volumes?
Relevant pieces of Dockerfile:
FROM centos/python-36-centos7
USER root
WORKDIR /usr/src/app
COPY . .
As for /usr/src/app, I've never seen anything in there. I normally use /usr/src/app since that's what I use for my Cloud Foundry deployments.
Any insight to point me in the right direction would be appreciated.
Doing the following resolved the issue:
Create (if daemon.json does not exist): /etc/docker/daemon.json
And write
{
“data-root”: “/app”
}
Looks like by default everything goes to /var, and in my case, /var only has 4GB of space. /app is where the 200GB resides.
You will have to restart docker service when creating/saving daemon.json.
Referenced this answer: the one with 88 upvotes

Docker does not update volume when updating .sql file

I have a docker compose file that looks like this
version: '3'
services:
webapp:
build: '.'
ports:
- "8000:8000"
networks:
- db
postgres:
image: "postgres:alpine"
environment:
POSTGRES_PASSWORD: "password"
volumes:
- "./scripts:/docker-entrypoint-initdb.d"
networks:
- db
networks:
db:
The scripts folder looks like this:
|- scripts
|-- init.sh
|-- init.sql
The Problem
My workflow for this project is progressive, so I add some SQL initialization data on my host OS, run sudo docker-compose down -v and then sudo docker-compose up. I did not update my user to not need the use of sudo for this scenario.
When I update the init.sh file, then these updates are reflected each time I run docker-compose up. The init.sql file however, only remembers the first "version" of this file. Any subsequent updates are ignored when running docker-compose up.
Things I tried
Tried sudo docker-compose up --renew-anon-volumes --force-recreate which also does not seem to help.
Tried pruning all the volumes with sudo docker volume prune. Does not help
Tried pruning the docker system with sudo docker system prune
What does work is if I copy the file and it's content to a new file name. Renaming the file does not work
So the question is simply, how do I get content updates of init.sql to be recognized by my docker compose setup?? I don't understand why changes to init.sh is picked up but changes to init.sql are ignored?
UPDATE
One important piece of information is that the project is sitting on a virtualbox shared folder, so the underlying file system is vboxsf while all of this is happening.
So it turns out that the underlying file system is playing a role here when using Docker Volumes. I have been using a virtualbox vm and the project was sitting on a vboxsf file system. So when attaching a volume in my docker compose scenario(?), it has been attaching to a vboxsf volume this whole time.
When I moved the project from the vboxsf filesystem to something else (whatever my home directory filesystem has, ext4 I think) then updates to the files worked as expected.
-----------I speak under correction here, link is important to track--------------
My understanding is that the way vboxsf works is that changes are broadcasted between host and guest filesystems and this is picked up by the host and guest OS. There is also an aspect about how shared memory is accessed, but I really don't have that kind of knowledge to elaborate on it further.
To understand the issue, this link seems to be the best resource for now:
https://www.virtualbox.org/ticket/819?cversion=0&cnum_hist=70
-------------------End----------------------------------
I don't think that this will be a problem in production, but it will definitely make you question your sanity for local development.
So please, when you are using a Linux VM for development, check which filesystem your Docker Volumes are using before you even start working on a project.
There is no error messages at all, which is the one of the worst circumstances to be in when debugging this problem!!
I also wasted about 2 days worth of life trying to figure out what's going on and how to fix it. Hopefully this 2 wasted days can result in many days saved instead :D

Nvidia-docker add folder to container

I'm pretty new to docker containers. I understand there are ADD and COPY operations so a container can see files. How does one give the container access to a given directory where I can put my datasets?
Let's say I have a /home/username/dataset directory how do I make it at /dataset or something in the docker container so I can reference it?
Is there a way for the container to reference a directory on the main system so you don't have to have duplicate files. Some of these datasets will be quite large and while I can delete the original after copying it over .. that's just annoying if I want to do something outside the docker container with the files.
You cannot do that during the build time. If you want to do it during build time then you need to copy it into the context
Or else when you run the container you need to do a volume bind mount
docker run -it -v /home/username/dataset:/dataset <image>
Directories on host can be mapped to directories inside container.
If you are using docker run to start your container, then you can include -v flag to include volumes.
docker run --rm -v "/home/username/dataset:/dataset" <image_name>
If you are using a compose file, you may include volumes using:
volumes:
- /home/<username>/dataset:/dataset
For a detailed description of how to use volumes, you may visit Use volumes in docker

Docker -- mounting a volume not behaving like regular mount

I am new to docker so I am certain I am doing something wrong. I am also not a php developer but that shouldn't matter in this case.
I am using a drupal docker image which has data at the /var/www/html directory.
I am attempting to overwrite this data with a drupal site from a local directory on the host system.
According to the docs this is the expected behavior
Mount a host directory as a data volume
In addition to creating a
volume using the -v flag you can also mount a directory from your
Docker engine’s host into a container.
$ docker run -d -P --name web -v /src/webapp:/webapp training/webapp
python app.py
This command mounts the host directory, /src/webapp,
into the container at /webapp. If the path /webapp already exists
inside the container’s image, the /src/webapp mount overlays but does
not remove the pre-existing content. Once the mount is removed, the
content is accessible again. This is consistent with the expected
behavior of the mount command.
However I am finding that the local drupal site files do not exist on the container. My complete workflow is as follows:
docker-compose.yml
drupal:
container_name: empower_drupal
build: ./build/drupal-local-codebase
ports:
- "8888:80"
- "8022:22"
- "443"
#volumes: THIS IS ALSO NOT WORKING
#- /home/sameh/empower-tap:/var/www/html
$ docker-compose up -d
# edit the container by snapshotting it
$ docker commit empower_drupal empower_drupal1
$ docker run -d -P --name empower_drupal2 -v /home/sameh/empower-tap:/var/ww/html empower_drupal1
# snapshot the container to examine it
$ docker commit 9cfeca48efd3 empower_drupal2
$ docker run -t -i empower_drupal2 /bin/bash
The empower_drupal2 container does not have the correct files from the /home/sameh/empower-tap directory.
Why this did not work
Here's what you did, with some annotations.
$ docker-compose up -d
Given your docker-compose.yml, with the volumes section commented out, at this point you have running container, but no volumes mounted.
# edit the container by snapshotting it
$ docker commit empower_drupal empower_drupal1
All you've really done here is made a copy of the image you had already, unless your container makes changes to itself on startup.
$ docker run -d -P --name empower_drupal2 -v /home/sameh/empower-tap:/var/ww/html empower_drupal1
Here you have run your new copy, mounted a volume. Ok, the files are available in this container now.
# snapshot the container to examine it
$ docker commit 9cfeca48efd3 empower_drupal2
I'm assuming here that you wanted to commit the contents of the volume into the image. That will not work. The commit documentation is clear about this point:
The commit operation will not include any data contained in volumes mounted inside the container.
$ docker run -t -i empower_drupal2 /bin/bash
So, as you found, when you run the image generated by commit, but without volume mounts, the files are not there.
Also, it is not clear in your docker-compose.yml example where the volumes: section was before it was commented out. Currently it seems to be on the left margin, which would not work. It would need to be at the same level as build: and ports: in order to work on your drupal service.
What to do instead
That depends on your goal.
Just copy the files from local
If you literally just want to populate the image with the files from your local system, you can do that in Dockerfile.
COPY local-dir/* /var/www/html
You mentioned that this copy can't work because the directory is not local. Unfortunately that cannot be solved easily with something like a symlink. Your best option is to copy the directory to the local context before building. Docker does not plan to change this behavior.
Override contents for development
A common scenario is you want to use your local directory for development, so that changes are reflected right away instead of doing a rebuild. But when not doing development, you want the files baked into the image.
In that case, start by telling Dockerfile to copy the files into the image, as above. That way an image build will contain them, volume mount or no.
Then, when you are doing development, use volumes: in docker-compose.yml, or the -v flag to docker run, to mount a volume. A volume mount will override whatever is baked into the image, so you will be using your local files. When you're done and the code is ready to go, just do an image build and your final files will be baked into the image for deployment.
Use a volume plus a commit
You can also do this in a slightly roundabout way by mounting the volume, copying the contents elswhere, then committing the result.
# start a container with the volume mounted somewhere
docker run -d -v /home/sameh/empower-tap:/var/www/html_temp [...etc...]
# copy the files elsewhere inside the container
docker exec <container-name> cp -r /var/www/html_temp /var/www/html
# commit the result
docker commit empower_drupal empower_drupal1
Then you should have your mounted volume files in the resulting image.

docker's VOLUME isn't mounting host files

I have this docker file:
FROM node:0.10.38
VOLUME /opt/build
WORKDIR /opt/build
EXPOSE 8080
CMD node app.js
However, when I got to run the container, I get the error that app.js does not exist, specifically /opt/build/app.js does not exist. I've also tried ADD and COPY to do this, both say it can't find anything, and I'd like to use VOLUME so it writes to the host's log that is also in /opt/build. All desired files are in fact on the host as well. By the way, I'm using coreos as the host OS.
As documented in http://docs.docker.com/userguide/dockervolumes/#volume --
The VOLUME Dockerfile command is equivalent to -v /opt/build ("Adding a data volume"), not equivalent to -v /opt/build:/opt/build ("Mount a Host Directory as a Data Volume"); the latter is what you want here.

Resources