【问题标题】:Docker - Execute command after mounting a volumeDocker - 挂载卷后执行命令
【发布时间】:2017-07-01 19:53:16
【问题描述】:

我有以下基于官方[php][1] 图像的 php 运行时 Dockerfile。

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

我在运行 composer install 时遇到问题。

我猜测 Dockerfile 在安装卷之前运行,因为如果添加,我会收到 composer.json 找不到文件错误:

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

以上。

但是,将以下属性添加到docker-compose.yml:

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

似乎在命令执行完成后终止容器。

有没有办法:

  • 等待安装卷
  • 使用挂载的composer.json 文件运行composer install
  • 让容器在之后继续运行

?

【问题讨论】:

  • Dude 删除该哈希检查,因为它经常更改,随着 Composer 的每个新版本。顺便说一句,您希望拥有在容器启动时执行安装的自定义入口点。此外,您应该将应用程序数据移动到仅数据容器中,以便您可以拆分职责。然后,您的应用程序可以独立于 Web 服务器的基础设施进行部署。祝你好运
  • @mike 您能否提供一个使用仅数据容器的示例以及如何将其与 docker-compose 一起使用?或者,提供您认为可以很好地描述此概念的资源?或两者? :-) 干杯
  • 网上有很多例子,随便找data only container或者docker persistence strategy。您所做的只是构建您的应用程序(composer install / docs / assets 等),然后获取尽可能小的图像(可用的 144 字节图像!)并将所有内容复制到它。使用 docker-compose 中的 volumes_from 指令将该数据挂载到 php 映像中的 apache 根目录。任何 CI/CD 系统(如 Jenkins、Gitlab 等)都可以实现这一切。

标签: docker docker-compose dockerfile


【解决方案1】:

我大体上同意 Chris 对本地发展的回答。我将提供一些与最近的 Docker 功能相结合的东西,它可能会为使用同一映像进行本地开发和最终生产部署设置路径。

让我们首先从我们可以构建的镜像开始,该镜像可以用于本地开发或包含代码和依赖项的某个地方的部署。在最新的 Docker 版本(17.05)中,我们可以利用一个新的多阶段构建功能。在这种情况下,我们可以首先将所有 Composer 依赖项安装到构建上下文中的文件夹中,然后将它们复制到最终映像中,而无需将 Composer 添加到最终映像中。这可能看起来像:

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

这会从应用程序映像本身中删除所有 Composer,而是使用第一阶段在另一个上下文中安装依赖项并将它们复制到最终映像。

现在,在开发过程中,您有一些选择。根据您的docker-compose.yml 命令,听起来您正在将应用程序以.:/var/www/root 的身份安装到容器中。您可以将composer 服务添加到您的docker-compose.yml,类似于我在https://gist.github.com/andyshinn/e2c428f2cd234b718239 的示例。在这里,当您需要在本地更新依赖项时,您只需执行 docker-compose run --rm composer install(这会将依赖项构建在容器内,这对于本机编译的扩展可能很重要,尤其是当您部署为容器并在 Windows 或 Mac 上开发时)。

另一种选择是做一些类似于 Chris 已经建议的事情,并在需要时使用官方 Composer 映像来更新和管理依赖项。在我对 GitHub 有私有依赖项之前,我在本地做过类似的事情,需要 SSH 身份验证:

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

回顾一下,这种使用外部容器/服务构建映像和安装 Composer 依赖项的方法的原因:

  • 将为容器正确构建特定于平台的依赖项(Linux 架构与 Windows 或 Mac)。
  • 本地计算机上不需要 Composer 或 PHP(它们都包含在 Docker 和 Docker Compose 中)。
  • 您构建的初始映像是可运行和可部署的,无需将代码装入其中。在开发中,您只是用本地卷覆盖 /var/www/root 文件夹。

【讨论】:

  • 语句“在这种情况下,我们可以先将所有 Composer 依赖项安装到构建上下文中的文件夹中,然后再将它们复制到最终映像中,而无需将 Composer 添加到最终映像中。”让我大开眼界。不明白为什么 /vendor 文件夹没有出现在我的最终图像中,但是使用“COPY --from=composer /app/vendor /var/www/root/vendor”命令我已经启动并运行了!非常感谢@Andy Shinn!
【解决方案2】:

我已经在这个兔子洞里呆了 5 个小时,所有的解决方案都太复杂了。最简单的解决方案是exclude vendor or node_modules and similar directories from volume。

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

所以这将映射当前项目目录但不包括其供应商子目录。不要忘记尾部的斜线!

所以现在您可以轻松地在 dockerfile 中运行 composer install,当 docker 挂载您的卷时,它将忽略供应商目录。

【讨论】:

  • 我想相反。或除此之外。使用构建中的供应商作为 docker-compose 未来开发的起点。在 docker-compose up 之前,我看到了一个将供应商从构建复制到本地的选项。还有其他选择吗?
【解决方案3】:

如果这是针对一般开发环境,那么意图并不理想,因为它将应用程序耦合到 Docker 配置。

只需通过其他方式单独运行composer install(在 dockerhub 上有一个可用的图像,它允许您执行(docker run -it --rm -v $(pwd):/app composer/composer install)。


但是是的,您可能需要 Dockerfile 中的最后一行是 bash -c "composer install && php-fpm"。


  • 等待安装卷

不,在 docker 构建过程中无法挂载卷。虽然你可以把源代码复制进去。

  • 使用安装的 composer.json 文件运行 composer install

不,请参阅上面的回复。

  • 让容器在之后继续运行

是的,您需要执行php-fpm --nodaemonize(这是一个长时间运行的进程,因此它不会终止。

【讨论】:

  • 您可能会争辩说,composer install 的所有内容都添加了应包含在映像中的应用程序的 _are_dependencies。在外部执行此操作会增加这些依赖项在部署期间或环境之间丢失或不匹配的可能性。
  • 好点。在这种情况下,还必须添加源代码,这听起来不像 OP 的意图。
  • 我同意。我认为对于开发而言,您的回答确实有意义,我喜欢您通过使用composer 图像将其修改为更多的“Docker 方式”。我也在尝试提前考虑您最终将应用程序部署到生产环境的时间,在这种情况下,答案可能会有所不同。
【解决方案4】:

在 docker 容器上挂载卷后执行命令

假设您正在从公共 repo 获取依赖项

docker run --interactive -t --privileged --volume ${pwd}:/xyz composer /bin/sh -c 'composer install'

要从私有 git 存储库中获取依赖项,您需要复制/创建 ssh 密钥,我想这应该超出了这个问题的范围。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-10-26
    • 1970-01-01
    • 2016-12-15
    • 1970-01-01
    • 1970-01-01
    • 2019-08-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多