调用 docker-compose build 命令时执行 Spring buildpacks

Mic*_*hal 4 spring buildpack docker docker-compose paketo

我正在使用 Spring Boot 2.3.0.M1引入的 Spring buildpacks来创建基于 Spring 的应用程序的 Docker 映像。一切顺利,我可以通过执行./gradlew bootBuildImageGradle 任务为每个应用程序创建 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 镜像 repo(可能是 SSH 连接),最后在 docker 上执行 buildpacks。

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

jon*_*ckt 6

这个问题真的让我发疯,因为我已经玩了很长一段时间 Spring Boot 和 Paketo Buildpacks -我真的很喜欢 Docker-Compose 的简单性。所以这些问题已经在我的脑海里了,但后来你问了它:)

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

TLDR:我的想法是使用spring-boot-maven-plugin(或它的 Gradle 等价物)在每个“docker-compose up像这样的正常”之前发布一个新的 Paketo 构建:

mvn clean spring-boot:build-image && docker-compose up
Run Code Online (Sandbox Code Playgroud)

示例项目父项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>
Run Code Online (Sandbox Code Playgroud)

示例项目docker-compose.yml看起来简单,使用由Paketo(这是由Maven插件触发)开发生产的集装箱图像-这些被命名为这样的:eureka-serviceregistry:0.0.1-SNAPSHOT。以下是docker-compose.yml所有 Spring Boot 服务的完整内容:

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
Run Code Online (Sandbox Code Playgroud)

===可能的改进======================

这个想法让我也有“单独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"
Run Code Online (Sandbox Code Playgroud)

我首先在docker-compose.yml我已经拥有的服务中集成了这项服务。运行 adocker-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'
...
Run Code Online (Sandbox Code Playgroud)

但由于种种原因,这感觉不对。一是您需要以某种方式等待所有其他 Compose 服务的启动,直到该paketo-build服务完成其工作并构建所有图像。但是正如 Docker 文档告诉我们的那样,我们需要反对在 Compose 中做出的设计决策才能实现这一目标!还没有,我觉得这个伟大的答案,其中Max解释说,这不是一个好的设计“污染”的“生产”docker-compose.yml与仅仅是那里为构建容器。

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

docker-compose -f build.yml up && docker-compose up
Run Code Online (Sandbox Code Playgroud)

请记住不要与第一个容器分离,-d因为在我们开始我们的docker-compose.yml. 使用这种方法,我们也绝对不需要Dockerfile. 但与此同时,我想到了完全消除对单独构建容器的需求,并在与 TLDR 中已经描述up&&类似内容连接之前简单地使用 Maven(或 Gradle)

mvn clean spring-boot:build-image && docker-compose up
Run Code Online (Sandbox Code Playgroud)

希望这对你有帮助。很高兴听到您的反馈!这里还有一个完整的 GitHub 操作构建,展示了 Cloud CI 服务器上的所有“魔法”。

现在没有办法docker-compose up --build使用 Paketo Buildpacks 来触发所有 Spring Boot 应用程序的新映像构建。

  • 感谢您的回答@Johnas。这不是我真正想要的,但已经非常接近了。最后,我刚刚创建了一个单独的 docker-compose 文件,在其中执行脚本以在已安装的卷下构建项目。这似乎是一个不错的选择,因为我们不依赖 gradle/java/etc.. 主机的配置。也许在 docker-compose 中创建多个服务可能有用,以使构建速度更快。 (3认同)