13

I have some containers for a web app for now (nginx, gunicorn, postgres and node to build static files from source and a React server side rendering). In a Dockerfile for the node container I have two steps: build and run (Dockerfile.node). It ends up with two directories inside a container: bundle_client - is a static for an nginx and bundle_server - it used in the node container itself to start an express server.

Then I need to share a built static folder (bundle_client) with the nginx container. To do so according to docker-compose reference in my docker-compose.yml I have the following services (See full docker-compose.yml):

node:
  volumes:
    - vnode:/usr/src

nginx:
  volumes:
    - vnode:/var/www/tg/static
  depends_on:
    - node

and volumes:

volumes:
  vnode:

Running docker-compose build completes with no errors. Running docker-compose up runs everyting ok and I can open localhost:80 and there is nginx, gunicorn and node express SSR all working great and I can see a web page but all static files return 404 not found error.

If I check volumes with docker volume ls I can see two newly created volumes named tg_vnode (that we consider here) and tg_vdata (see full docker-compose.yml)

If I go into an nginx container with docker run -ti -v /tmp:/tmp tg_node /bin/bash I can't see my www/tg/static folder which should map my static files from the node volume. Also I tried to create an empty /var/www/tg/static folder with nginx container Dockerfile.nginx but it stays empty.

If I map a bundle_client folder from the host machine in the docker-compose.yml in a nginx.volumes section as - ./client/bundle_client:/var/www/tg/static it works ok and I can see all the static files served with nginx in the browser.

What I'm doing wrong and how to make my container to share built static content with the nginx container?

PS: I read all the docs, all the github issues and stackoverflow Q&As and as I understood it has to work and there is no info what to do when is does not.

UPD: Result of docker volume inspect vnode:

[
{
    "CreatedAt": "2018-05-18T12:24:38Z",
    "Driver": "local",
    "Labels": {
        "com.docker.compose.project": "tg",
        "com.docker.compose.version": "1.21.1",
        "com.docker.compose.volume": "vnode"
    },
    "Mountpoint": "/var/lib/docker/volumes/tg_vnode/_data",
    "Name": "tg_vnode",
    "Options": null,
    "Scope": "local"
}
]

Files: Dockerfile.node, docker-compose.yml

Nginx dockerfile: Dockerfile.nginx


UPD: I have created a simplified repo to reproduce a question: repo (there are some warnings on npm install nevermind it installs and builds ok). Eventually when we open localhost:80 we see an empty page and 404 messages for static files (vendor.js and app.js) in Chrome dev tools but there should be a message React app: static loaded generated by react script.

Max
  • 404
  • 2
  • 17
  • 39
  • 2
    Well the problem with using same volume container across the multiple services, is that the container which was loaded first will copy its own data to the volume and rest will all see the same. If you want to avoid that you need to use `nocopy` [option](https://docs.docker.com/compose/compose-file/#long-syntax-3). I see in your compose you have used it at multiple places, only one place will put its content in the volume and win with initial content. A build will put the files are run-time if it is happening after mount. Its hard for me to visualise your compose, but see if these pointers help – Tarun Lalwani May 21 '18 at 10:29
  • Yeah, "...that the container which was loaded first will copy its own data to the volume and rest will all see the same" and that is what I need - to initially copy built in `bundle_client` artefact in a volume and then use it in nginx container. And I make sure that node container is first in order by setting `depends_on.node` for `nginx` container. Maybe I messed up with Dokerfile and compose file and misunderstand how it actually works. Can you please explain - "A build will put the files are run-time if it is happening after mount.". I'm not sure if I understand it. – Max May 21 '18 at 10:58
  • I added `nocopy: true` to `nginx.volumes` according to the long syntax, and comment using of `vnode` in the gunicorn container but unfortunately folder is still empty inside the nginx container. – Max May 21 '18 at 11:07
  • Possible to get a repo to debug? – Tarun Lalwani May 21 '18 at 11:28
  • Could you add your Dockerfile.nginx file, please? I'm trying to reproduce your error. I have some clues but I need to check nginx container – Alejandro Galera May 21 '18 at 11:38
  • @AlexGalera Sure, I have updated a question. – Max May 21 '18 at 11:53
  • @TarunLalwani Unfortunately it has no sense and will be failed on build since it contains dependencies from the private local storage. But I can try to simplify this to the very simple gunicorn/node/nginx repo just to reproduce shared volumes with the same configuration. – Max May 21 '18 at 11:56
  • Yah because its hard to comment on such a long thing – Tarun Lalwani May 21 '18 at 12:15
  • Could you add `docker-volume` tag to the question, please? Thanks in advance. – Alejandro Galera May 21 '18 at 13:09
  • @TarunLalwani I've updated a question with a simplified repo. But since it has parts from the original one it can have some extra stuff but I hope it is only in npm/webpack configuration so it does not matter. – Max May 21 '18 at 13:24
  • You just want to share `bundle_client` with nginx at `var/www/test/static` ..right? – Tarun Lalwani May 21 '18 at 14:00
  • Absolutely, as simple as that. I just want to run `docker-compose up` and then open a `localhost` and see all newly bundled static served. – Max May 21 '18 at 14:04
  • I need it to make a deployment and automatically take new commits (now with `docker-compose` and maybe with `docker-stack` in a future), build a new static files version (and update other parts) and redeploy a new release. In a dev mode I'm using host volume `./client:/var/www...` and it works ok but on a new release (deploy) I should say to docker to automatically build files and share it with an nginx. – Max May 21 '18 at 14:10
  • I can see the issue now, but for some reason the node is failing in your repo, checking that now – Tarun Lalwani May 21 '18 at 14:13
  • Why is there no `CMD` in `Dockerfile.node`? Also no start script defined in `package.json`? – Tarun Lalwani May 21 '18 at 14:16
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/171474/discussion-between-tarun-lalwani-and-max). – Tarun Lalwani May 21 '18 at 14:17

2 Answers2

12

Two changes you need. In your node service add the volume like:

volumes:
  - vnode:/usr/src/bundle_client

Since you want to share /usr/src/bundle_client you should NOT be using /usr/src/ because that will share the full folder and the structure too.

And then in your nginx service add the volume like:

volumes:
  - type: volume
    source: vnode
    target: /var/www/test/static
    volume:
      nocopy: true

The nocopy: true keeps our intention clear that on initial map of the container the content of the mapped folder should not be copied. And by default the first container to get mapped to the volume will get the contents of the mapped folder. In your case you want this to be the node container.

Also before testing make sure you run below command to kill the cached volumes:

docker-compose down -v

You can see during my test the container had the files:

Nginx has files

Tomasz Dzieniak
  • 2,765
  • 3
  • 24
  • 42
Tarun Lalwani
  • 142,312
  • 9
  • 204
  • 265
3

Explanation of what happens step by step

Dockerfile.node

    ...
    COPY ./client /usr/src
    ...

docker-compose.yml

services:
  ...
  node:
    ...
    volumes:
      - ./server/nginx.conf:/etc/nginx/nginx.conf:ro
      - vnode:/usr/src
  ...
volumes:
  vnode:
  1. docker-compose up creates with this Dockerfile.node and docker-compose section a named volume with data saved in /usr/src.

Dockerfile.nginx

FROM nginx:latest

COPY ./server/nginx.conf /etc/nginx/nginx.conf

RUN mkdir -p /var/www/tg/static

EXPOSE 80
EXPOSE 443

CMD ["nginx", "-g", "daemon off;"]
  1. That produces that nginx containers created with docker-compose will have an empty /var/www/tg/static/

docker-compose.yml

 ...
 nginx:
    build:
      context: .
      dockerfile: ./Dockerfile.nginx
    container_name: tg_nginx
    restart: always
    volumes:
      - ./server/nginx.conf:/etc/nginx/nginx.conf:ro
      - vnode:/var/www/tg/static
    ports:
      - "80:80"
      - "443:443"
    depends_on:
      - node
      - gunicorn
    networks:
      - nw_web_tg

 volumes:
   vdata:
   vnode:
  1. docker-compose up will produce that vnode named volume is created and filled with data from /var/www/tg/static (empty by now) to existing vnode.

So, at this point, - nginx container has /var/www/tg/static empty because it was created empty (see mkdir in Dockerfile.nginx) - node container has /usr/src dir with client file (see that was copied in Dockerfile.node) - vnode has content of /usr/src from node and /var/www/tg/static from nginx.

Definitively, to pass data from /usr/src from your node container to /var/www/tg/static in nginx container you need to do something that is not very pretty because Docker hasn't developed another way yet: You need to combine named volume in source folder with bind volume in destination:

 nginx:
     build:
       context: .
       dockerfile: ./Dockerfile.nginx
     container_name: tg_nginx
     restart: always
     volumes:
       - ./server/nginx.conf:/etc/nginx/nginx.conf:ro
       - /var/lib/docker/volumes/vnode/_data:/var/www/tg/static
     ports:
       - "80:80"
       - "443:443"
     depends_on:
       - node
       - gunicorn
     networks:
       - nw_web_tg

Just change in docker-compose - vnode:/var/www/tg/static by - /var/lib/docker/volumes/vnode/_data:/var/www/tg/static

Alejandro Galera
  • 3,445
  • 3
  • 24
  • 42
  • I thought the same way and initially there was no `RUN mkdir -p /var/www/tg/static` it was added later just to try. If comment it out I end up with `/var` folder that does not contains `www` in it. – Max May 21 '18 at 12:04
  • Explicitly remove it with `RUN rm -rf /var/www/tg/static` has no effect. – Max May 21 '18 at 12:07
  • Ok, I misunderstood. See explanation and we'll go on. I don't understand very well what you're trying to do. Maybe you understood something wrong about named volumes, that I hope to have clarify it a little bit. – Alejandro Galera May 21 '18 at 12:43
  • I'll try to explain shortly here: – Max May 21 '18 at 12:46
  • I have a node container that on `docker-compose up` should run `npm install && npm build` and create an artefact folder with compiled static and put into somewhere in order to pass it to the nginx container (as I understand named volume is the only way to do that). Next, as I understood when I first mention a volume (node section here) it will fill it with given data and then (in nginx - the second mention) it will use this data. And I make sure that node is first and nginx is second by `depends_on.node`. What I missed here? – Max May 21 '18 at 12:55
  • You're right about named volumes. Where does `npm install` create that info? In `/usr/src` in `node` container? I think what you miss is that you shouldn't create another named volume in `nginx` container, because `nginx` is destination of data from node, not a source of data (because is empty). So, you need to create a bind from named volume to nginx directory – Alejandro Galera May 21 '18 at 12:58
  • Exactly, it creates a folder with static files `/usr/src/bundle_client` and it is there if I check a node container. – Max May 21 '18 at 13:00
  • Unfortulately changing volume name to a path does not work either =( I've added a simplified repo in the question body, maybe it can help. – Max May 21 '18 at 13:28