Ulrich Eckhardt
Ulrich Eckhardt

Reputation: 17434

Specify origin of data for a shared volume

I have a task that I already solved, but where I'm not satisfied with the solution. Basically, I have a webserver container (Nginx) and a fast-CGI container (PHP-FPM). The webserver container is built on an off-the-shelf image, the FCGI container is based on a custom image and contains the application files. Now, since not everything is sourcecode and processed on the FCGI container, I need to make the application files available inside the webserver container as well.

Here's the docker-compose.yml that does the job:

version: '3.3'

services:
  nginx:
    image: nginx:1-alpine
    volumes:
      - # customize just the Nginx configuration file
        type: bind
        source: ./nginx.conf
        target: /etc/nginx/nginx.conf
      - # mount application files from PHP-FPM container
        type: volume
        source: www-data
        target: /var/www/my-service
        read_only: true
        volume:
          nocopy: true
    ports:
      - "80:80"
    depends_on:
      - php-fpm

  php-fpm:
    image: my-service:latest
    command: ["/usr/sbin/php-fpm7.3", "--nodaemonize", "--force-stderr"]
    volumes:
      - # create volume from application files
        # This one populates the content of the volume.
        type: volume
        source: www-data
        target: /var/www/my-service

volumes:
  # volume with application files shared between nginx and php-fpm
  www-data:

What I don't like here is mostly reflected by the comments concerning the volumes. Who creates and stores data should be obvious from the code and not from comments. Also, what I really dislike is that docker actually creates a place where it stores data for this volume. Not only does this use up disk space and increase startup time, it also requires me to never forget to use docker-compose down --volumes in order to refresh the content on next start. Imagine my anger when I found out that down didn't tear down what up created and that I was hunting ghosts from previous runs.

My questions concerning this:

Upvotes: 4

Views: 531

Answers (3)

jmaitrehenry
jmaitrehenry

Reputation: 2420

It depends of the usecase of your setup. If it's only for local dev or if you want the same thing on production. On dev, having a volume populated manually or by one container for others could be OK.

But if you want something that will run the same way in production, you may need something else. For exemple, in production, I don't want to have my code in a volume but in my image in an immutable way and I just need to redeploy it.

For me a volume is not for storing application code but for storing data like cache, user uploaded content, etc. Something we want to keep between deployments.

So, if we want to have 2 images with the same files not in a volume, I will build 2 images with the application code and static content, one for php, one for nginx.

But the deployment is usually not synchrone for the 2 images. We solve this issue by deploying the PHP application first and the nginx application after. On nginx, we add a config that try to serve static content from it first and if the file doesn't exist, to ask it to PHP.

For the dev environment, we will reuse the same image but use a volume to mount the current code inside the container (an host bind mount).

But in some case, the bind mount could have some issues: - On Mac the file sharing is slow but it should be better with the latest version of Docker Desktop in the Edge channel (2.3.1.0) - On Windows, the file sharing is slow too - On Linux, we need to be careful about the file permission and the user used inside the container

If you try to solve one/many of this issues with the volume solution, we could find some solution for that. Ex, on Mac, I will try to Edge release of docker first, on Windows, if possible, I will use WSL2 and Docker set to use the WSL2 backend.

Upvotes: 0

Ryabchenko Alexander
Ryabchenko Alexander

Reputation: 12420

You also can make custom nginx image with copy of static from your php image

here is Dockerfile for nginx

FROM my-service:latest AS src-files

FROM nginx
COPY --from=src-files /path-to-static-in-my-service-image /path-to-static-in-nginx

This will allow you to use no volumes with source code

Also can use TAG from env variables in Dockerfile

FROM my-service:${TAG} AS src-files
...

Upvotes: 0

Pierre B.
Pierre B.

Reputation: 12943

You can use the local driver with option type=tmpfs, for example:

volumes:
  www-data:
    driver: local
    driver_opts:
      type: tmpfs
      device: tmpfs

Which will follow your requirements:

  • Data will be shared between containers at runtime
  • Data should not be persisted, i.e. volumes will be emptied when container are stopped, restarted or destroyed

This is CLI equivalent of

docker volume create --driver local --opt type=tmpfs --opt device=tmpfs www-data

Important note: this is NOT a Docker tmpfs mount, but a Docker volume using a tmpfs option. As stated in the local volume driver documentation it uses the Linux mount options, in our case --types to specify a tmpfs filesystem. Contrary to a simple tmpfs mount, it will allow you to share the volume between container while retaining classic behavior of a temporary filesystem

I can't find documentation for available volume drivers or even explore which volume drivers exist

Can I express in code that one container contains data that should be made available to other containers more clearly? The above code works, but it fails utterly to express the intent.

I don't think so, your code is are already quite clear as to the intent of this volume:

  • That's what volume are for: sharing data between containers. As stated in the doc Volumes can be more safely shared among multiple container, furthermore only containers are supposed to write into volumes.
  • nocopy and read_only clearly express that nginx relies on data written by another container as it will only be able to read from this volume
  • Given your volume is not external, it is safe to assume only another container from the same stack can use it
  • A bit of logic and experience with Docker allow to quickly come to the previous point, but even for less experienced Docker users your comments gives clear indications, and your comments are part of the code ;)

Upvotes: 3

Related Questions