leanmind logo leanmind text logo

Blog

Código Sostenible

Cómo escribir código fácil de mantener mediante valores, principios y técnicas.

Argumentos en docker, cómo parametrizar contenededores

Por Adrián Ferrera González

El contexto en el que surgió escribir este artículo es el siguiente:

Tenemos una aplicación Java + Spring, la cual ya está dockerizada. Existen dos entornos: Desarrollo local y un entorno docker. Tenemos definido un application.properties dentro de nuestro proyecto, donde se definen múltiples variables, las cuales cambian dependiendo del entorno. Al cambiar de entorno, hay que cambiar manualmente el valor de esas variables.

Estado de application.properties actual

1
2
3
4
5
## Local environment
spring.datasource.url=jdbc:postgresql://localhost:5432/license

## Docker environment
#spring.datasource.url=jdbc:postgresql://database:5432/license

Problema

Para cambiar dicho entorno, es necesario estar modificando este fichero, corriendo el riesgo que se acabe llegando a producción con las propiedades incorrectas.

Solución

Para solucionar esta casuística, debemos saber que Spring no es más que una aplicación java, y que una vez se empaqueta la aplicación, esta se lanza con un comando java -jar [nombre de la librería].jar, pudiendo aceptar argumentos.

Por suerte, Spring reconoce dichos argumentos y les da prioridad frente a los del fichero application.properties, de tal forma que podríamos lanzar el siguiente comando:

$ java- jar api-0.0.1.jar --spring.datasource.url=jdbc:postgresql://database:5432/license

Si nos llevamos esta línea a un Dockerfile, podríamos indicar lanzar el comando CMD y que nuestro docker por defecto siempre lance la aplicación apuntando al contenedor de base de datos del docker de la siguiente forma:

1
2
3
4
5
6
7
FROM openjdk:12

EXPOSE 8080

## [...compilation steps...]

CMD java- jar api-0.0.1.jar --spring.datasource.url=jdbc:postgresql://database:5432/license

Refinando la solución

La opción anterior es completamente válida y resuelve nuestro problema, ya no tendremos que estar cambiando ficheros de configuración constantemente, corriendo el riesgo de que al cliente le llegue una versión inválida, pero podemos mejorarla un poco más.

De la misma forma que java admite argumentos, docker también lo hace. Esto puede permitir que nuestros clientes puedan decidir no conectar con nuestra base de datos dockerizada, sino con una que tengan desplegada en otra máquina. Spring se encarga de sobreescribir las variables, pero… ¿y nosotros?

Dentro de un Dockerfile, podemos definir argumentos usando la instrucción ARG. La forma más común es recibir un argumento y definirla como variable de entorno usando ENV.

ARG my_arg=default_value
ENV my_env ${my_arg}

Si extrapolamos esto a nuestro Dockerfile, podemos definir que se va a conectar por defecto a nuestra base de datos dockerizada, salvo que nos indiquen lo contrario por argumentos:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
FROM openjdk:12

EXPOSE 8080

## [...compilation steps...]

ARG datasource_url=jdbc:postgresql://database:5432/license
ENV DATASOURCE_URL ${datasource_url}

CMD java- jar api-0.0.1.jar --spring.datasource.url="${DATASOURCE_URL}"

Por lo tanto, cuando construyamos nuestro contenedor, solo tendremos que pasar dicha variable para sobreescribir el valor.

¿Vamos un paso más allá?

Si nuestro ecosistema se construye a través de un docker-compose.yml, únicamente tendremos que identificar el servicio en cuestión y hacer uso de la propiedad args, para darle un valor de la siguiente manera:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
api:
    build:
      context: .
      dockerfile: Dockerfile-api
      args:
        datasource_url: jdbc:postgresql://database:5432/license
    ports:
      - "8080:8080"
    restart: on-failure
    volumes:
      - ./.docker/certificates:/certificates

De esta forma, cumplimos con la premisa de tener el mismo entorno para producción, desarrollo y test, minimizando el riesgo de problemas por entorno.

Además, permite a los clientes modificar ciertas características de la aplicación, sin tener que llegar a bajo nivel, simplemente editando un valor en un contexto con el que ya está familiarizado.

Conclusión

Al final, no hemos hecho otra cosa que propagar una variable hacia capas más profundas, que, en caso de no indicarse en una de esas capas, tomará el que se entiende que es valor por defecto más sensato en ese ámbito:

Publicado el 06/03/2020 por
Adrián image

Adrián Ferrera González

https://adrianferrera.dev

¿Quieres más? te invitamos a suscribirte a nuestro boletín para avisarte cada vez que recopilemos contenido de calidad que compartir.

Si disfrutas leyendo nuestro blog, ¿imaginas lo divertido que sería trabajar con nosotros? ¿te gustaría?

Impulsamos el crecimiento profesional de tu equipo de developers