Home > OS >  Update source code of server app inside docker container without touching the existing saved data
Update source code of server app inside docker container without touching the existing saved data

Time:11-20

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:

enter image description here

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

...
  • Related