【问题标题】:Execute Spring buildpacks when calling docker-compose build command调用 docker-compose build 命令时执行 Spring buildpacks
【发布时间】:2021-03-27 05:22:46
【问题描述】:

我正在使用 Spring Boot 2.3.0.M1 引入的 Spring buildpacks 来创建基于 Spring 的应用程序的 Docker 映像。一切顺利,我可以通过执行./gradlew bootBuildImage Gradle 任务为每个应用程序创建 docker 图像,将 docker-compose 文件指向创建的图像(例如image: spring-test:latest)并最终成功运行所有应用程序(docker-compose up)。

尽管我有一个 bash 脚本来自动化构建过程,但我想摆脱这个额外的步骤,让 Spring buildpacks 任务在我运行 docker-compose up --build 命令时自动执行,因此每个应用程序的 docker 映像都会构建并上传到主机的本地 docker 存储库,由 docker compose 接管。

我的第一次尝试是为每个在主机上执行bootBuildImage 任务的应用程序创建一个虚拟 Dockerfile,但这需要从 docker 到主机的 SSH 连接,甚至不确定是否能正常工作。

另一个想法是使用类似的方法,唯一的变化是首先将应用程序的源代码挂载或复制到 docker,配置 buildpacks 以将图像存储到主机的本地 docker image repo(可能是 SSH 连接),最后在 docker 上执行 buildpacks。

我想知道是否没有更好、更优雅的解决方案。

【问题讨论】:

    标签: spring docker docker-compose buildpack paketo


    【解决方案1】:

    这个问题真的把我逼疯了,因为I've been playing around with Spring Boot & Paketo Buildpacks for quite a while now - 而且我真的很喜欢 Docker-Compose 的简单性。所以问题已经在我的脑海里了,但后来你问了:)

    我没有找到 100% 完美的解决方案,但我认为有一些想法。让我们假设一个由多个 Spring Boot 应用程序组成的示例项目,这些应用程序由 Maven 多模块设置组成(我知道您使用的是 Gradle,但在出色的 spring.io 指南中有a guide on doing multi module setups with Gradle): github.com/jonashackt/cxf-spring-cloud-netflix-docker。我创建了一个新分支 buildpacks-paketo 包含我们需要的所有内容 - 并从相应的 Spring Boot 应用程序中删除了所有 Dockerfiles。因为我们不应该再使用Cloud Native Buildpacks(这是他们的设计目标)了。

    TLDR:我的想法是使用 spring-boot-maven-plugin(或它的 Gradle 等效项)在每个“正常的 docker-compose up 之前发布一个新的 Paketo 构建,如下所示:

    mvn clean spring-boot:build-image && docker-compose up
    

    The example projects parent pom.xml 看起来像这样(缩短):

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>de.jonashackt</groupId>
        <artifactId>cxf-spring-cloud-netflix-docker-build-all</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <packaging>pom</packaging>
    
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.4.1</version>
        </parent>
    
    
        <modules>
            <module>eureka-serviceregistry</module>
            <module>spring-boot-admin</module>
            <module>zuul-edgeservice</module>
            <module>weatherbackend</module>
            <module>weatherservice</module>
            <module>weatherclient</module>
        </modules>
    
    </project>
    

    example projects docker-compose.yml 看起来很简单,它使用 Paketo 生成的容器镜像(由 Maven 插件触发)——它们的名称如下:eureka-serviceregistry:0.0.1-SNAPSHOT。这是所有 Spring Boot 服务的完整 docker-compose.yml

    version: '3.3'
    
    services:
    
      eureka-serviceregistry:
        image: eureka-serviceregistry:0.0.1-SNAPSHOT
        ports:
         - "8761:8761"
        tty:
          true
        restart:
          unless-stopped
    
      spring-boot-admin:
        image: spring-boot-admin:0.0.1-SNAPSHOT
        ports:
         - "8092:8092"
        environment:
          - REGISTRY_HOST=eureka-serviceregistry
        tty:
          true
        restart:
          unless-stopped
    
      # no portbinding here - the actual services should be accessible through Zuul proxy
      weatherbackend:
        image: weatherbackend:0.0.1-SNAPSHOT
        ports:
         - "8090"
        environment:
          - REGISTRY_HOST=eureka-serviceregistry
        tty:
          true
        restart:
          unless-stopped
    
      # no portbinding here - the actual services should be accessible through Zuul proxy
      weatherservice:
        image: weatherservice:0.0.1-SNAPSHOT
        ports:
         - "8095"
        environment:
          - REGISTRY_HOST=eureka-serviceregistry
        tty:
          true
        restart:
          unless-stopped
    
      zuul-edgeservice:
        image: zuul-edgeservice:0.0.1-SNAPSHOT
        ports:
         - "8080:8080"
        environment:
          - REGISTRY_HOST=eureka-serviceregistry
        tty:
          true
        restart:
          unless-stopped
    

    === 可能的增强功能 =====================

    这个想法让我也有“在单个 docker-compose.yml 上,并且只按照你的要求使用 docker-compose up - 没有额外的命令。因此我创建了另一个“Docker Compose 构建服务”,它应该只构建这样的服务映像:

    version: '3.3'
    
    services:
    
      paketo-build:
        image: maven:3.6-openjdk-15
        command: "mvn clean spring-boot:build-image -B -DskipTests --no-transfer-progress" # build all apps
        volumes:
          - "/var/run/docker.sock:/var/run/docker.sock:ro" # Mount Docker from host into build container for Paketo to work
          - "$HOME/.m2:/root/.m2" # Mount your local Maven repository into build container to prevent repeated downloads
          - "$PWD:/workspace" # Mount all Spring Boot apps into the build container
        working_dir: "/workspace"
    

    我首先将此服务集成到我已经拥有的docker-compose.yml 中。运行 docker-compose up paketo-build 完成了我一直在寻找的事情:在 Compose 设置中构建我们所有的 Spring Boot 应用程序:

    ...
    paketo-build_1  | [INFO] --- spring-boot-maven-plugin:2.4.1:build-image (default-cli) @ eureka-serviceregistry ---
    paketo-build_1  | [INFO] Building image 'docker.io/library/eureka-serviceregistry:0.0.1-SNAPSHOT'
    paketo-build_1  | [INFO]
    paketo-build_1  | [INFO]  > Pulling builder image 'docker.io/paketobuildpacks/builder:base' 100%
    paketo-build_1  | [INFO]  > Pulled builder image 'paketobuildpacks/builder@sha256:3cff90d13d353ffdb83acb42540dae4ce6c97d55c07fb01c39fe0922177915fa'
    paketo-build_1  | [INFO]  > Pulling run image 'docker.io/paketobuildpacks/run:base-cnb' 100%
    paketo-build_1  | [INFO]  > Pulled run image 'paketobuildpacks/run@sha256:f393fa2927a2619a10fc09bb109f822d20df909c10fed4ce3c36fad313ea18e3'
    paketo-build_1  | [INFO]  > Executing lifecycle version v0.10.1
    paketo-build_1  | [INFO]  > Using build cache volume 'pack-cache-9d8694845b92.build'
    paketo-build_1  | [INFO]
    paketo-build_1  | [INFO]  > Running creator
    paketo-build_1  | [INFO]     [creator]     ===> DETECTING
    ...
    paketo-build_1  | [INFO]     [creator]
    paketo-build_1  | [INFO]     [creator]     Paketo Spring Boot Buildpack 3.5.0
    paketo-build_1  | [INFO]     [creator]       https://github.com/paketo-buildpacks/spring-boot
    paketo-build_1  | [INFO]     [creator]       Creating slices from layers index
    ...
    paketo-build_1  | [INFO]     [creator]     Adding label 'io.buildpacks.project.metadata'
    paketo-build_1  | [INFO]     [creator]     Adding label 'org.opencontainers.image.title'
    paketo-build_1  | [INFO]     [creator]     Adding label 'org.opencontainers.image.version'
    paketo-build_1  | [INFO]     [creator]     Adding label 'org.springframework.boot.spring-configuration-metadata.json'
    paketo-build_1  | [INFO]     [creator]     Adding label 'org.springframework.boot.version'
    paketo-build_1  | [INFO]     [creator]     Setting default process type 'web'
    paketo-build_1  | [INFO]     [creator]     *** Images (7efae8be1167):
    paketo-build_1  | [INFO]     [creator]           docker.io/library/eureka-serviceregistry:0.0.1-SNAPSHOT
    paketo-build_1  | [INFO]
    paketo-build_1  | [INFO] Successfully built image 'docker.io/library/eureka-serviceregistry:0.0.1-SNAPSHOT'
    ...
    

    但由于很多原因,这感觉不对。一是您需要以某种方式等待所有其他 Compose 服务的启动,直到 paketo-build 服务完成它的工作并构建所有图像。 BUT as the Docker docs tell us,我们需要根据 Compose 中的设计决策来实现这一目标!还有did I find this great answer,Max 解释说用专门用于构建的容器“污染”“生产”docker-compose.yml 不是一个好的设计。

    之后,我将 paketo-build 服务提取到它自己的 Compose 文件中 - 称为 build.yml inside the example project。有了它,我们现在可以运行 Paketo 构建,而无需依赖主机安装 Maven - 并且仅使用 Docker-Compose:

    docker-compose -f build.yml up && docker-compose up
    

    记住不要使用-d 与第一个容器分离,因为在我们开始docker-compose.yml 之前必须完成完整的Paketo 构建。使用这种方法,我们也绝对不需要Dockerfile。但与此同时,我想到完全消除对单独构建容器的需求,只需在 up 之前使用 Maven(或 Gradle)&amp;&amp; 连接,就像已经描述的那样TLDR:

    mvn clean spring-boot:build-image && docker-compose up
    

    希望这对您有所帮助。很高兴听到您的反馈! Here's also a full GitHub actions build 展示了云 CI 服务器上的所有“魔法”。

    目前,afaik 无法使用 docker-compose up --build 使用 Paketo Buildpacks 触发所有 Spring Boot 应用程序的全新映像构建。

    【讨论】:

    • 感谢您的回答@Johnas。这不是我真正想要的,但它非常接近。最后,我刚刚创建了一个单独的 docker-compose 文件,我在其中执行脚本以在已安装的卷下构建项目。这似乎是一个不错的选择,因为我们不依赖 gradle/java/etc.. 主机的配置。在 docker-compose 中创建多个服务以加快构建速度可能会很有用。
    • 很高兴听到它对您有帮助 :) 您在 gitlab.com/spring-cloud-infrastructure/docker 的示例 repo 看起来非常棒!
    猜你喜欢
    • 2019-10-22
    • 2019-10-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-15
    • 1970-01-01
    • 2023-01-28
    • 1970-01-01
    相关资源
    最近更新 更多