代码之家  ›  专栏  ›  技术社区  ›  Hirnhamster

Docker:如何在多个容器/服务(如PHP-FPM和NGINX)之间共享相同的应用程序代码

  •  1
  • Hirnhamster  · 技术社区  · 6 年前

    tl;博士

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

    问:用docker/docker compose来归档的好策略/工作流是什么。

    我们正在对接包含多个组件(容器/服务)的PHP应用程序,例如。

    • 工作节点(PHP进程通过supervisor保持活动)
    • 调度程序(在cron中管理工人和运行周期性任务)
    • PHP-FPM/Nginx(网络接口)

    服务在docker compose文件中定义。 在开发过程中,我们通过卷从每个容器中主机上的一个目录装载应用程序代码, 这样我们就可以“立即”看到每个服务的变化( Example ). 生活很美好。

    我们现在正在建立一个基于Jenkins的CI/CD环境,它应该构建(测试)容器和 稍后将推送到注册表。既然“从主机装载”已经不可能了,我现在想知道 最好的方法是将应用程序代码放在每个容器中。

    在我们的设置中有两件事使这个imho特别复杂:

    1. 我们有多个需要相同应用程序代码的容器
    2. “构建工件”不是一个单独的自容器二进制文件(例如,使用go) 但是“我们所有的代码+安装的依赖项”==>“很多文件”(慢…)
    3. 有一个构建步骤需要最终映像中不需要的软件

    “3”的解决方案通常是:使用多阶段构建。我们这样做。但是:所有的例子 似乎假设生成的代码只在另一个容器中使用(在我们的例子中不是这样,请参阅1)

    我们现在所做的

    • 文件夹结构
    application-code/
      .docker/
        builder/
          Dockerfile
        php-fpm/
          Dockerfile
        docker-compose.yml
      build.sh
      index.php
    
    • 引入一个额外的“builder”容器来“构建”应用程序 (获取“all”应用程序代码作为生成上下文;运行“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不会安装composer)
    • 应用程序代码只生成一次,然后复制到

    相反

    • 生成器容器没有其他用途,只是“build”==>感觉不干净
    • 建造“建造者”必须在建造任何其他容器之前完成
      • 这意味着我们还有

    选择

    • 不要使用构建器容器,而是将构建步骤“合并”到“调度程序”容器中 通过使用多阶段构建(这样我们就不会在最终图像中使用composer)
      • 去掉“builder”-但现在所有其他服务都依赖于“Scheduler”==>感觉更脏
    • 使用卷共享代码
      • 我们不必“复制”图像中的文件,只需“装入卷”==>感觉“干净“/不复制文件” (我首先认为这是一个非常好的方法……)
      • 但是:
      • 在构建期间不能填充卷,因此需要“运行”容器才能在容器中获取appliation代码 ==>我们突然不仅有一个构建器容器,而且还需要“运行”它来填充卷
      • 容器不再是“自包含”的,即从注册表中提取“仅调度程序”将不起作用- 我们还必须将卷放在适当的位置,并且它必须由生成器填充==>编排变得更加复杂
      • 卷不是短暂的,即在刷新之前它将包含“旧”应用程序代码==>这可能会导致 混乱和意外行为

    链接

    1 回复  |  直到 6 年前
        1
  •  0
  •   Luceos    6 年前

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

    您的builder方法明显缺少的一点是,尽管您使用 install . 为了使生产级代码库投入生产,您至少需要运行 composer install --prefer-dist --no-dev -o 致:

    • 删除排除在 .gitattributes 通过使用 --prefer-dist (这是默认的,因为composer稳定)
    • 通过排除下载require dev --no-dev
    • 通过优化autoloader文件 -o

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

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

    • 生成: 签出、编写器安装并允许在以下步骤中重用或创建“测试映像”
    • 测试: 运行测试并包含或分离覆盖(进入同步步骤)
    • 准备: 重用生成文件并使用必要的生产标志运行composer安装
    • 形象 :从上一步创建图像

    我个人认为你的问题的答案很有主见。也许我和库伯内特斯合作的方法会有帮助。

    推荐文章