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}`);
});
CodePudding user response:
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
...