Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Docker named volumes between multiple containers

I want to deploy some services into my server and all of them will use nginx as web server, every project has it own .conf file and I want to share all of then with nginx container. I tried to use named volumes but when it's used by more than one container the data gets replaced. I want to get all this .conf files from diferent containers and put in a volume so it can be read by nginx container. I also tried to use subdirectories in named volumes, but, use namedVolumeName/path do not work.

Obs: I'm using docker-compose in all projects

version: "3.7"

services:
  backend:
    container_name: jzmimoveis-backend
    image: paulomesquita/jzmimoveis-backend
    command: uwsgi --socket :8000 --wsgi-file jzmimoveis/wsgi.py
    volumes:
      - nginxConfFiles:/app/nginx
      - jzmimoveisFiles:/app/src
    networks:
      - jzmimoveis
    restart: unless-stopped
    expose:
      - 8000

  frontend:
    container_name: jzmimoveis-frontend
    image: paulomesquita/jzmimoveis-frontend
    command: serve -s build/
    volumes:
      - nginxConfFiles:/app/nginx
    networks:
      - jzmimoveis
    restart: unless-stopped
    expose:
      - 5000

volumes:
  nginxConfFiles:
    external: true
  jzmimoveisFiles:
    external: true
networks:
  jzmimoveis:
    external: true

For example, is this case i linked both frontend and backend nginx file to the named volume nginxConfFiles, but, when I do docker-compose up -d in this file, just one of the .conf file appears in volume, I think it gets overwritten by the other container in the same file.

like image 353
Paulo Mesquita Avatar asked Jun 27 '20 23:06

Paulo Mesquita


People also ask

How do I create a volume in Docker containers?

Create and Name a Volume. The docker volume create command will create a named volume. The name allows you to easily locate and assign Docker volumes to containers. To create a volume, use the command: sudo docker volume create --name [volume name] mixed.

What is the difference between named and anonymous volumes in Docker?

There are two types of volumes to consider: Named volumes have a specific source from outside the container, for example awesome:/bar. Anonymous volumes have no specific source so when the container is deleted, instruct the Docker Engine daemon to remove them. To automatically remove anonymous volumes, use the --rm option.

What is the difference between bind mounts and volumes in Docker?

While bind mounts are dependent on the directory structure of the host machine, volumes are completely managed by Docker. Volumes have several advantages over bind mounts: Volumes are easier to back up or migrate than bind mounts. You can manage volumes using Docker CLI commands or the Docker API.

What version of Docker-Compose has named volumes?

NOTE: This feature is available starting from version 3.4 file format. If the *services-volume is just a pointer to the value set above, this looks awesome... I'll have to try it. Named volumes, aka the top-level volumes field, seem to still be a thing in v3 of docker-compose.


2 Answers

Probably you could have, on the nginx container, the shared volume pointing to /etc/nginx/conf.d, and then use different names for each project conf file.

Below a proof-of-concept, three servers with a config file to be attached on each one, and a proxy (your Nginx) with the shared volume bound to /config:

version: '3'

services:
  server1:
    image: busybox:1.31.1
    volumes:
    - deleteme_after_demo:/config
    - ./server1.conf:/app/server1.conf
    command: sh -c "cp /app/server1.conf /config; tail -f /dev/null"

  server2:
    image: busybox:1.31.1
    volumes:
    - deleteme_after_demo:/config
    - ./server2.conf:/app/server2.conf
    command: sh -c "cp /app/server2.conf /config; tail -f /dev/null"

  server3:
    image: busybox:1.31.1
    volumes:
    - deleteme_after_demo:/config
    - ./server3.conf:/app/server3.conf
    command: sh -c "cp /app/server3.conf /config; tail -f /dev/null"

  proxy1:
    image: busybox:1.31.1
    volumes:
    - deleteme_after_demo:/config:ro
    command: tail -f /dev/null

volumes:
  deleteme_after_demo:

Let's create 3 config files to be included:

➜ echo "server 1" > server1.conf
➜ echo "server 2" > server2.conf
➜ echo "server 3" > server3.conf

then:

➜ docker-compose up -d                  
Creating network "deleteme_default" with the default driver
Creating deleteme_server2_1 ... done
Creating deleteme_server3_1 ... done
Creating deleteme_server1_1 ... done
Creating deleteme_proxy1_1  ... done

And finally, let's verify the config files are accessible from proxy container:

➜ docker-compose exec proxy1 sh -c "cat /config/server1.conf"
server 1

➜ docker-compose exec proxy1 sh -c "cat /config/server2.conf"
server 2

➜ docker-compose exec proxy1 sh -c "cat /config/server3.conf"
server 3

I hope it helps. Cheers!

Note: you should see mounting a volume exactly the same way as using Unix mount command. If you already have content inside the mount point, after mount you are not going to see it, but the content of the mounted device (unless it was empty and first created here). Whatever you want to see there needs to be already on the device or you need to move it afterward.

So, I did it by mounting the files because I had no data in the container I used. Then copying these with the starting command. You could address it a different way, eg copying the config file to the mounted volume by the use of an entry point script in your image.

like image 106
Ictus Avatar answered Nov 15 '22 09:11

Ictus


A named volume is initialized when it's empty/new and a container is started using that volume. The initialization is from the image filesystem, and after that, the named volume is persistent and will retain the state from the previous use.

In this case, what you have is a race condition. The volume is sharing the files, but it depends on which container compose starts up first to control which image is used to initialize the volume. The named volume is shared between multiple images, it's just the content that you want to be different.

For your use case, you may be better off putting some logic in the image build and entrypoint to save the files you want to mirror in the volume to a different location in the image on build, and then update the volume on container startup. By moving this out of the named volume initialization steps, you avoid the race condition, and allow the volume to be updated with future changes from the image. An example of this is in my base image with the save-volume you'd run in the Dockerfile, and load-volume you'd run in your entrypoint.

As a side note, it's also a good practice to mount that named volume as read-only in the containers that have no need to write to the config files.

like image 38
BMitch Avatar answered Nov 15 '22 10:11

BMitch