RUN composer install and npm install in every build of a container

Viewed 3445

I'm trying to create a php-apache container with npm and composer installed and run composer install, npm install in every build, but I'm getting errors.

# Dockerfile

FROM php:7.4-apache

RUN apt-get -y update && apt-get upgrade -y

RUN apt-get update && apt-get install -y \
        libfreetype6-dev \
        libjpeg62-turbo-dev \
        libpng-dev \
        npm \
    && docker-php-ext-configure gd --with-freetype --with-jpeg \
    && docker-php-ext-install -j$(nproc) gd

# Composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer

# Enable apache modules
RUN a2enmod rewrite headers

EXPOSE 80
#RUN composer install
#RUN npm install

ENTRYPOINT ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]


# docker-compose.yml

version: "3"

services:
  painel-admin:
    build:
      context: ./bin/painel-admin
    container_name: 'painel-admin'
    command: >
      sh -c "php /usr/local/bin/composer install"
    restart: 'always'
    ports:
      - "81:80"
      - "82:443"
    volumes:
      - ${DOCUMENT_ROOT-..}:/var/www/html
      - ${PHP_INI-./config/php/php.ini}:/usr/local/etc/php/php.ini
      - ${VHOSTS_DIR-./config/vhosts}:/etc/apache2/sites-enabled
      - ${LOG_DIR-./logs/apache2}:/var/log/apache2

log error:

Action '-D FOREGROUND sh -c php /usr/local/bin/composer install' failed.

Same error if I try with npm install.

I can run the commands inside the docker, but I want to automate that

2 Answers

I think you are following you an incorrect approach. Those commands are part of the image building process, so they should be part of your Dockerfile.

And build process happens before volumes are available (the container is not running, so it's not possible to depend on those). What you need to do is copy the necessary files to the image you are building before you run composer install.

A more sensible approach, taking advantage of multi-stage build dockerfiles would be:

## First stage. Copy project files and run composer
FROM composer:2 as composer_stage

RUN rm -rf /var/www && mkdir -p /var/www/html
WORKDIR /var/www/html

COPY composer.json composer.lock symfony.lock .env ./
COPY public public/

RUN composer install --ignore-platform-reqs --prefer-dist --no-scripts --no-progress --no-suggest --no-interaction --no-dev --no-autoloader

RUN composer dump-autoload --optimize --apcu --no-dev

COPY bin bin/
COPY config config/
COPY src src/

RUN composer run-script $NODEV post-install-cmd; \
    chmod +x bin/console;

## Second stage. Build NPM dependencies

FROM node:12 as npm_builder

COPY --from=composer_stage /var/www/html /var/www/html

WORKDIR /var/www/html
COPY yarn.lock package.json webpack.config.js ./
COPY assets ./assets

RUN yarn install
RUN yarn encore prod

# I'm using yarn here, but using npm would be similar, depending on how your project is setup

# RUN npm install
# RUN npm run build # if necessary and the command exists in your project

## Third stage, mostly copied from your original Dockerfile

FROM php:7.4-apache

RUN apt-get -y update && apt-get upgrade -y

COPY --from=npm_builder /var/www/html /var/www/html

RUN apt-get update && apt-get install -y \
        libfreetype6-dev \
        libjpeg62-turbo-dev \
        libpng-dev \
        npm \
    && docker-php-ext-configure gd --with-freetype --with-jpeg \
    && docker-php-ext-install -j$(nproc) gd

# Enable apache modules
RUN a2enmod rewrite headers

EXPOSE 80

ENTRYPOINT ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]

This way, your final resulting image does not include any of the development dependencies. The image is thought mostly for production, so you can build it and deploy it.

To run it locally during development, you just run the same image locally with your desired volume mount-points. You would only need to rebuild the image whenever your dependencies change or are upgraded.

You'll need to adjust the paths so it matches your desired configuration (I'm building the project on /var/www/html and I point the webserver to /var/www/html/public, but you can easily change those).

Since you are using an image that includes both a webserver and the PHP runtime, that should be it.

You can try to put your composer install in your entrypoint.

I would normally use a file for this. You might want to try this.

entrypoint.sh

#!/bin/sh
set -e

php /usr/local/bin/composer install

exec "$@"

and in your dockerfile

# ...

COPY entrypoint.sh /usr/local/bin/entrypoint.sh
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
CMD ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]

Which would result in something like this.

/usr/local/bin/entrypoint.sh /usr/sbin/apache2ctl -D FOREGROUND

Since the last line of the entry point is exec "$@" it would take all parameters passed to entrypoint.sh and execute it.

Related