1

I want to dockerize my php application and deploy it in production. Here is my application structure :

api
|
+-- src
+-- config
+-- vendor
+-- tests
+-- public

I need to embed api/* in my container, and I'm confused about the best approach. Here is mine :

  1. A light multi-stage docker image (around 85 mb but can be more optimized)

    FROM php:7.4-fpm-alpine AS base
    
    WORKDIR /var/www/html
    ENV COMPOSER_ALLOW_SUPERUSER 1
    RUN apk update && \
      docker-php-ext-install mysqli tokenizer pdo_mysql && \
      curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
    
    FROM base AS dev
    
    COPY xdebug.ini /usr/local/etc/php/conf.d/xdebug-dev.ini
    RUN apk add --no-cache --virtual .phpize-deps $PHPIZE_DEPS && \
      pecl install xdebug-2.9.1 && \
      docker-php-ext-enable xdebug && \
      touch /tmp/xdebug.log && \
      chmod -R 666 /tmp/xdebug.log && \
      apk del -f .phpize-deps
    
  2. A mounted volume (docker-compose.yml)

    api:
      volumes:
        - ./api:/var/www/html
      command: composer install && php-fpm;
    

I saw a lot of docker images using COPY .. instruction in Dockerfile instead of volumes. I believe COPY is great to copy small config files (or a small build for compiled languages), but it's not optimized to keep the images small (basic rule with docker).

Am I missing something? For example, is it bad practice to handle volumes from outside the dev environment ?

1

Standard practice in non-development environments is to COPY the application code into the image, and to set the standard CMD there too.

...
COPY . .
RUN composer install
# php:fpm-alpine image already provides the correct CMD

There are a couple of important advantages to doing this:

  • You can just run the Docker image directly on the target system; you don't have to copy the Docker image, and also the application code separately.
  • RUN composer install happens only once during the build sequence. command: composer install ... runs on every restart, which is slower, uses network bandwidth, and can fail.
  • If you give a unique tag to each image, you can easily roll back to an earlier image by changing the tag back. It's hard to do this with a volume mount.
  • You'll docker run an identical image in all environments; you don't need to worry that the host-directory copy of the application is slightly different on that one system.

There's no specific rule that a Docker image needs to be very small; the only practical issue I've encountered is that multi-gigabyte images don't transfer across the network well. Indeed, I'd recommend the opposite choice around COPY vs. volumes that you've suggested: COPY the application code in, and use volumes for config files and other things that aren't "your code", and that need to be directly accessed from the host. (You do want to rebuild your application when the application changes; you don't want to rebuild your application when the database URL changes.)

2

To copy into the image or to mount volumes--my advice:

  • if the files will change infrequently, then copy them into the image, with the understanding that if they need to be modified, you will need to rebuild the image.
  • if you need to frequently change the files, or store some kind of state about the files (and re-use them with future container instances of the image), then mount them as volumes.
1
  • That's a good point ! My second thought is about immutability. You ship a packaged application in production. So no need to worry about the filesystems and base code deployment.
    – kero_zen
    Dec 10 '20 at 17:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.