在每次容器构建中运行composer install和npm install。

5

我试图创建一个带有npm和composer安装的php-apache容器,并在每次构建中运行composer installnpm install,但是我遇到了错误。

# 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

日志错误:

执行 '-D FOREGROUND sh -c php /usr/local/bin/composer install' 失败。

如果我尝试使用 npm install 会出现相同的错误。

我可以在docker内运行这些命令,但我想要自动化。


2
最好在 Dockerfile 中像你展示的那样运行 RUN npm install。这意味着将应用程序代码复制到镜像中,而不是使用 volumes: 进行绑定挂载。将镜像中的 ENTRYPOINT 更改为 CMD 也会使 command: 覆盖工作,但它将更改容器以运行构建时命令而不是 HTTP 守护程序。 - David Maze
1
你尝试过什么来检查那个错误的原因了吗? - Nico Haase
2个回答

7
我认为你正在采用错误的方法。这些命令是镜像构建过程的一部分,因此它们应该是Dockerfile的一部分。
而构建过程发生在卷可用之前(容器还没有运行,因此无法依赖于它们)。您需要做的是在运行composer install之前将必要的文件复制到正在构建的映像中。
更明智的方法是利用多阶段构建 Dockerfile。
## 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"]

这样,你的最终结果图像不包括任何开发依赖项。该图像主要用于生产,因此您可以构建并部署它。
在开发过程中在本地运行它,只需使用所需的卷挂载点在本地运行相同的图像即可。只有当您的依赖关系发生更改或升级时,才需要重新构建图像。
您需要调整路径以使其与所需的配置匹配(我正在/var/www/html上构建项目,并将Web服务器指向/var/www/html/public,但您可以轻松更改这些内容)。
由于您正在使用同时包含Web服务器和PHP运行时的图像,因此应该就这样了。

感谢您的回答,@yivi。我正在使用Laravel。那么,我应该复制vendor和node_modules以避免在每个构建中重新安装所有软件包吗? - André Walker
不,你只需要复制我在上面的Dockerfile中显示的文件。composer.json和composer.lock,然后是package.json和package-lock.json。vendor和node_modules是在镜像内部创建的,而不是从你的计算机复制过来的。 - yivi
你说你把Web服务器指向了/var/www/html/public。那你是在哪里做的? - André Walker
不在那里。您的Web服务器配置也未包含在您的问题中。无论您在哪里配置Web服务器(例如,Apache),您都应该指向该目录。 - yivi

-1

你可以尝试将composer install放在入口点。

通常我会使用一个文件来处理这个问题。你可以尝试一下。

entrypoint.sh

#!/bin/sh
set -e

php /usr/local/bin/composer install

exec "$@"

还有,在你的 Dockerfile 中

# ...

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

这将导致类似于这样的结果。

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

由于入口点的最后一行是exec "$@",它会接收传递给entrypoint.sh的所有参数并执行它们。


谢谢你的回答,Julien。但是我觉得我需要在docker-compose.yml中运行这个命令,因为我需要其中指定的卷。 - André Walker
也许你应该试一试。当使用入口点时,卷应该存在。 - Julien B.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接