Why can't composer find a composer.json file inside my Docker container?

acobster picture acobster · Feb 10, 2017 · Viewed 10.9k times · Source

I have a pretty straightforward Dockerfile based on Thelia's and Composer's. I want it to do as much setup as possible, so to that end, I am installing composer in the PATH within the container and then trying to run composer install. However, at that point, it seems my local/mounted files don't exist within the container yet (see output from RUN echo pwd: ... below).

The build fails with this error message:

Composer could not find a composer.json file in /var/www/html

To initialize a project, please create a composer.json file as described in the https://getcomposer.org/ "Getting Started" section

ERROR: Service 'web' failed to build: The command '/bin/sh -c composer install' returned a non-zero code: 1

Note that building without the RUN composer install instruction and then running docker-compose exec web composer install works.

Does the mount declared in docker-compose.yml not take effect until the image has been completely built? Do I need to explicitly COPY my local files for them to be visible during the build process?

Dockerfile:

FROM php:5.6-apache
COPY docker-php-pecl-install /usr/local/bin/

RUN apt-get update && apt-get install -y \
 libfreetype6-dev \
 libjpeg62-turbo-dev \
 libmcrypt-dev \
 libpng12-dev \
 libicu-dev \
 git \
 zip \
 libzip-dev \
    && docker-php-ext-install intl pdo_mysql mcrypt mbstring zip calendar \
    && docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \
    && docker-php-ext-install gd \
    && docker-php-pecl-install xdebug-2.3.3

RUN a2enmod rewrite
RUN usermod -u 1000 www-data
COPY config/php.ini /usr/local/etc/php/
COPY config/vhost/vhost.conf /etc/apache2/sites-enabled/

# Expose webroot
VOLUME /var/www/html
WORKDIR /var/www/html
COPY . /var/www/html # Added at Edit 1

# Allow Composer to be run as root
ENV COMPOSER_ALLOW_SUPERUSER 1

# Setup the Composer installer
RUN curl -o /tmp/composer-setup.php https://getcomposer.org/installer \
  && curl -o /tmp/composer-setup.sig https://composer.github.io/installer.sig \
  && php -r "if (hash('SHA384', file_get_contents('/tmp/composer-setup.php')) !== trim(file_get_contents('/tmp/composer-setup.sig'))) { unlink('/tmp/composer-setup.php'); echo 'Invalid installer' . PHP_EOL; exit(1); }" \
  && php /tmp/composer-setup.php \
  && chmod a+x composer.phar \
  && mv composer.phar /usr/local/bin/composer

# Install composer dependencies
RUN echo pwd: `pwd` && echo ls: `ls`  # outputs:
                                      # pwd: /var/www/html
                                      # ls:
RUN composer install

docker-compose.yml:

web:
  build: ./docker/php
  ports:
    - "8002:80"
  links:
    - mariaDB
  environment:
    SYMFONY_ENV: dev
  command: /usr/sbin/apache2ctl -D FOREGROUND
  volumes:
    - .:/var/www/html

mariaDB:
   ...

Edit 1 - after COPYing files explicitly

I added a COPY . /var/www/html after the VOLUME and WORKDIR instructions, and noticed something weird. Now the RUN pwd: ... instruction echoes:

pwd: /var/www/html

ls: Dockerfile config docker-php-pecl-install

This is the contents of my docker/php directory, not the project root!

Full(ish) directory structure:

.
├── LICENSE.txt
├── Readme.md
├── Thelia
├── bin
│   └── ...
├── bootstrap.php
├── cache
│   └── ...
├── change-version.sh
├── composer.json
├── composer.lock
├── docker
│   └── php
│       ├── Dockerfile
│       ├── config
│       └── docker-php-pecl-install
├── docker-compose.yml
├── lib
│   └── Thelia
│       └── Project
├── local
│   └── ...
├── log
├── templates
│   └── ...
├── vendor
│   └── ...
└── web
    ├── favicon.ico
    ├── index.php
    ├── index_dev.php
    └── robots.txt

So my revised question is: how do I tell Docker to COPY from the context in which I run docker-compose? I thought this was the default behavior for docker-compose, and that's how it's worked for me on past projects...

Answer

k0pernikus picture k0pernikus · Feb 10, 2017

You define a docker-compose 1.7 file with:

web:
  build: ./docker/php

which results basically in:

docker build -f ./docker/php/Dockerfile ./docker/php/

So basically the build path is the context of the build process.

If you really want to define the context, you have to upgrade your docker-compose defintion to at least v2 or v3.

There you should be able to do:

version: '2'
services:
  web:
    build:
      context: ./
      dockerfile: ./docker/php/Dockerfile

I would advise against that as:

  • whenever one edits that Dockerfile, you always have to remember the parent's folder context structure, which will be quite hard to maintain
  • as the context grows ever larger, you will slow the build process by sending a huge amount of data into the container build's context. A .dockerignore file would help, but you would have to use it as a whitelist instead of a blacklist.

If you want to build multiple php containers that all install dependencies with your docker-php-pecl-install file, I advise you to not inject a god context, but that your rather create a base php image from its own Dockerfile, which you then reference in the in the other's containers Dockerfile as the FROM source. That base image can be an unrelated project, all you need it do to is built an image, and reused in different other php projects.

If you want to reuse that image in your entire company, it's helpful to setup a private docker registry, where you and your team can push and pull images from.