Docker - Execute command after mounting a volume

2019-03-20 06:13发布

I have the following Dockerfile for a php runtime based on the official [php][1] image.

FROM php:fpm
WORKDIR /var/www/root/
RUN apt-get update && apt-get install -y \
        libfreetype6-dev \
        libjpeg62-turbo-dev \
        libmcrypt-dev \
        libpng12-dev \
        zip \
        unzip \
    && docker-php-ext-install -j$(nproc) iconv mcrypt \
    && docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \
    && docker-php-ext-install -j$(nproc) gd \
    && docker-php-ext-install mysqli \
    && docker-php-ext-enable opcache \
    && php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" \
    && php -r "if (hash_file('SHA384', 'composer-setup.php') === '669656bab3166a7aff8a7506b8cb2d1c292f042046c5a994c43155c0be6190fa0355160742ab2e1c88d40d5be660b410') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" \
    && php composer-setup.php \
    && php -r "unlink('composer-setup.php');" \
    && mv composer.phar /usr/local/bin/composer

I am having trouble running composer install.

I am guessing that the Dockerfile runs before a volume is mounted because I receive a composer.json file not found error if adding:

...
&& mv composer.phar /usr/local/bin/composer \
&& composer install

to the above.

But, adding the following property to docker-compose.yml:

command: sh -c "composer install && composer require drush/drush"

seems to terminate the container after the command finishes executing.

Is there a way to:

  • wait for a volume to become mounted
  • run composer install using the mounted composer.json file
  • have the container keep running afters

?

3条回答
冷血范
2楼-- · 2019-03-20 06:58

I generally agree with Chris's answer for local development. I am going to offer something that combines with a recent Docker feature that may set a path for doing both local development and eventual production deployment with the same image.

Let's first start with the image that we can build in a manner that can be used for either local development or deployment somewhere that contains the code and dependencies. In the latest Docker version (17.05) there is a new multi-stage build feature that we can take advantage of. In this case we can first install all your Composer dependencies to a folder in the build context and then later copy them to the final image without needing to add Composer to the final image. This might look like:

FROM composer as composer
COPY . /app
RUN composer install --ignore-platform-reqs --no-scripts

FROM php:fpm
WORKDIR /var/www/root/
RUN apt-get update && apt-get install -y \
        libfreetype6-dev \
        libjpeg62-turbo-dev \
        libmcrypt-dev \
        libpng12-dev \
        zip \
        unzip \
    && docker-php-ext-install -j$(nproc) iconv mcrypt \
    && docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \
    && docker-php-ext-install -j$(nproc) gd \
    && docker-php-ext-install mysqli \
    && docker-php-ext-enable opcache
COPY . /var/www/root
COPY --from=composer /app/vendor /var/www/root/vendor

This removes all of Composer from the application image itself and instead uses the first stage to install the dependencies in another context and copy them over to the final image.

Now, during development you have some options. Based on your docker-compose.yml command it sounds like you are mounting the application into the container as .:/var/www/root. You could add a composer service to your docker-compose.yml similar to my example at https://gist.github.com/andyshinn/e2c428f2cd234b718239. Here, you just do docker-compose run --rm composer install when you need to update dependencies locally (this keeps the dependencies build inside the container which could matter for native compiled extensions, especially if you are deploying as containers and developing on Windows or Mac).

The other option is to just do something similar to what Chris has already suggested, and use the official Composer image to update and manage dependencies when needed. I've done something like this locally before where I had private dependencies on GitHub which required SSH authentication:

docker run --rm --interactive --tty --volume $PWD:/app:rw,cached --volume $SSH_AUTH_SOCK:/ssh-auth.sock --env SSH_AUTH_SOCK=/ssh-auth.sock --volume $COMPOSER_HOME:/composer composer:1.4 install --ignore-platform-reqs --no-scripts

To recap, the reasoning for this method of building the image and installing Composer dependencies using an external container / service:

  • Platform specific dependencies will be built correctly for the container (Linux architecture vs Windows or Mac).
  • No Composer or PHP is required on your local computer (it is all contained inside Docker and Docker Compose).
  • The initial image you built is runnable and deployable without needing to mount code into it. In development, you are just overriding the /var/www/root folder with a local volume.
查看更多
欢心
3楼-- · 2019-03-20 07:01

I've been down this rabbit hole for 5 hours, all of the solutions out there are way too complicated. The easiest solution is to exclude vendor or node_modules and similar directories from volume.

#docker-compose.yml
volumes:
      - .:/srv/app/
      - /srv/app/vendor/

So this will map current project directory but exclude its vendor subdirectory. Dont forget the trailing slash!

So now you can easily run composer install in dockerfile and when docker mounts your volume it will ignore vendor directory.

查看更多
劳资没心,怎么记你
4楼-- · 2019-03-20 07:19

If this is is for a general development environment, then the intention is not really ideal because it's coupling the application to the Docker configuration.

Just run composer install seperately by some other means (there is an image available for this on dockerhub, which allows you to just do (docker run -it --rm -v $(pwd):/app composer/composer install).


But yes it is possible you would need the last line in the Dockerfile to be bash -c "composer install && php-fpm".


  • wait for a volume to become mounted

No, volumes are not able to be mounted during a docker build process. Though you can copy the source code in.

  • run composer install using the mounted composer.json file

No, see above response.

  • have the container keep running after

Yes, you would need to execute php-fpm --nodaemonize ( which is a long running process, hence it won't terminate.

查看更多
登录 后发表回答