Reputation: 3962
I have the following Docker Compose
project (files contents provided below):
.
|-- .dockerignore
|-- Dockerfile
|-- docker-compose.yml
|-- messages
|-- 20221120-010625.txt
|-- 20221120-010630.txt
`-- 20221120-010641.txt
|-- package.json
`-- server.js
When you run the Docker Compose
project with the following command:
$ docker-compose up -d
you can go to the url: http://localhost/?message=<message>
and record multiple messages on the server.
Here you have an example:
So far so good, but...
My use case is: Some times I need to update the source code of the website. For example, imagine I need to prefix the page text on the screenshot above: Created file ...
with: ###
like:
### Created file: "/var/www/html/messages/20221120-010641.txt" with content: "this is a test".
BUT I cannot mess with the existing messages because that's valuable data for the server app.
I tried with the following commands:
$ docker-compose down --volumes
$ docker-compose up -d --force-recreate --build
My problem is: after updating the source code accordingly, even though the page text got properly updated, all the messages got lost, which is not good.
Could you please indicate me how can I achieve this?
I tried by defining a named volume inside the docker-compose.yml
like:
services:
serverapp:
...
volumes:
- messages:/var/www/html/messages
volumes:
messages:
... expecting that if I destroy the server app the messages persist, but that didn't work because that named volume was owned by the user root
and the messages are created by the user: node
which doesn't have permission to create files on that directory, which causes an error.
Here is the content of the involved files:
.dockerignore
/node_modules/
/messages/
/npm-debug.log
Dockerfile
FROM node:16-alpine
RUN mkdir -p /var/www/html && chown -R node:node /var/www/html
WORKDIR /var/www/html
COPY --chown=node:node . .
USER node
RUN npm i
EXPOSE 8080
CMD [ "npm", "run", "start" ]
# ENTRYPOINT ["tail", "-f", "/dev/null"]
docker-compose.yml
version: '3'
services:
serverapp:
image: alpine:3.14
build:
dockerfile: Dockerfile
container_name: serverapp
restart: unless-stopped
ports:
- "80:80"
package.json
{
"name": "docker-compose-tester",
"version": "1.0.0",
"main": "server.js",
"scripts": {
"start": "cross-env NODE_ENV=debug nodemon --exec babel-node server.js"
},
"dependencies": {
"express": "^4.16.1",
"moment": "^2.29.4"
},
"devDependencies": {
"@babel/node": "^7.20.2",
"cross-env": "^7.0.3",
"nodemon": "^2.0.20"
}
}
server.js
const express = require('express');
const moment = require('moment');
const path = require('path');
const fs = require('fs');
const PORT = 80;
const app = express();
app.use('/messages/', express.static(path.join(__dirname, 'messages')));
app.get('/', (req, res) => {
const message = req.query.message;
if (!message) {
return res.send('<pre>Please use a query like: "/?message=Hello+World"</pre>');
}
const dirPathMessages = path.join(__dirname, 'messages');
const date = moment(new Date()).format('YYYYMMDD-HHmmss');
const fileNameMessage = `${date}.txt`;
const filePathMessage = path.join(dirPathMessages, fileNameMessage);
fs.mkdirSync(dirPathMessages, { recursive: true });
fs.writeFileSync(filePathMessage, message);
const filesList = fs.readdirSync(dirPathMessages);
const filesListStr = filesList.reduce((output, fileNameMessage) => {
const filePathMessage = path.join(dirPathMessages, fileNameMessage);
const message = fs.readFileSync(filePathMessage);
return output + `<div><a href="/messages/${fileNameMessage}">/messages/${fileNameMessage}</a> -> ${message}</div>` + "\n";
}, '');
res.send(`<pre>${filesListStr}\nCreated file: "${filePathMessage}" with content: "${message}".</pre>`);
});
app.listen(PORT, () => {
console.log(`TCP Server is running on port: ${PORT}`);
});
Upvotes: 0
Views: 247
Reputation: 10681
Your approach with named volume is correct. To fix the permission problem, change the owner of the messages
folder in the Dockerfile, before switching to the node
user.
FROM node:16-alpine
RUN mkdir -p /var/www/html && chown -R node:node /var/www/html
WORKDIR /var/www/html
COPY --chown=node:node . .
RUN mkdir -p messages && chown node:node messages
USER node
...
Upvotes: 2