Spring Boot의 application.properties에서 env 변수 사용


200

우리는 스프링 부트 웹앱을 개발 하고 있으며 우리가 사용하는 데이터베이스는 MySql이다 ;

  • 우리가 가지고있는 설정은 먼저 로컬에서 테스트하는 것입니다 (PC에 MySql을 설치해야 함을 의미합니다).

  • 그런 다음 Bitbucket으로 푸시합니다 .

  • Jenkins 는 Bitbucket에 대한 새로운 푸시를 자동으로 감지하고 빌드를 수행합니다 (Jenkins mvn 빌드가 통과하려면 Jenkins를 실행하는 가상 머신에 MySql도 설치해야 함).

  • Jenkins 빌드가 패스되면 OpenShift 의 응용 프로그램으로 코드를 푸시합니다 (Jenkins의 Openshift 배포 플러그인 사용).

우리가 이미 이해했을 수도있는 문제 는 다음과 같습니다.

  • 에서와 application.properties우리가하지 하드 코드 MySQL의 정보를 할 수 있습니다. 우리 프로젝트는 3 개의 다른 장소 ( local , JenkinsOpenShift ) 에서 실행 되므로 데이터 소스 필드를 동적으로 만들어야합니다 application.properties(우리는 다른 방법이 있지만 현재는이 솔루션을 연구하고 있습니다).

    spring.datasource.url = 
    spring.datasource.username = 
    spring.datasource.password = 

우리가 생각 해낸 해결책은 시스템 환경 변수를 로컬 및 Jenkins vm에서 작성하고 (OpenShift에서 이름을 지정하는 것과 동일한 이름으로) 각각 올바른 값을 지정하는 것입니다.

export OPENSHIFT_MYSQL_DB_HOST="jdbc:mysql://localhost"
export OPENSHIFT_MYSQL_DB_PORT="3306"
export OPENSHIFT_MYSQL_DB_USERNAME="root"
export OPENSHIFT_MYSQL_DB_PASSWORD="123asd"

우리는 이것을했고 작동합니다. 또한 Map<String, String> env = System.getenv();환경 변수를 다음과 같이 Java 변수로 만들 수 있는지 확인했습니다 .

String password = env.get("OPENSHIFT_MYSQL_DB_PASSWORD");   
String userName = env.get("OPENSHIFT_MYSQL_DB_USERNAME");   
String sqlURL = env.get("OPENSHIFT_MYSQL_DB_HOST"); 
String sqlPort = env.get("OPENSHIFT_MYSQL_DB_PORT");

이제 남은 것은 Java 변수를 사용해야 application.properties한다는 것입니다. 이것이 바로 우리가 어려움을 겪고있는 것입니다.

어떤 폴더에서, 어떻게, 우리가 할당해야 할 password, userName, sqlURL,과 sqlPort에 대한 변수 application.properties를보고 우리가 어떻게에 포함 할 수 있도록를 application.properties?

우리는 많은 것들 중 하나를 시도했습니다.

spring.datasource.url = ${sqlURL}:${sqlPort}/"nameofDB"
spring.datasource.username = ${userName}
spring.datasource.password = ${password}

지금까지 운이 없습니다. 우리는 아마도 이러한 env 변수를 올바른 클래스 / 폴더에 넣지 않았거나에서 잘못 사용하고 있습니다 application.properties.

도와 주셔서 감사합니다 !!

감사!


3
자세한 내용은 @ConfigurationProperties 를 읽으십시오 . 그러나 이것은 프로파일 특정 구성 특성에
Eddie B

답변:


270

Java 변수를 사용할 필요가 없습니다. 시스템 환경 변수를 포함 시키려면 application.properties파일에 다음을 추가 하십시오.

spring.datasource.url = ${OPENSHIFT_MYSQL_DB_HOST}:${OPENSHIFT_MYSQL_DB_PORT}/"nameofDB"
spring.datasource.username = ${OPENSHIFT_MYSQL_DB_USERNAME}
spring.datasource.password = ${OPENSHIFT_MYSQL_DB_PASSWORD}

그러나 @Stefan Isele 이 제안한 방식 이 더 바람직합니다.이 경우 env 변수를 하나만 선언해야하기 때문입니다 spring.profiles.active. Spring은 application-{profile-name}.properties템플릿에 의해 적절한 속성 파일을 자동으로 읽습니다 .


12
이 방법은 도커 연결에 더 편리합니다. 예를 들면 :docker run --name my-tomcat -p 127.0.0.1:8080:8080 -e APP_DB_DB=mydb -e APP_DB_USER=dbuser -e APP_DB_PASS=dbpass --link mongo-myapp:mongo -v /path-to/tomcat/webapps:/usr/local/tomcat/webapps -d tomcat:8-jre8-alpine
Fırat KÜÇÜK

17
이것이 가장 좋은 방법입니다. 환경 변수를 사용하면 애플리케이션과 함께 비밀을 일반 텍스트로 나열 할 필요가 없습니다. 이는 훨씬 더 안전하고 소스 코드 액세스 보안 조치에 대한 종속성을 줄여 전체 자산을 보호합니다. 속성이 포함 된 실수로 게시 된 게시물은 정보가 유출되지 않습니다.
kipper_t

51
스프링 부트를 사용하는 경우 (부팅없이 작동하는지 확인하지 않은 경우) application.properties를 수정하지 않고 환경 변수를 통해 자동으로 속성을 재정의 할 수 있다고 언급하고 싶습니다. 즉,라는 속성이 있으면 spring.activemq.broker-url해당 환경 변수는 다음과 같습니다 SPRING_ACTIVEMQ_BROKER_URL. 마침표와 대시는 자동으로 밑줄로 변환됩니다. 컨테이너 / 스프링 부트 작업시 매우 편리합니다.
abe

15
클라우드 용으로 디자인하는 경우 스프링 프로파일을 사용하는 것이 바람직하지 않습니다. 12 요인 앱 표준에서는 환경 변수 사용을 권장합니다. 12factor.net/config
Mikhail Golubtsov

6
나는이 주제가 약간 오래되었다는 것을 안다. 그러나 환경 변수 설정과 스프링 프로파일 설정을 모두 결합 할 수 있습니다. 개발 프로파일에는 정적 정보가 있어야하지만 프로덕션 프로파일은 환경 변수를 사용할 수 있습니다. 이런 식으로 개발자는 개발 프로필을 배포하려는 경우 더 이상 시스템에서 환경 변수를 정의 할 필요가 없습니다.
underscore_05

72

환경에 따라 구성을 다르게하는 가장 쉬운 방법은 스프링 프로파일을 사용하는 것입니다. 외부화 된 구성을 참조하십시오 .

이것은 당신에게 많은 유연성을 제공합니다. 내 프로젝트에서 사용하고 있으며 매우 도움이됩니다. 귀하의 경우 3 개의 프로파일이 있습니다 : 'local', 'jenkins'및 'openshift'

그런 다음 3 프로필 특정 속성 파일이 : application-local.properties, application-jenkins.properties, 및application-openshift.properties

관련 환경에 대한 속성을 설정할 수 있습니다. 앱을 실행할 때 다음과 같이 활성화 할 프로파일을 지정해야합니다. -Dspring.profiles.active=jenkins

편집하다

스프링 문서에 따르면 시스템 환경 변수를 설정할 수 있습니다 SPRING_PROFILES_ACTIVE 를 프로파일을 활성화하고 매개 변수로 전달할 필요가 없습니다.

런타임에 웹 앱에 대한 활성 프로파일 옵션을 전달하는 방법이 있습니까?

스프링은 애플리케이션 컨텍스트를 구축 할 때 활성 프로파일을 첫 번째 단계 중 하나로 결정합니다. 그런 다음 활성 프로파일을 사용하여 읽을 특성 파일과 인스턴스화 할 Bean을 결정합니다. 응용 프로그램이 시작되면 변경할 수 없습니다.


4
이 답변이 마음에 들지만 프로필 이름을 환경에서 가져 오려면 어떻게해야합니까? 나는 -Dspring.active.profiles = $ SPRING_ACTIVE_PROFILES 시도하고 /etc/profile.d/myenvvars.sh에서 OS의 ENV var에 설정하지만, 봄 부팅은 선택하지 않는 한
톰 하트웰

1

5
이 답변 덕분에 Stefan, 그것은 나를 위해 일했지만 한 번의 변경으로-속성은 실제로 spring.active.profiles가 아니라 spring.profiles.active입니다
Rudi

11
Spring 프로파일은 OP와 관련하여 매우 유용 할 수 있지만 적합하지 않습니다. 이는 소스 코드가 저장되는 방식과 함께 저장된 속성 정보의 민감도 때문입니다. OP 컨텍스트는 데이터베이스 액세스에 관한 것입니다. 그러한 상황에서는 소스로 일반 텍스트로 제품 세부 정보를 원하지 않습니다. 즉, 소스가 손상되면 데이터베이스도 손상됩니다. Vault와 같은 환경 변수 나 비밀 도구를 사용하는 것이 좋습니다. 나는 env를 선호합니다. 또한 일관성과 관련하여 모든 환경이 동일한 방식으로 작동하도록합니다. 미래의 사고를 피합니다.
kipper_t

2
애플리케이션 JAR 외부에서 스프링 부트 프로파일 특성 파일을 사용할 수 있습니다. 예를 들어이 환경 별 파일 application-production.properties은 안전한 방식으로 프로덕션 시스템에 배치되며 일반적으로 애플리케이션 소스 코드 저장소에 없습니다.
콜린 D 베넷

13

이것은 저의 평판이 직접 언급 할만큼 높지 않기 때문에 많은 의견에 대한 답변입니다.

응용 프로그램 컨텍스트가 아직로드되지 않은 한 런타임시 프로파일을 지정할 수 있습니다.

// Previous answers incorrectly used "spring.active.profiles" instead of
// "spring.profiles.active" (as noted in the comments).
// Use AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME to avoid this mistake.

System.setProperty(AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME, environment);
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/META-INF/spring/applicationContext.xml");

12

으로 직접 환경 변수를 인식하지 못하는 Flayway application.properties (Spring-Boot V2.1)에 . 예 :

spring.datasource.url=jdbc:mysql://${DB_HOSTNAME}:${DB_PORT}/${DB_DATABASE}
spring.datasource.username=${DB_USER}
spring.datasource.password=${DB_PASS}

이 문제를 해결하기 위해이 환경 변수를 수행했습니다. 보통 .env 파일을 만듭니다.

SPRING_DATASOURCE_URL=jdbc:mysql://127.0.0.1:3306/place
SPRING_DATASOURCE_USERNAME=root
SPRING_DATASOURCE_PASSWORD=root

그리고 변수를 내 환경으로 내보내십시오.

export $(cat .env | xargs)

마지막으로 명령을 실행하십시오.

mvn spring-boot:run

또는 jar 파일을 실행하십시오.

java -jar target/your-file.jar

여기에 다른 접근 방식이 있습니다 : https://docs.spring.io/spring-boot/docs/2.1.0.BUILD-SNAPSHOT/maven-plugin/examples/run-env-variables.html


1
env-vars 란 무엇입니까? 그들은 어떻게 사용됩니까? 귀하의 답변은 완전한 설명이없는 것을 나타내며 귀하는 링크를 포함하지 않습니다. 나는 이것을 거의 downvoted했지만, 당신의 담당자는 21이므로 당신은 새로운 사람이고 한 사람은 당신의 대답이 유용하다고 생각합니다. 나는 당신이 나만큼 즐길 수 있기를 바랍니다.
PatS

2
@PatS에게 감사드립니다. 자세한 내용을 추가했습니다. 도움이 되길 바랍니다.
펠리페 지로 티

1
훌륭한 변화. 답변을 업데이트 해 주셔서 감사합니다.
PatS

9

다음 은 환경 체인을 통한 스 니펫 코드이며 다른 환경에 대해 특성 파일을로드하는 중입니다.

응용 프로그램 리소스 아래의 속성 파일 ( src / main / resources ) :-

 1. application.properties
 2. application-dev.properties
 3. application-uat.properties
 4. application-prod.properties

이상적으로 application.properties 에는 모든 환경에서 액세스 할 수있는 모든 공통 특성이 포함되며 환경 관련 특성은 환경을 지정하는 경우에만 작동합니다. 따라서 이러한 속성 파일을로드하는 순서는 다음과 같습니다.

 application.properties -> application.{spring.profiles.active}.properties.

여기에 코드 스 니펫 :-

    import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
    import org.springframework.core.io.ClassPathResource;
    import org.springframework.core.io.Resource;

    public class PropertiesUtils {

        public static final String SPRING_PROFILES_ACTIVE = "spring.profiles.active";

        public static void initProperties() {
            String activeProfile = System.getProperty(SPRING_PROFILES_ACTIVE);
            if (activeProfile == null) {
                activeProfile = "dev";
            }
            PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer
                    = new PropertySourcesPlaceholderConfigurer();
            Resource[] resources = new ClassPathResource[]
                    {new ClassPathResource("application.properties"),
                            new ClassPathResource("application-" + activeProfile + ".properties")};
            propertySourcesPlaceholderConfigurer.setLocations(resources);

        }
    }

2
Spring Boot는이 시나리오를 즉시 처리하지 않습니까? 참조 여기에 외부 구성 설명서를
닭발

4

어쩌면 이것을 너무 늦게 쓸 수도 있지만 속성을 읽는 방법을 재정의하려고 할 때 비슷한 문제가 발생했습니다.

내 문제는 : 1)이 속성이 env에 설정된 경우 env에서 속성을 읽습니다 .2)이 속성이 시스템 속성에 설정된 경우 시스템 속성에서 속성을 읽습니다 .3) 마지막으로 응용 프로그램 속성에서 읽습니다.

따라서이 문제를 해결하기 위해 Bean 구성 클래스로 이동합니다.

@Validated
@Configuration
@ConfigurationProperties(prefix = ApplicationConfiguration.PREFIX)
@PropertySource(value = "${application.properties.path}", factory = PropertySourceFactoryCustom.class)
@Data // lombok
public class ApplicationConfiguration {

    static final String PREFIX = "application";

    @NotBlank
    private String keysPath;

    @NotBlank
    private String publicKeyName;

    @NotNull
    private Long tokenTimeout;

    private Boolean devMode;

    public void setKeysPath(String keysPath) {
        this.keysPath = StringUtils.cleanPath(keysPath);
    }
}

@PropertySource에서 팩토리를 덮어 씁니다. 그런 다음 속성을 읽기위한 자체 구현을 만들었습니다.

    public class PropertySourceFactoryCustom implements PropertySourceFactory {

        @Override
        public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
            return name != null ? new PropertySourceCustom(name, resource) : new PropertySourceCustom(resource);
        }


    }

그리고 PropertySourceCustom을 만들었습니다

public class PropertySourceCustom extends ResourcePropertySource {


    public LifeSourcePropertySource(String name, EncodedResource resource) throws IOException {
        super(name, resource);
    }

    public LifeSourcePropertySource(EncodedResource resource) throws IOException {
        super(resource);
    }

    public LifeSourcePropertySource(String name, Resource resource) throws IOException {
        super(name, resource);
    }

    public LifeSourcePropertySource(Resource resource) throws IOException {
        super(resource);
    }

    public LifeSourcePropertySource(String name, String location, ClassLoader classLoader) throws IOException {
        super(name, location, classLoader);
    }

    public LifeSourcePropertySource(String location, ClassLoader classLoader) throws IOException {
        super(location, classLoader);
    }

    public LifeSourcePropertySource(String name, String location) throws IOException {
        super(name, location);
    }

    public LifeSourcePropertySource(String location) throws IOException {
        super(location);
    }

    @Override
    public Object getProperty(String name) {

        if (StringUtils.isNotBlank(System.getenv(name)))
            return System.getenv(name);

        if (StringUtils.isNotBlank(System.getProperty(name)))
            return System.getProperty(name);

        return super.getProperty(name);
    }
}

그래서 이것은 나를 도왔습니다.


4

Spring context 5.0을 사용하여 다음 주석을 통해 시스템 환경에 따라 올바른 속성 파일을 성공적으로로드했습니다.

@PropertySources({
    @PropertySource("classpath:application.properties"),
    @PropertySource("classpath:application-${MYENV:test}.properties")})

여기서 MYENV 값은 시스템 환경에서 읽히고 시스템 환경이 존재하지 않으면 기본 테스트 환경 속성 파일이로드됩니다. 잘못된 MYENV 값을 지정하면 응용 프로그램을 시작하지 못합니다.

참고 : 각 프로파일에 대해 유지 관리하고 싶습니다-application- [profile] .property 파일을 만들어야하며 Spring 부트가 아닌 Spring context 5.0을 사용했지만 스프링 4.1에서도 작동한다고 생각합니다.


3

나는 질문의 저자와 같은 문제에 직면했다. 우리 팀의 각 구성원이 서로 다른 로컬 환경 .gitignore을 가지고 있었고 다른 db 연결 문자열과 자격 증명을 가진 파일이 필요했기 때문에이 질문에 대한 답변으로는 충분 하지 않았으므로 사람들은 공통 파일을 커밋하지 않습니다. 실수로 다른 사람의 DB 연결을 끊습니다.

또한 아래 절차를 따를 때 다양한 환경에 쉽게 배포 할 수 있었으며 추가 보너스로 버전 관리에 민감한 정보가 전혀 필요하지 않았습니다. .

parameters.yml(.gitignored)와 parameters.yml.dist(첫 번째를 만드는 샘플 인 ) PHP Symfony 3 프레임 워크에서 아이디어 얻기composer install ),

아래 답변의 지식을 https://stackoverflow.com/a/35534970/986160https://stackoverflow.com/a/35535138/986160 결합하여 다음을 수행했습니다 .

본질적으로 이것은 스프링 구성의 상속을 자유롭게 사용할 수 있습니다. 하고 다음과 같이 최상위 및 추가 민감한 자격 증명의 구성을 통해 활성 프로파일을 선택할 수 있습니다.

application.yml.dist (샘플)

    spring:
      profiles:
        active: local/dev/prod
      datasource:
        username:
        password:
        url: jdbc:mysql://localhost:3306/db?useSSL=false&useLegacyDatetimeCode=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8

application.yml (개발자 서버의 .gitignore-d)

spring:
  profiles:
    active: dev
  datasource:
    username: root
    password: verysecretpassword
    url: jdbc:mysql://localhost:3306/real_db?useSSL=false&useLegacyDatetimeCode=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8

application.yml (로컬 시스템의 .gitignore-d)

spring:
  profiles:
    active: dev
  datasource:
    username: root
    password: rootroot
    url: jdbc:mysql://localhost:3306/xampp_db?useSSL=false&useLegacyDatetimeCode=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8

application-dev.yml (추가 환경 별 속성이 중요하지 않음)

spring:
  datasource:
    testWhileIdle: true
    validationQuery: SELECT 1
  jpa:
    show-sql: true
    format-sql: true
    hibernate:
      ddl-auto: create-droop
      naming-strategy: org.hibernate.cfg.ImprovedNamingStrategy
    properties:
      hibernate:
        dialect: org.hibernate.dialect.MySQL57InnoDBDialect

.properties로 동일하게 수행 가능


0

특성 파일이 환경 변수로 외부화되는 경우 실행 구성 다음에 IDE에 추가 ​​할 수 있습니다.

--spring.config.additional-location={PATH_OF_EXTERNAL_PROP}

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.