java - deploy - Usando la variable env en la aplicación Spring Boot.
openshift deploy java application (8)
Estamos trabajando en una aplicación web Spring Boot y la base de datos que estamos usando es MySql ;
-
la configuración que tenemos es que primero lo probamos localmente (significa que necesitamos instalar MySql en nuestra PC);
-
luego empujamos a Bitbucket ;
-
Jenkins detecta automáticamente el nuevo impulso a Bitbucket y lo construye (para que pase la compilación mvn de Jenkins también necesitamos instalar MySql en las máquinas virtuales que ejecutan Jenkins).
-
si la compilación de Jenkins pasa, enviamos el código a nuestra aplicación en OpenShift (usando el complemento de implementación Openshift en Jenkins).
El problema que tenemos, ya que quizás ya lo haya resuelto, es que:
-
en
application.properties
no podemos codificar la información de MySql. Dado que nuestro proyecto se ejecutará en 3 lugares diferentes ( local , Jenkins y OpenShift ), necesitamos que el campo del origen de datos sea dinámico enapplication.properties
(sabemos que hay diferentes formas de hacerlo, pero estamos trabajando en esta solución por ahora) .spring.datasource.url = spring.datasource.username = spring.datasource.password =
La solución que se nos ocurrió es que creamos variables de entorno del sistema localmente y en la máquina virtual de Jenkins (nombrándolas de la misma manera que OpenShift las nombra) y asignándoles los valores correctos, respectivamente:
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"
Hemos hecho esto y funciona.
También hemos verificado con
Map<String, String> env = System.getenv();
que las variables de entorno se pueden convertir en variables java como tales:
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");
Ahora lo único que queda es que necesitamos usar estas variables de Java en nuestra
application.properties
y eso es con lo que estamos teniendo problemas.
¿En qué carpeta y cómo debemos asignar las variables de
password
, nombre de
password
,
sqlURL
y
sqlPort
para
application.properties
para poder verlas y cómo las incluimos en
application.properties
?
Hemos probado muchas cosas, una de ellas es:
spring.datasource.url = ${sqlURL}:${sqlPort}/"nameofDB"
spring.datasource.username = ${userName}
spring.datasource.password = ${password}
Sin suerte hasta ahora.
Probablemente no estamos colocando estas variables env en la clase / carpeta correcta o las estamos usando incorrectamente en
application.properties
.
¡¡Tu ayuda es altamente apreciada!!
¡Gracias!
Esto es en respuesta a una serie de comentarios, ya que mi reputación no es lo suficientemente alta como para comentar directamente.
Puede especificar el perfil en tiempo de ejecución siempre que el contexto de la aplicación aún no se haya cargado.
// 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");
Flayway no reconoce las variables de entorno directas en application.properties (Spring-Boot V2.1). p.ej
spring.datasource.url=jdbc:mysql://${DB_HOSTNAME}:${DB_PORT}/${DB_DATABASE}
spring.datasource.username=${DB_USER}
spring.datasource.password=${DB_PASS}
Para resolver este problema, hice estas variables de entorno, generalmente creo el archivo .env:
SPRING_DATASOURCE_URL=jdbc:mysql://127.0.0.1:3306/place
SPRING_DATASOURCE_USERNAME=root
SPRING_DATASOURCE_PASSWORD=root
Y exportar las variables a mi entorno:
export $(cat .env | xargs)
Y finalmente solo ejecuta el comando
mvn spring-boot:run
O ejecuta tu archivo jar
java -jar target/your-file.jar
Hay otro enfoque aquí: https://docs.spring.io/spring-boot/docs/2.1.0.BUILD-SNAPSHOT/maven-plugin/examples/run-env-variables.html
La forma más fácil de tener diferentes configuraciones para diferentes entornos es usar perfiles de resorte. Ver @ConfigurationProperties .
Esto te da mucha flexibilidad. Lo estoy usando en mis proyectos y es extremadamente útil. En su caso, tendría 3 perfiles: ''local'', ''jenkins'' y ''openshift''
Luego tiene 3 archivos de propiedades específicos del perfil:
application-local.properties
,
application-jenkins.properties
y
application-openshift.properties
Allí puede establecer las propiedades para el entorno correspondiente.
Cuando ejecuta la aplicación, debe especificar el perfil para activarlo de esta manera:
-Dspring.profiles.active=jenkins
Editar
Según el documento de Spring, puede configurar la variable de entorno del sistema
SPRING_PROFILES_ACTIVE
para activar los perfiles y no es necesario pasarla como parámetro.
¿Hay alguna manera de pasar la opción de perfil activo para la aplicación web en tiempo de ejecución?
No. Spring determina los perfiles activos como uno de los primeros pasos, al construir el contexto de la aplicación. Los perfiles activos se utilizan para decidir qué archivos de propiedades se leen y qué beans se instancian. Una vez que se inicia la aplicación, esto no se puede cambiar.
Me enfrenté al mismo problema que el autor de la pregunta.
Para nuestro caso, las respuestas en esta pregunta no fueron suficientes ya que cada uno de los miembros de mi equipo tenía un entorno local diferente y definitivamente necesitábamos
.gitignore
el archivo que tenía las diferentes cadenas y credenciales de conexión db, por lo que la gente no compromete archivo común por error y romper las conexiones db de otros.
Además de eso, cuando seguimos el siguiente procedimiento, fue fácil de implementar en diferentes entornos y, como beneficio adicional , no necesitábamos tener ninguna información confidencial en el control de versiones .
Obteniendo la idea del framework PHP Symfony 3 que tiene un
parameters.yml
(.gitignored) y un
parameters.yml.dist
(que es una muestra que crea el primero a través de la
composer install
),
Hice lo siguiente combinando el conocimiento de las respuestas a continuación: https://.com/a/35534970/986160 y https://.com/a/35535138/986160 .
Esencialmente, esto le da la libertad de usar la herencia de las configuraciones de resorte y elegir los perfiles activos a través de la configuración en la parte superior más cualquier credencial extra sensible de la siguiente manera:
application.yml.dist (muestra)
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 en el servidor de desarrollo)
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 en la máquina local)
spring:
profiles:
active: local
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 (propiedades específicas del entorno adicionales no sensibles)
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
Lo mismo se puede hacer con .properties
No necesita usar variables java.
Para incluir variables de entorno del sistema, agregue lo siguiente a su archivo
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}
Pero la forma sugerida por
@Stefan Isele
es más preferible, porque en este caso debe declarar solo una variable env:
spring.profiles.active
.
Spring leerá automáticamente el archivo de propiedades apropiado por la plantilla
application-{profile-name}.properties
.
Tal vez escribo esto demasiado tarde, pero he tenido un problema similar cuando he tratado de anular los métodos para leer las propiedades.
Mi problema ha sido: 1) Leer la propiedad de env si esta propiedad se ha configurado en env 2) Leer la propiedad de la propiedad del sistema si esta propiedad se ha establecido en la propiedad del sistema 3) Y por último, leer de las propiedades de la aplicación.
Entonces, para resolver este problema, voy a mi clase de configuración de 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);
}
}
Y sobrescribir fábrica en @PropertySource. Y luego he creado mi propia implementación para leer propiedades.
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);
}
}
Y creó 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);
}
}
Entonces, esto me ha ayudado.
Usando Spring context 5.0, logré cargar correctamente el archivo de propiedades correcto basado en el entorno del sistema a través de la siguiente anotación
@PropertySources({
@PropertySource("classpath:application.properties"),
@PropertySource("classpath:application-${MYENV:test}.properties")})
Aquí, el valor MYENV se lee del entorno del sistema y, si el entorno del sistema no está presente, se cargará el archivo de propiedades del entorno de prueba predeterminado; si le doy un valor MYENV incorrecto, no se podrá iniciar la aplicación.
Nota: para cada perfil, desea mantener - necesitará hacer un archivo application- [profile] .property y aunque utilicé Spring context 5.0 y no Spring boot - creo que esto también funcionará en Spring 4.1
Here hay un código de fragmento a través de una cadena de archivos de propiedades de entornos que se están cargando para diferentes entornos.
Archivo de propiedades bajo los recursos de su aplicación ( src / main / resources ): -
1. application.properties
2. application-dev.properties
3. application-uat.properties
4. application-prod.properties
Idealmente, application.properties contiene todas las propiedades comunes que son accesibles para todos los entornos y las propiedades relacionadas con el entorno solo funcionan en entornos específicos. por lo tanto, el orden de carga de estos archivos de propiedades será tal:
application.properties -> application.{spring.profiles.active}.properties.
Fragmento de código aquí: -
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);
}
}