skip to Main Content

I’m learning Docker and I’m facing the following issue, mainly related to sharing files between services. I’ve prepared an illustration:

Illustration

I have 3 stages in my Dockerfile: builder, php and caddy. My docker-compose.yml defines 2 services: php and caddy (the target is the correspoding stage in the Dockerfile). Requirements:

  1. builder stage produces some files needed by php service at runtime (i.e. public/build/metatada.json)
  2. builder stage produces some files needed by caddy service at runtime (i.e. public/js/foo.js)
  3. php stage produces some files that should be available to caddy service at runtime (i.e. public/bundles/foo.js)
  4. caddy service must share public/ folder with php service, because new files can be created (i.e. user public/uploads/newfile.pdf)
  5. New static files in source code must be taken into account (i.e. public/img/newimage.png)

Solution 1: my first naive solution won’t work because point 4: caddy service can’t access resources created by the php services at runtime (i.e. public/uploads/new.png):

FROM node:current-alpine as builder
WORKDIR /app
COPY . .
RUN npm run build

FROM php:fpm-latest as php
COPY . .
COPY --from=builder /app/public/ public/

FROM caddy:latest as caddy
WORKDIR /srv
COPY --from=builder /app/public/ public/

Solution 2: bind mount source code public folder to caddy /srv/public and php /var/www/html/public folder. This solves 4 and 5, but completely wipes the builder output so points 1, 2, 3 are not respected.

Which other option I have? I already tried to share a volume (see my other SO question) but it won’t work either.

2

Answers


  1. Forgive me if I’m also being naive with this approach, I have read your other question as well. Just a suggestion to try.

    Could you not keep both public folder’s contents elsewhere in the container, use solution 2 and load the container’s public folder contents after boot using the entrypoint script in each container, if a certain file can’t be found? That way nothing is obscured by the bind and you still have all the files.

    Something like:

    entrypoint.sh

    ...
    while [ ! -e "/app/public/bundles/foo.js" ];do
        cp -r /apptmp/public/* /app/public/
        break;
    done
    ...
    

    Where /apptmp/public contains the files generated during the build process and /app/public is the mounted directory.

    (I used while but if is fine too).

    EDIT: If the files are needed at runtime you can script a wait into the entrypoints as well.

    Login or Signup to reply.
  2. I am not sure if it will work but you could try the following.

    The main idea is try using some kind of initialization container in your docker compose setup.

    I mean, in addition to the php and caddy containers, you will use a third one.

    This third container will be responsible for initializing the different resources required by the rest of containers in your setup, php and caddy.

    The following docker-compose file definition try illustrating the concept:

    services:
    
      init-assets:
         build: .
         volumes:
           - shared-volume:/public
    
      php:
        image: php:fpm-latest
        # ... other configuration
        volumes:
          - shared-volume:/app/public
        depends_on:
          init-assets:
            condition: service_completed_successfully 
    
      caddy:
        image: caddy:latest
        # ... other configuration
        volumes:
          - shared-volume:/srv
        depends_on:
          php:
            condition: service_started
    
    volumes:
      shared-volume:
    

    I am not used to work with php and I based this compose file in this related SO question: please, forgive me for any inaccuracy.

    It is very important to note that in order to make the final application work properly the same volume is used. Please, review the different mounts points in the different containers and make it point to the right locations.

    The build for the init-assets container can be performed with a Dockerfile very similar to the one you proposed in your first solution: just be sure to change the final caddy container entry point to something that exists the container.

    This approach is typically used for performing the initial setup of databases as in the case of airflow and as described here in SO too, but I think it could be of help in your use case as well.

    As I said, they are just ideas but I hope that with minor tweaks they will make your application work properly.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search