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

Execute Spring buildpacks when calling docker-compose build command

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

即使我有一个 bash 脚本来自动执行构建过程,我还是想去掉这个额外的步骤,让 Spring buildpacks 任务在我 [=43] 时自动执行=] docker-compose up --build 命令因此每个应用程序的 docker 图像将被构建并上传到主机的本地 docker 存储库,从那里它将被 docker compose 接管。

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

另一个想法是使用类似的方法,唯一的改变是首先将应用程序的源代码安装或复制到 docker,配置 buildpacks 以将图像存储到主机的本地 docker 图像存储库(也许SSH 连接),最后在 docker.

上执行 buildpacks

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

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

我没有找到 100% 完美的解决方案,但我认为有一些想法。让我们假设一个包含多个 Spring 引导应用程序的示例项目,它们由 Maven 多模块设置组成(我知道您使用的是 Gradle,但是 a guide on doing multi module setups with Gradle 在 spring.io 指南): github.com/jonashackt/cxf-spring-cloud-netflix-docker. I created a new branch buildpacks-paketo containing all we need - and removed all Dockerfiles from the respective Spring Boot apps. Since we shouldn't need them anymore using Cloud Native Buildpacks(这是他们的设计目标)。

TLDR: 我的想法是使用 spring-boot-maven-plugin(或者它的 Gradle 等效)在每个“正常 docker-compose up 像这样:

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 引导服务的完整 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 中。 运行 a docker-compose up paketo-build 完成了我正在寻找的事情:在 Compose 设置中构建我们所有的 Spring 引导应用程序:

...
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, we will need to work against design decisions made in Compose to make that happen! Also ,其中 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)通过 && 就像 TLDR 中已经描述的那样:

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

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

现在 afaik 无法使用 docker-compose up --build 触发所有 Spring 使用 Paketo Buildpacks 启动应用程序的全新映像构建。