【问题标题】:Spring Native: How to pass environment variables in at build timeSpring Native:如何在构建时传递环境变量
【发布时间】:2022-06-14 20:58:23
【问题描述】:

我想在我的项目中使用 Spring Native。 目前在运行应用程序时,我在运行时传入环境变量。 我需要在构建时将这些环境变量烘焙到映像中。

当我对环境变量进行硬编码时,Spring Native 可以正常工作,但我不想将它们提交给源代码管理。我宁愿我的 CI 工具在构建时传递它们。

有人做到了吗?我找不到任何说明如何操作的文档。

目前我的尝试都报错了:

build.gradle


    plugins {
        id "java"
        id "org.springframework.boot" version "2.6.2"
        id 'io.spring.dependency-management' version '1.0.11.RELEASE'
        id "jacoco"
        id "org.flywaydb.flyway" version "7.11.2"
        id "com.github.ben-manes.versions" version "0.39.0"
        id 'org.springframework.experimental.aot' version '0.11.1'
    }
    
    group = 'org.api'
    
    repositories {
        mavenCentral()
        maven { url 'https://repo.spring.io/release' }
    }
    
    dependencies {
    
        // Spring Boot
        implementation('org.springframework.boot:spring-boot-starter-actuator')
        implementation('org.springframework.boot:spring-boot-starter-data-jpa')
        implementation('org.springframework.boot:spring-boot-starter-hateoas')
        implementation('org.springframework.boot:spring-boot-starter-web')
        implementation('org.springframework.boot:spring-boot-starter-security')
        implementation('org.springframework.boot:spring-boot-starter-aop')
        implementation('org.springframework.boot:spring-boot-starter-validation')
        runtimeOnly('org.springframework.boot:spring-boot-devtools')
        annotationProcessor "org.springframework.boot:spring-boot-configuration-processor"
    
        // Testing
        testImplementation(platform('org.junit:junit-bom:5.7.0'))
        testImplementation('org.junit.jupiter:junit-jupiter')
        testImplementation('org.springframework.boot:spring-boot-starter-test')
        testImplementation("org.assertj:assertj-core:3.21.0")
        testImplementation 'org.mockito:mockito-core:4.1.0'
        testImplementation "org.springframework.security:spring-security-test"
    
        // Persistence
        implementation('com.h2database:h2')
        implementation('org.postgresql:postgresql')
        implementation('org.springframework.boot:spring-boot-starter-jdbc')
        implementation "org.flywaydb:flyway-core:8.1.0"
    
        // Swagger
        implementation 'org.springdoc:springdoc-openapi-ui:1.5.12'
        implementation 'org.springdoc:springdoc-openapi-hateoas:1.5.12'
        implementation 'org.springdoc:springdoc-openapi-security:1.5.12'
    
        // Observability
        implementation 'io.sentry:sentry-spring-boot-starter:5.4.3'
        implementation 'io.sentry:sentry-logback:5.4.3'
    
        // Security
        implementation 'com.auth0:auth0-spring-security-api:1.4.1'
        implementation 'org.springframework.security:spring-security-oauth2-resource-server'
        implementation 'org.springframework.security:spring-security-oauth2-jose'
        implementation 'org.springframework.security:spring-security-config'
    
        // Outgoing Email
        implementation 'com.sendgrid:sendgrid-java:4.8.0'
    
        // Serialisation
        implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.0'
    }
    
    sourceSets {
        test {
            resources {
                srcDir file('src/test/java')
                exclude '**/*.java'
            }
        }
    }
    
    test {
        useJUnitPlatform()
        testLogging {
            events "passed", "skipped", "failed"
        }
        systemProperty 'PORT', '8080'
        systemProperty 'SENTRY_DSN', 'https://123@456.ingest.sentry.io/789'
    }
    
    flyway {
        url = 'jdbc:postgresql://localhost:5432/api'
        user = 'postgres'
        password = 'mysecretpassword'
        schemas = ['public']
    }
    
    dependencyUpdates {
        outputFormatter = "html"
    }
    
    bootBuildImage {
        builder = "paketobuildpacks/builder:tiny"
        environment = [
                'BP_NATIVE_IMAGE' : 'true'
        ]
    }

settings.gradle

    pluginManagement {
        repositories {
            maven { url 'https://repo.spring.io/release' }
            gradlePluginPortal()
        }
    }
    
    rootProject.name = 'api'

application.yml


    spring:
      hateoas.use-hal-as-default-json-media-type: false
      data:
        jpa:
          repositories:
            bootstrap-mode: deferred
      jpa:
        open-in-view: false
        properties:
          hibernate.jdbc.time_zone: UTC
        hibernate:
          ddl-auto: none
          naming:
            physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
            implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
      jmx:
        enabled: false
      security:
        oauth2:
          resourceserver:
            jwt:
              issuer-uri: https://localhost:9000
    
    auth0:
      audience: http://localhost:8080
    
    springdoc:
      packages-to-scan: org.api
      swagger-ui:
        path: /swagger-ui.html
        operationsSorter: method
        tagsSorter: alpha
        docExpansion: list
    
    management:
      endpoint:
        health:
          show-details: when_authorized
    
    server:
      port: ${PORT} <--- Environment Variable I need to pass in at build time
    
    sentry:
      dsn: ${SENTRY_DSN} <--- Environment Variable I need to pass in at build time
      max-breadcrumbs: 150
      logging:
        minimum-event-level: warn
        minimum-breadcrumb-level: info
      traces-sample-rate: 1.0
      in-app-includes: org.api

尝试编译使用: ./gradlew bootbuildimage -DSENTRY_DSN=https://123@456.ingest.sentry.io/789 -DPORT=8080

尝试将此添加到build.gradle

    bootBuildImage {
        builder = "paketobuildpacks/builder:tiny"
        environment = [
                'BP_NATIVE_IMAGE' : 'true',
                'SENTRY_DSN' : 'https://123@456.ingest.sentry.io/789',
                'PORT' : '8080'
        ]
    }

所有结果:


    > Task :generateAot
    2022-01-10 19:29:24.426  INFO 7749 --- [           main] o.s.a.build.ContextBootstrapContributor  : Detected application class: nz.ringfence.valuable.api.ValuableApiApplication
    2022-01-10 19:29:24.429  INFO 7749 --- [           main] o.s.a.build.ContextBootstrapContributor  : Processing application context
    org.springframework.beans.factory.BeanDefinitionStoreException: Failed to process import candidates for configuration class [nz.ringfence.valuable.api.ValuableApiApplication]; nested exception is java.lang.IllegalStateException: Error processing condition on io.sentry.spring.boot.SentryAutoConfiguration
            at org.springframework.context.annotation.ConfigurationClassParser.processImports(ConfigurationClassParser.java:610)
            at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorGroupingHandler.lambda$processGroupImports$1(ConfigurationClassParser.java:812)
            at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
            at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorGroupingHandler.processGroupImports(ConfigurationClassParser.java:809)
            at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorHandler.process(ConfigurationClassParser.java:780)
            at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:193)
            at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:331)
            at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:247)
            at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:311)
            at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:112)
            at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:746)
            at org.springframework.context.support.ApplicationContextAccessor.prepareContext(ApplicationContextAccessor.java:23)
            at org.springframework.context.annotation.BuildTimeBeanDefinitionsRegistrar.processBeanDefinitions(BuildTimeBeanDefinitionsRegistrar.java:66)
            at org.springframework.aot.context.bootstrap.generator.ApplicationContextAotProcessor.process(ApplicationContextAotProcessor.java:94)
            at org.springframework.aot.build.ContextBootstrapContributor.contribute(ContextBootstrapContributor.java:80)
            at org.springframework.aot.build.BootstrapCodeGenerator.generate(BootstrapCodeGenerator.java:91)
            at org.springframework.aot.build.BootstrapCodeGenerator.generate(BootstrapCodeGenerator.java:71)
            at org.springframework.aot.build.GenerateBootstrapCommand.call(GenerateBootstrapCommand.java:107)
            at org.springframework.aot.build.GenerateBootstrapCommand.call(GenerateBootstrapCommand.java:42)
            at picocli.CommandLine.executeUserObject(CommandLine.java:1953)
            at picocli.CommandLine.access$1300(CommandLine.java:145)
            at picocli.CommandLine$RunLast.executeUserObjectOfLastSubcommandWithSameParent(CommandLine.java:2352)
            at picocli.CommandLine$RunLast.handle(CommandLine.java:2346)
            at picocli.CommandLine$RunLast.handle(CommandLine.java:2311)
            at picocli.CommandLine$AbstractParseResultHandler.execute(CommandLine.java:2179)
            at picocli.CommandLine.execute(CommandLine.java:2078)
            at org.springframework.aot.build.GenerateBootstrapCommand.main(GenerateBootstrapCommand.java:112)
    Caused by: java.lang.IllegalStateException: Error processing condition on io.sentry.spring.boot.SentryAutoConfiguration
            at org.springframework.boot.autoconfigure.condition.SpringBootCondition.matches(SpringBootCondition.java:60)
            at org.springframework.context.annotation.ConditionEvaluator.evaluate(ConditionEvaluator.java:120)
            at org.springframework.context.annotation.ConditionEvaluator.shouldSkip(ConditionEvaluator.java:106)
            at org.springframework.context.annotation.ConditionEvaluator.shouldSkip(ConditionEvaluator.java:88)
            at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:226)
            at org.springframework.context.annotation.ConfigurationClassParser.processImports(ConfigurationClassParser.java:600)
            ... 26 more
    Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'SENTRY_DSN' in value "${SENTRY_DSN}"
            at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:180)
            at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:126)
            at org.springframework.core.env.AbstractPropertyResolver.doResolvePlaceholders(AbstractPropertyResolver.java:239)
            at org.springframework.core.env.AbstractPropertyResolver.resolveRequiredPlaceholders(AbstractPropertyResolver.java:210)
            at org.springframework.core.env.AbstractPropertyResolver.resolveNestedPlaceholders(AbstractPropertyResolver.java:230)
            at org.springframework.boot.context.properties.source.ConfigurationPropertySourcesPropertyResolver.getProperty(ConfigurationPropertySourcesPropertyResolver.java:79)
            at org.springframework.boot.context.properties.source.ConfigurationPropertySourcesPropertyResolver.getProperty(ConfigurationPropertySourcesPropertyResolver.java:60)
            at org.springframework.core.env.AbstractEnvironment.getProperty(AbstractEnvironment.java:594)
            at org.springframework.boot.autoconfigure.condition.OnPropertyCondition$Spec.collectProperties(OnPropertyCondition.java:140)
            at org.springframework.boot.autoconfigure.condition.OnPropertyCondition$Spec.access$000(OnPropertyCondition.java:105)
            at org.springframework.boot.autoconfigure.condition.OnPropertyCondition.determineOutcome(OnPropertyCondition.java:91)
            at org.springframework.boot.autoconfigure.condition.OnPropertyCondition.getMatchOutcome(OnPropertyCondition.java:55)
            at org.springframework.boot.autoconfigure.condition.SpringBootCondition.matches(SpringBootCondition.java:47)
            ... 31 more
    
    > Task :generateAot FAILED
    
    FAILURE: Build failed with an exception.

【问题讨论】:

    标签: java spring-boot gradle spring-native


    【解决方案1】:

    您似乎需要在 application.yml 中提供一个虚拟值并在运行时传递正确的环境变量。

    虚拟变量:

    server:
       port: 8080
    

    如果您使用的是 docker,您可以在运行时传递它,例如:

    docker-compose.yml

    version: '3.8'
    services:
      demo:
        image: demo
        build:
          context: ./
        container_name: demo
        environment:
          - server.port=8080
    

    您也可以使用本地 .env 文件并像这样传递它:

    .env

    PORT=8080
    

    docker-compose.yml

    version: '3.8'
    services:
      demo:
        image: demo
        build:
          context: ./
        container_name: demo
        environment:
          - server.port=${PORT}
    

    对于像server.port 这样的可选属性,您不需要在application.yml 文件中创建一个虚拟对象。你可以在运行时设置它,它会工作。

    我不关心您的 sentry.dsn 属性,也许您需要创建一个虚拟对象才能使其工作并在运行时覆盖它,就像上面的示例一样。

    更新:

    另一种方法是在构建映像的系统(docker 或本地)上设置$PORT 环境变量。也许这也可以。非常感谢您的反馈。

    dockerfile 中,您可以使用ENV 语句设置环境变量,例如ENV NAME=VALUE。在 Linux 上你可以运行 export NAME=VALUE

    来源:https://github.com/spring-projects-experimental/spring-native/issues/1470

    更新 2:

    我用 Spring Native 结合环境变量做了一些测试。我更喜欢的建议是在构建原生映像之前删除application.yml,并在运行时传递所有必需的变量。

    为了使您的构建成功,您可能需要禁用以下测试: mvn -Pnative package -DskipTests=true

    您可以查看我的示例存储库: https://github.com/swissbuechi/spring-native-demo

    【讨论】:

      猜你喜欢
      • 2021-09-15
      • 2020-01-05
      • 1970-01-01
      • 1970-01-01
      • 2016-12-29
      • 2018-03-24
      • 2019-02-10
      • 2017-05-16
      • 2018-07-03
      相关资源
      最近更新 更多