应用程序元数据

Spring Boot 支持在可执行 jar 中捆绑有关应用程序配置属性的元数据,用于工具和文档生成。在本节中,我们将讨论如何配置和构建应用程序,包括如何提供应用程序配置元数据作为容器镜像中的标签,以便与 Data Flow 配合使用。

对于您自己的应用程序,您可以轻松地生成应用程序配置元数据,方法是使用 spring-boot-configuration-processor 库从使用 @ConfigurationProperties 注释的类中生成。该库包含一个 Java 注释处理器,当您编译项目以生成配置元数据文件时,该处理器将被调用,该文件存储在 uber-jar 中,名为 META-INF/spring-configuration-metadata.json

要使用配置处理器,请在应用程序的 pom.xml 中包含以下依赖项

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>

公开数据流的应用程序属性

Stream 和 Task 应用程序是 Spring Boot 应用程序,它们提供通用的应用程序属性,以及数据流使用的通用属性,以及应用程序依赖项中包含的其他属性。对于典型的应用程序,可用的完整属性集可能非常庞大。这对数据流工具的可用性提出了挑战。因此,数据流 UI 和 shell 中的应用程序配置功能依赖于额外的配置属性元数据,以确保在提供上下文帮助(例如列出可用属性、处理自动完成和执行前端验证)时,默认情况下仅包含最相关的配置属性。

数据流配置元数据

要定义哪些应用程序属性与数据流最相关,请在项目资源目录中创建一个名为 META-INF/dataflow-configuration-metadata.properties 的文件。此文件必须包含以下一个或两个属性

  • configuration-properties.classes,包含以逗号分隔的完全限定 @ConfigurationProperties 类名列表。
  • configuration-properties.names,包含以逗号分隔的属性名称列表。这可以是完整的属性名称(例如 server.port)或前缀(例如 spring.jmx),以包含所有相关属性。

Spring Cloud Stream 应用程序 Git 存储库是查找示例的好地方。例如,jdbc sink 的 dataflow-configuration-metadata.properties 文件包含

configuration-properties.classes=org.springframework.cloud.fn.consumer.jdbc.JdbcConsumerProperties
configuration-properties.names=\
spring.datasource.url,\
spring.datasource.driver-class-name,\
spring.datasource.username,\
spring.datasource.password,\
spring.datasource.schema,\
spring.datasource.data,\
spring.datasource.initialization-mode

在这里,我们希望公开 sink 使用的特定 @ConfigurationProperties,以及配置 JDBC 数据源所需的一些标准 spring.datasource 配置属性。

打包配置元数据

在可执行 jar 或容器镜像中打包配置属性的常见步骤如下:

  1. 上文所述,将 Boot 的配置处理器添加到您的 pom.xml 中
  2. 上文所述,指定要公开的属性
  3. 如有必要,请按照此处的说明配置 spring-cloud-app-starter-metadata-maven-plugin

在容器镜像中使用应用程序元数据创建标签的附加步骤如下:

  1. 按照此处的说明配置 properties-maven-plugin,以将 META-INF/spring-configuration-metadata-encoded.properties 加载为 maven 属性。 除其他步骤外,它还将加载 org.springframework.cloud.dataflow.spring.configuration.metadata.json 属性。
  2. 如有必要,请按照此处的说明扩展 jib-maven-plugin(或 docker-maven-plugin)配置。

专用元数据工件

将应用程序元数据包含在 uber jar 中的缺点是需要下载可能非常大的 uber jar 才能检查元数据。 这会导致在调用需要此元数据的某些数据流操作时出现明显的延迟。 创建一个仅包含应用程序元数据的单独 jar 文件有几个优点

  • 元数据工件通常只有几 KB,而实际应用程序则有几 MB。 因此,它的下载速度更快,在使用数据流的 UI 和 shell 时可以实现快速响应时间。
  • 较小的尺寸也有助于资源受限的环境,例如 Cloud Foundry,在这些环境中,本地磁盘大小通常受到限制。

对于使用容器镜像的环境(例如 Kubernetes),数据流通过 REST API 访问已配置的容器注册表以查询元数据,而无需下载镜像。 或者,如果您选择创建专用的元数据 jar,数据流将使用它。

创建元数据工件

spring-cloud-app-starter-metadata-maven-plugin 插件有助于为您的应用程序准备所有必要的元数据文件。根据运行时的不同,元数据要么打包为单独的伴随工件 jar,要么打包为应用程序容器映像内的配置标签。要使用该插件,请将以下内容添加到您的 pom.xml

<plugin>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-dataflow-apps-metadata-plugin</artifactId>
   <version>1.0.2</version>
   <configuration>
      <storeFilteredMetadata>true</storeFilteredMetadata>
   </configuration>
   <executions>
      <execution>
         <id>aggregate-metadata</id>
         <phase>compile</phase>
         <goals>
            <goal>aggregate-metadata</goal>
         </goals>
      </execution>
   </executions>
</plugin>

您必须将此插件与创建 spring-configuration-metadata.json 文件的 spring-boot-configuration-processor 插件结合使用。请确保同时配置了这两个插件。

注意:Spring Boot 的 Cloud Native 构建包(强烈建议使用 Spring Boot 2.4.1 版及以上版本 的 Maven 插件的 build-image 目标)会自动为配置正确的应用程序提供此元数据。

元数据 Jar 文件

对于 uber-jar 打包的应用程序,该插件将创建一个包含元数据的伴随工件。具体来说,它包含有关配置属性元数据的 Spring Boot JSON 文件以及上一节中描述的数据流配置元数据文件。以下示例显示了此类工件的内容,以规范日志接收器为例

jar tvf log-sink-rabbit-3.0.0.BUILD-SNAPSHOT-metadata.jar
373848 META-INF/spring-configuration-metadata.json
   174 META-INF/dataflow-configuration-metadata.properties

spring-cloud-app-starter-metadata-maven-plugin 插件会生成一个随时可用的应用程序 metadata.jar 工件。请确保在应用程序的 pom 中配置了该插件。

元数据容器映像标签

对于打包为容器映像的应用程序,spring-cloud-app-starter-metadata-maven-plugin 会将 spring-configuration-metadata.json 文件的内容作为配置标签复制到生成的应用程序容器映像中,并将数据流的公开属性复制到 org.springframework.cloud.dataflow.spring.configuration.metadata.json 标签下。所有配置元数据都包含在容器映像中,因此不需要伴随工件。

在编译时,该插件会生成一个 META-INF/spring-configuration-metadata-encoded.properties 文件,其中包含一个属性:org.springframework.cloud.dataflow.spring.configuration.metadata.json。属性值是字符串化的、公开的配置元数据子集。以下清单显示了一个典型的元数据 JSON 文件

org.springframework.cloud.dataflow.spring.configuration.metadata.json={\n  \"groups\": [{\n    \"name\": \"log\",\n    \"type\": \"org.springframework.cloud.stream.app.log.sink.LogSinkProperties\",\n    \"sourceType\": \"org.springframework.cloud.stream.app.log.sink.LogSinkProperties\"\n  }],\n  \"properties\": [\n    {\n      \"name\": \"log.expression\",\n      \"type\": \"java.lang.String\",\n      \"description\": \"A SpEL expression (against the incoming message) to evaluate as the logged message.\",\n      \"sourceType\": \"org.springframework.cloud.stream.app.log.sink.LogSinkProperties\",\n      \"defaultValue\": \"payload\"\n    },\n    {\n      \"name\": \"log.level\",\n      \"type\": \"org.springframework.integration.handler.LoggingHandler$Level\",\n      \"description\": \"The level at which to log messages.\",\n      \"sourceType\": \"org.springframework.cloud.stream.app.log.sink.LogSinkProperties\"\n    },\n    {\n      \"name\": \"log.name\",\n      \"type\": \"java.lang.String\",\n      \"description\": \"The name of the logger to use.\",\n      \"sourceType\": \"org.springframework.cloud.stream.app.log.sink.LogSinkProperties\"\n    }\n  ],\n  \"hints\": []\n}

Properties Maven 插件

要将此属性转换为 Docker 标签,我们首先需要使用 properties-maven-plugin 插件将其加载为 Maven 属性。

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>properties-maven-plugin</artifactId>
    <version>1.0.0</version>
    <executions>
        <execution>
            <phase>process-classes</phase>
            <goals>
                <goal>read-project-properties</goal>
            </goals>
            <configuration>
                <files>
                    <file>${project.build.outputDirectory}/META-INF/spring-configuration-metadata-encoded.properties</file>
                </files>
            </configuration>
        </execution>
    </executions>
</plugin>

Container Maven 插件

借助 fabric8:docker-maven-pluginjib Maven 插件,将 org.springframework.cloud.dataflow.spring.configuration.metadata.json 属性插入到具有相同名称的 Docker 标签中。

<plugin>
    <groupId>com.google.cloud.tools</groupId>
    <artifactId>jib-maven-plugin</artifactId>
    <version>2.0.0</version>
    <configuration>
        <from>
            <image>springcloud/openjdk</image>
        </from>
        <to>
            <image>springcloudstream/${project.artifactId}</image>
            <tags>
                <tag>3.0.0.BUILD-SNAPSHOT</tag>
            </tags>
        </to>
        <container>
            <creationTime>USE_CURRENT_TIMESTAMP</creationTime>
            <format>Docker</format>
            <labels>
                <org.springframework.cloud.dataflow.spring-configuration-metadata.json>
                    ${org.springframework.cloud.dataflow.spring.configuration.metadata.json}
                </org.springframework.cloud.dataflow.spring-configuration-metadata.json>
            </labels>
        </container>
    </configuration>
</plugin>

注意:docker-maven-plugin 版本必须至少为 0.33.0 或更新版本!

<plugin>
    <groupId>io.fabric8</groupId>
    <artifactId>docker-maven-plugin</artifactId>
    <version>0.33.0</version>
    <configuration>
        <images>
            <image>
                <name>springcloudstream/${project.artifactId}:2.1.3.BUILD-SNAPSHOT</name>
                <build>
                    <from>springcloud/openjdk</from>
                    <volumes>
                        <volume>/tmp</volume>
                    </volumes>
                    <labels>
                        <org.springframework.cloud.dataflow.spring-configuration-metadata.json>
                          ${org.springframework.cloud.dataflow.spring.configuration.metadata.json}
                        </org.springframework.cloud.dataflow.spring-configuration-metadata.json>
                    </labels>
                    <entryPoint>
                        <exec>
                            <arg>java</arg>
                            <arg>-jar</arg>
                            <arg>/maven/log-sink-kafka.jar</arg>
                        </exec>
                    </entryPoint>
                    <assembly>
                        <descriptor>assembly.xml</descriptor>
                    </assembly>
                </build>
            </image>
        </images>
    </configuration>
</plugin>

使用应用程序元数据

生成应用程序配置元数据(作为单独的伴随工件或作为配置标签嵌入到应用程序容器镜像中)后,您可能需要一些额外的配置才能让 Data Flow 知道在哪里查找它。

使用元数据 Jar 文件

使用 app register 命令注册单个应用程序时,可以在 shell 中使用可选的 --metadata-uri 选项,如下所示:

dataflow:>app register --name log --type sink
    --uri maven://org.springframework.cloud.stream.app:log-sink:3.2.1
    --metadata-uri maven://org.springframework.cloud.stream.app:log-sink:jar:metadata:3.2.1

使用 app import 命令注册多个文件时,除了每个 <type>.<name> 行之外,该文件还应包含 <type>.<name>.metadata 行。严格来说,这样做是可选的(如果某些应用程序有,而其他应用程序没有,它也能工作),但这是最佳实践。

以下示例显示了一个 uber jar 应用程序,其中元数据工件托管在 Maven 存储库中(通过 http://file:// 检索它同样可行)。

source.http=maven://org.springframework.cloud.stream.app:log-sink:3.2.1
source.http.metadata=maven://org.springframework.cloud.stream.app:log-sink:jar:metadata:3.2.1

使用元数据容器镜像标签

使用 app register 命令注册单个 Docker 应用程序时,Data Flow 服务器会自动检查 org.springframework.cloud.dataflow.spring-configuration-metadata.json 配置标签中的元数据。

dataflow:>app register --name log --type sink --uri container:springcloudstream/log-sink-rabbit:2.1.13.RELEASE

配置特定于每个目标容器注册表提供程序或实例。

对于具有卷挂载密钥的私有容器注册表,注册表配置会自动从密钥中推断出来。此外,spring.cloud.dataflow.container.registry-configurations 还具有允许您显式配置多个容器注册表的属性,如下所示

容器注册表支持

您可以开箱即用地连接到各种云端和本地容器注册表,例如 HarborArifactory/JFrogAmazon ECRAzure 容器注册表托管您自己的私有注册表

由于不同的注册表可能会采用不同的身份验证方案,以下部分提供了特定于注册表的配置详细信息

- spring.cloud.dataflow.container.registry-configurations[default].registry-host=registry-1.docker.io
- spring.cloud.dataflow.container.registry-configurations[default].authorization-type=dockeroauth2
- spring.cloud.dataflow.container.registry-configurations[default].extra[registryAuthUri]=https://auth.docker.io/token?service=registry.docker.io&scope=repository:{repository}:pull&offline_token=1&client_id=shell
spring:
  cloud:
    dataflow:
      container:
        registry-configurations:
          default:
            registry-host: registry-1.docker.io
            authorization-type: dockeroauth2
            extra:
              'registryAuthUri': 'https://auth.docker.io/token?service=registry.docker.io&scope=repository:{repository}:pull&offline_token=1&client_id=shell'

默认情况下使用此注册表。如果镜像名称未提供注册表主机前缀。公共 Docker Hub 存储库不需要用户名和密码授权。但是,私有 Docker Hub 存储库需要凭据。

- spring.cloud.dataflow.container.registry-configurations[harbor].registry-host=demo.goharbor.io
- spring.cloud.dataflow.container.registry-configurations[harbor].authorization-type=dockeroauth2
- spring.cloud.dataflow.container.registry-configurations[harbor].user=admin
- spring.cloud.dataflow.container.registry-configurations[harbor].secret=Harbor12345
spring:
  cloud:
    dataflow:
      container:
        registry-configurations:
          harbor:
            registry-host: demo.goharbor.io
            authorization-type: dockeroauth2
            user: admin
            secret: Harbor12345

Harbor 注册表配置使用类似于 DockerHub 的 OAuth2 令牌授权,但使用不同的 registryAuthUri。后者会在引导时自动解析,但您可以像这样覆盖它

- spring.cloud.dataflow.container.registry-configurations[harbor].extra[registryAuthUri]=https://demo.goharbor.io/service/token?service=harbor-registry&scope=repository:{repository}:pull
spring:
  cloud:
    dataflow:
      container:
        registry-configurations:
          harbor:
            extra:
              'registryAuthUri': https://demo.goharbor.io/service/token?service=harbor-registry&scope=repository:{repository}:pull
- spring.cloud.dataflow.container.registry-configurations[myjfrog].registry-host=springsource-docker-private-local.jfrog.io
- spring.cloud.dataflow.container.registry-configurations[myjfrog].authorization-type=basicauth
- spring.cloud.dataflow.container.registry-configurations[myjfrog].user=[artifactory user]
- spring.cloud.dataflow.container.registry-configurations[myjfrog].secret=[artifactory encrypted password]
spring:
  cloud:
    dataflow:
      container:
        registry-configurations:
          myjfrog:
            registry-host: springsource-docker-private-local.jfrog.io
            authorization-type: basicauth
            user: [artifactory user]
            secret: [artifactory encrypted password]

注意:您需要在 JFrog 中创建一个加密密码

- spring.cloud.dataflow.container.registry-configurations[myecr].registry-host=283191309520.dkr.ecr.us-west-1.amazonaws.com
- spring.cloud.dataflow.container.registry-configurations[myecr].authorization-type=awsecr
- spring.cloud.dataflow.container.registry-configurations[myecr].user=[your AWS accessKey]
- spring.cloud.dataflow.container.registry-configurations[myecr].secret=[your AWS secretKey]
- spring.cloud.dataflow.container.registry-configurations[myecr].extra[region]=us-west-1
- spring.cloud.dataflow.container.registry-configurations[myecr].extra[registryIds]=283191309520
spring:
  cloud:
    dataflow:
      container:
        registry-configurations:
          myecr:
            registry-host: 283191309520.dkr.ecr.us-west-1.amazonaws.com
            authorization-type: awsecr
            user: [your AWS accessKey]
            secret: [your AWS secretKey]
            extra:
              region: us-west-1
              'registryIds': 283191309520

除了凭据之外,您还必须通过额外属性配置提供注册表的 region(例如,.extra[region]=us-west-1)。或者,您可以通过将 .extra[registryIds] 属性设置为逗号分隔值来设置注册表 ID。

- spring.cloud.dataflow.container.registry-configurations[myazurecr].registry-host=tzolovazureregistry.azurecr.io
- spring.cloud.dataflow.container.registry-configurations[myazurecr].authorization-type=basicauth
- spring.cloud.dataflow.container.registry-configurations[myazurecr].user=[your Azure registry username]
- spring.cloud.dataflow.container.registry-configurations[myazurecr].secret=[your Azure registry access password]
spring:
  cloud:
    dataflow:
      container:
        registry-configurations:
          myazurecr:
            registry-host: tzolovazureregistry.azurecr.io
            authorization-type: basicauth
            user: [your Azure registry username]
            secret: [your Azure registry access password]

自定义

属性可以覆盖或增强通过注册表密钥获得的配置。例如,如果您创建了一个密钥来访问运行在地址 my-private-registry:5000 的注册表,则可以按如下方式禁用此注册表的 SSL 验证

- spring.cloud.dataflow.container.registry-configurations[myregistry].registry-host=my-private-registry:5000
- spring.cloud.dataflow.container.registry-configurations[myregistry].disableSslVerification=true
spring:
  cloud:
    dataflow:
      container:
        registry-configurations:
          myregistry:
            registry-host: my-private-registry:5000
            disableSslVerification: true

这对于使用自签名证书测试注册表非常方便。

  • 通过 Http 代理连接

您可以通过预先配置的代理重定向某些注册表配置。例如,如果通过配置在 my-proxy.test:8080 的代理访问运行在地址 my-private-registry:5000 的注册表,

- spring.cloud.dataflow.container.http-proxy.host=my-proxy.test
- spring.cloud.dataflow.container.http-proxy.port=8080
- spring.cloud.dataflow.container.registry-configurations[myregistrywithproxy].registry-host=my-proxy-registry:5000
- spring.cloud.dataflow.container.registry-configurations[myregistrywithproxy].use-http-proxy=true
spring:
  cloud:
    dataflow:
      container:
        httpProxy:
          host: my-proxy.test
          port: 8080
        registry-configurations:
          myregistrywithproxy:
            registry-host: my-proxy-registry:5000
            use-http-proxy: true

spring.cloud.dataflow.container.http-proxy 属性允许您配置全局 HTTP 代理,并且您可以选择使用注册表配置 use-http-proxy 属性为每个注册表使用代理。默认情况下不使用代理。