1

tl;博士

  • 应用程序代码需要一个构建步骤(引入依赖项)
  • 多个容器需要相同的“构建”代码

问:使用 docker / docker-compose 存档的好的策略/工作流程是什么?

我们正在使用多个组件(容器/服务)对 PHP 应用程序进行 dockerizing,例如

  • 工作节点(PHP 进程通过主管保持活动状态)
  • 调度程序(管理工作人员并在 cron 中运行重复任务)
  • PHP-FPM/Nginx(网页界面)

这些服务在 docker-compose 文件中定义。在开发过程中,我们通过卷从每个容器的主机上的目录中挂载应用程序代码,以便我们“立即”看到每个服务中的更改(示例)。生活很好。

我们现在正在建立一个基于 Jenkins 的 CI/CD 环境,该环境应该构建(+ 测试)容器,然后将其推送到注册表。由于“从主机挂载”不再可能,我现在想知道将应用程序代码放入每个容器中的最佳方法是什么。

我们的设置中有两件事使这个恕我直言特别复杂:

  1. 我们有多个容器需要相同的应用程序代码
  2. “build-artifact”不是一个单一的、自容器的二进制文件(就像我们使用 go 那样),而是“我们所有的代码 + 安装的依赖项”==>“很多文件”(慢...)
  3. 涉及的构建步骤需要最终映像中不需要的软件

“3”的解决方案。通常是:使用多阶段构建。我们这样做。但是:那里的所有示例似乎都假设构建的代码将仅在另一个容器中使用(在我们的示例中并非如此,请参见 1。)

我们目前所做的

  • 文件夹结构
application-code/
  .docker/
    builder/
      Dockerfile
    php-fpm/
      Dockerfile
    docker-compose.yml
  build.sh
  index.php
  • 引入一个额外的“构建器”容器来“构建”应用程序(获取“所有”应用程序代码作为构建上下文;运行“composer install”)
# ./builder/Dockerfile
COPY ./ /codebase
RUN cd /codebase && composer install
  • 在每个需要应用程序代码的容器中从此构建器“复制”,例如通过
# ./php-fpm/Dockerfile
ARG APP_CODE_PATH="/var/www/current"
COPY --from=builder --chown=www-data /codebase ${APP_CODE_PATH}
  • 通过 docker-compose 编排
# ./docker-compose.yml
version: '3.7'

services:
  builder-ci:
    image: builder
    build:
      # ../ contains the "raw" application code
      context: ../
      dockerfile: ./.docker/builder/Dockerfile

    php-fpm:
      build:
        context: .
        dockerfile: ./php-fpm/Dockerfile
        args:
          - APP_CODE_PATH=/var/www/current
  • 通过构建
# build.sh
## build builder
docker-compose -f ./.docker/docker-compose.yml --project-directory ./.docker build builder
## build the rest
docker-compose -f ./.docker/docker-compose.yml --project-directory ./.docker build --parallel

  • “较小”的图像(php-fpm 不会安装作曲家)
  • 应用程序代码只构建一次,然后复制过来

魂斗罗

  • 构建器容器除了“构建”之外没有其他用途 ==> 感觉不干净
  • 构建“构建器”必须在构建任何其他容器之前完成
    • 这意味着我们有一个额外的

备择方案

  • 不要使用构建器容器,而是通过使用多阶段构建将构建步骤“合并”到例如“调度程序”容器中(因此我们最终不会在最终图像中使用作曲家)
    • 摆脱“builder” - 但现在所有其他服务都依赖于“Scheduler” ==> 感觉更脏
  • 使用卷共享代码
    • 我们不必“复制”图像中的文件,而可以简单地“安装卷”==>感觉“干净”/没有“文件重复”(我首先认为这是一个非常好的方法......)
    • 但:
    • 您无法在构建期间填充卷,因此您需要“运行”容器以便在容器“中”获取应用程序代码 ==> 我们突然间不仅有一个构建器容器,而且我们还需要“运行”它填充音量
    • 容器不再“自包含”,即从注册表中提取“仅调度程序”将不起作用 - 我们还必须有适当的卷并且必须由构建器填充 ==> 编排变得更加复杂
    • 卷不是临时的,即在刷新之前它将包含“旧”应用程序代码 ==> 这可能会导致混乱和意外行为

链接

4

1 回答 1

0

担心步数不应该是一个讨论。您应该担心的是图像是否包含您需要的内容,没有任何噪音。

您的构建器方法明显缺乏的一件事是,即使您仅使用install. 为了将生产级代码库投入生产,您至少需要运行composer install --prefer-dist --no-dev -o

  • .gitattributes通过使用删除排除在外的文件--prefer-dist(这是默认的,因为作曲家稳定)
  • 通过排除下载 require-dev 与--no-dev
  • 通过优化自动加载器文件-o

此命令与用于运行测试的命令完全不同。

使用容器化代码库时的默认管道是:

  • 构建:结帐,作曲家安装并允许在以下步骤中重复使用或创建“测试图像”
  • 测试:运行测试并包含或分离覆盖率(进入同时步骤)
  • 准备:重用构建文件并使用必要的生产标志运行 composer install
  • image : 从上一步创建图像

我个人认为你的问题的答案是高度自以为是的。也许我使用 Kubernetes 的方法会有所帮助。

于 2019-01-23T18:43:41.353 回答