Reputation: 960
I've a Node JS file that will watch changes on a folder. It worked good until I decided to migrate it to a Docker Container. I've made a minimal example to recreate.
This is my NodeJS code:
const express = require('express');
const server = express();
const PORT = process.env.PORT || 3000;
const fs = require('fs');
const {
join
} = require('path');
const dirs = p => fs.readdirSync(p).filter(f => fs.statSync(join(p, f)).isDirectory());
const watchingPath= `../${process.env.WATCH}` || "../watchingFolder";
const watching = dirs(watchingPath);
console.log(`Watching ${watching} š`);
watching.forEach(dir => {
var path = `${watchingPath}/${dir}/`;
fs.watch(path, (eventType, filename) => {
fs.readFile(`${path}${filename}`, async (err, data) => {
if (err) return;
console.log(`${path} got ${filename}`);
});
});
});
server.listen(PORT, () => console.log(`Server running on ${PORT}`));
Locally im watching the folder ../watchingFolder
which is binded to a folder in the container named /wFolder
. The binding can be seen on the docker-compose.yml
:
version: "3"
services:
eye:
build: ./EYE
container_name: watchTest
ports:
- 80:3000
restart: on-failure
environment:
- PORT=3000
- WATCH=wfolder
volumes:
- ./watchingFolder:/wfolder
and the Dockerfile
for the image is:
FROM node:latest
RUN mkdir /eye
RUN mkdir /wfolder
ADD . /eye
WORKDIR /eye
RUN npm i
EXPOSE 80
CMD ["npm", "start"]
Now it works fine but for some reason whenever I move a file in my Machine, instead of printing the changes once, it does it Twice.
I'd like to know why does Docker behaves like this.
Thanks.
Upvotes: 2
Views: 2956
Reputation: 74761
tldr; debounce file system events for the same path/type.
The events that fs.watch
receives are operating system dependant as they rely on the underlying "file event watching" implementation of each OS.
In this case there is also an intermediate file system driver between the host and the Docker VM running the containers that does some translation, so may also change what you would see normally triggered from a plain Linux OS compared to what you are seeing from macOS.
If you install the inotify-tools
package into the container you can see a little more detail on the type of CHANGE
node is observing.
Run inotifywait
in the container
root@4210a3a174cc:/app# apt-get update
root@4210a3a174cc:/app# apt-get install -y inotify-tools
root@4210a3a174cc:/app# inotifywait -m /wfolder
Setting up watches.
Watches established.
Then create a file in the container:
ā docker exec 4210a3a174cc bash -c 'echo test > /wfolder/test_in_docker'
Results in:
root@4210a3a174cc:/app# inotifywait -m /wfolder
Setting up watches.
Watches established.
/wfolder/ CREATE test_in_docker
/wfolder/ OPEN test_in_docker
/wfolder/ MODIFY test_in_docker
/wfolder/ CLOSE_WRITE,CLOSE test_in_docker
Then create a file outside the container:
mac$ echo test > wfolder/test_out_of_docker
Results in:
root@4210a3a174cc:/app# inotifywait -m /wfolder
Setting up watches.
Watches established.
/wfolder/ CREATE test_out_of_docker
/wfolder/ MODIFY test_out_of_docker
So there is a file creation, and modify when you put a new file there.
You will also notice that cp
, mv
and rm
behave differently from the host and inside the container and normally produce more than 1 event (except for rm
inside the container)
In all cases though you will get a batch of events in a short period of time, so you can usually debounce them into one call.
Upvotes: 4