Reputation: 11356
I'm trying to set up development containers for a node.js project so that my local source files are shared with the guest (so I can dnsmasq
wildcard requests to a local domain over port 80, but I guess that's irrelevant to the question). When I make a change locally, the node process in the container is restarted:
Dockerfile
:
FROM node:8
# Install app dependencies
RUN npm install -g nodemon
COPY package.json /tmp/package.json
RUN cd /tmp && npm install --production
NODE_PATH=/tmp/node_modules
WORKDIR /app
EXPOSE 8080
EXPOSE 9229
CMD nodemon --inspect index.js
docker-compose.json
(truncated):
version: '3.3'
services:
app:
build: .
container_name: 'my-app'
restart: unless-stopped
ports:
- 8080:8080
- 9229:9229
volumes:
- .:/app
Because the node_modules
are installed in the Dockerfile
, every time I add a package to packages.json
, I have to manually re-build the container:
docker-compose stop
docker-compose build
docker-compose up -d
I could set up a watch for this using inotify, but I don't like to stop the entire stack (including other services defined there).
Is it possible to have this logic inside the container, and re-run npm install
every time package.json
is changed?
Upvotes: 6
Views: 8024
Reputation: 11356
I wrote an entry file that starts two threads in node.js
, so Dockerfile
can be very vanilla. Now all we need is docker-compose
.
Dockerfile
FROM node:8
RUN npm install -g nodemon
docker-compose.yml
version: '3.5'
services:
app:
build: .
user: '1000'
working_dir: /opt/app
ports:
- 8080:8080
- 9229:9229
volumes:
- ./:/opt/app
command: 'node docker-entry.js'
docker-entry.js
/**
* @file docker-entry for development container
* @author Redsandro (https://www.windowsremix.com/)
*/
'use strict'
const { spawn } = require('child_process')
/*
* Install dependencies every time package.json changes
*/
spawn('nodemon -w package.json --exec "npm install"', {
stdio: 'inherit',
shell: true
})
/*
* Restart node when a source file changes, plus:
* Restart when `npm install` ran based on `package-lock.json` changing.
*/
spawn('nodemon --inspect -e js,json -i node_modules -i package.json index.js', {
stdio: 'inherit',
shell: true
})
.dockerignore
# Just ignore everything
**
This works because package-lock.json
changes when dependencies were added or updated. So npm 5 is required. Untested on other node images.
Make sure there is no node_modules
or package-lock.json
in your working directory on the first run. The internal node app will crash on first run (obviously, it cannot find the dependencies) but it will restart once the dependencies are installed.
Enter docker-compose up -d && docker-compose logs -f
to see the results. Make some changes to the files, and add a package to package.json
.
Every time you start the container, npm install
will run, which is not necessary 9 out of 10 times unless you change package.json
while the container was stopped. This is fine, because the node application is started in parallel, you don't have to wait for this to finish. You can use the container immediately. (Unless this is your first run of course.)
To do: I had hoped to move node_modules
to some guest directory (e.g. /tmp
) so all this data would be stored on the docker server rather than on my laptop ssd. More importantly, your host and guest share the same node_modules
now. If you use a non-linux OS and compiled packages, you're gonna have a bad time. Because it will either work in the host, or in the guest, but not in both. I can't seem to specify a different installation directory for npm
like I did before without npm writing to package-lock.json
there too, and that file is necessary in the local directory for this setup to work. I'm sure with some clever linking it can work, but I've exhausted the time I had allotted for this solution. Tips are welcome. Feel free to add addendums and other answers.
Upvotes: 2
Reputation: 82
Is it possible to have this logic inside the container, and re-run npm install every time package.json is changed?
It is not fully clear to me, if your changes in package.json are done on host or in docker container in /tmp/ directory. I guess you modify a file on host since you have other project files there.
If you modify package.json in other location than /tmp/ in your container, you need to manually copy new version of package.json to /tmp in container
docker cp "location/of/package.json" "containername":/tmp/
then you just enter the container and install dependencies.
docker exec -it containername bash
cd /tmp/
npm install
Is it possible to have this logic inside the container, and re-run npm install every time package.json is changed?
You could pass logic described above to simple bash script. Then you can run some watcher (for example nodemon) to observe package.json and run the script after file change. Maybe it would be also good idea to pass big delay so it doesn't try to install a module before you even type full name of it. But in most situations I guess you just use npm command instead of typing name of the module manually.
Upvotes: 2