Search code examples
bashdockerdocker-composedockerfile

How do you interact with a shared Volume created by Docker Compose in a Dockerfile?


I have a Volume shared amongst several containers in my Docker Compose setup. It looks like this in compose.yml:

php:
  volumes:
    - type: bind
      source: ./apps
      target: /var/www

I have a bash script which interacts with files in the shared Volume. It's called setup.sh. It's included in php's Dockerfile.

What I would like to do is have it run as a RUN command in the Dockerfile:

COPY dockerfiles/php/setup.sh /usr/local/bin/setup.sh
RUN chmod +x /usr/local/bin/setup.sh
RUN  /usr/local/bin/setup.sh

However, this doesn't work, because the Volume is not mounted until the very last command—the entrypoint.

This does work:

CMD sh -c /usr/local/bin/setup.sh && /usr/sbin/php-fpm && tail -f /dev/null

However, the issue with this is that when I spin up the containers with docker compose, it shows as Started, but the script takes a minute or so to run before PHP-FPM is up. There is no easy way to tell when the script is done. So I'd prefer to run the script before the entrypoint so it's just waiting on PHP-FPM to start up (which does not take long).

Is there any way to do that?


Solution

  • How do you interact with a shared Volume created by Docker Compose in a Dockerfile?

    You can't. It doesn't exist yet. It's possible the image that's being built will be run multiple times, on different machines, with different mounted volumes.

    The setup you describe sounds like you're trying to inject the entire application source code through a bind mount. This is often used to try to make Docker emulate a local development environment. A typical Docker setup is to have immutable, self-contained images, however: all of the application code is contained in the image (and compiled if required), and you can run the image without the mount you show.

    If you COPY the application code into the image, then it's present, and you can RUN your build command.

    COPY ./apps /var/www/
    COPY dockerfiles/php/setup.sh /usr/local/bin/setup.sh
    RUN setup.sh
    CMD ["php-fpm", "-F"]
    

    If you're in a different situation – maybe these files are some sort of installation-specific data – then you must run this at container startup time; you can't run it earlier. In that case, I prefer an entrypoint wrapper script as a little bit cleaner than the extended CMD you show. That script can look like

    #!/bin/sh
    
    # run the setup script
    setup.sh || exit 1
    
    # switch to the main container command
    exec "$@"
    

    Check the script into source control as executable, COPY it into your image, and make that script be the image's ENTRYPOINT. You must use JSON-array syntax for ENTRYPOINT. The last line of the script runs the CMD, which may be any syntax.

    COPY entrypoint.sh /usr/local/bin/
    ENTRYPOINT ["entrypoint.sh"]
    CMD ["php-fpm", "-F"]
    

    In both cases I'm using the php-fpm -F option to run the interpreter in the foreground. Avoid using tail(1) to "keep the container alive".

    With the ENTRYPOINT approach it's possible to override the CMD to do something else. For example, if you

    docker run --rm your-image \
      ls -l /var/www/extra_content
    

    or similar, you can run a temporary container (--rm), running ls as the main container process, and verify what got created by the setup script.