Docker file ENV variable substitution - java

I have a case where docker image/container for same java spring boot app is built on separate servers (DEV server, QA server, PROD server). However, in my Dockerfile, I have a ENTRYPOINT line that looks as follows:
ENTRYPOINT ["java","-jar","-DskipTests","-Dspring.profiles.active=development","some.jar",">some.log","2>someerror.log"]
But I need a way to pass on the correct "-Dspring.profiles.active=development" for when I am in QA or PROD.
I noticed that if I issue this docker command on the server (QA for example):
docker exec <container id> env | grep V
then I can clearly see that there is an environment variable defined as:
ZZ_ENVIRONMENT=QA
Seeing this, I am looking for a way such that in the Dockerfile, maybe I can use this info such that if:
if ZZ_ENVIRONMENT=QA then
ENTRYPOINT ["java","-jar","-DskipTests","-Dspring.profiles.active=qa","some.jar",">some.log","2>someerror.log"]
if ZZ_ENVIRONMENT=PROD then
ENTRYPOINT ["java","-jar","-DskipTests", Dspring.profiles.active=production","some.jar",">some.log","2>someerror.log"]
else
ENTRYPOINT ["java","-jar","-DskipTests","-Dspring.profiles.active=development","some.jar",">some.log","2>someerror.log"]
Is this possible? and if so I would appreciate example that I can follow. If this is the wrong way to do this, please advise the best way to do this. Please note: I do not have sudo access to the servers, just user access.

You can pass the environment variables while starting the docker container in the docker run command.
For example
docker run --env ZZ_ENVIRONMENT=PROD --env ENV_VAR2=value2 <docker_image_name>
If you have a list of environment variables then you can simply create a .env file and add all the varibales into it and in the docker run command can refer the env file as below
docker run --env-file .env <docker_image_name>
where your .env file counld be as
ZZ_ENVIRONMENT=PROD
ENV_VAR2=value2

The best way to go about this would be instead of this logic in DockerFile, you could use the SPRING_PROFILES_ACTIVE variable directly or in your enviornment file.
Docker File
ENTRYPOINT ["java","-jar","-DskipTests""some.jar",">some.log","2>someerror.log"]
Docker Run
docker run -e "SPRING_PROFILES_ACTIVE=qa" -p 8080:8080 -t test-image:v1
docker run -e "SPRING_PROFILES_ACTIVE=production" -p 8080:8080 -t test-image:v1
Alternativetly, you could explore docker compose like below (more maintainable approach)
docker-compose.yml
version: '3'
services:
spring-boot-service:
image: test-image:v1
env_file:
- spring-boot-service.env
ports:
- 8080:8080
spring-boot-service.env
SPRING_PROFILES_ACTIVE=production

Related

How to pass Java options/variables to Springboot app in docker run command

I have a Spring Boot application which uses profiles to configure in different environments. I want to pass this profile information as a parameter to my docker run command. How do I go about doing it?
Here is my dockerfile
FROM openjdk:8-jdk-alpine
ARG JAR_FILE=target/demo-app-1.0-SNAPSHOT.jar
COPY ${JAR_FILE} /opt/lib/demo-app.jar
EXPOSE 80
# ENTRYPOINT ["java","-Dspring.profiles.active=dockerdev","-jar","/opt/lib/demo-app.jar"]
# Above line works, but not desired as profile will change in diff envs
ENTRYPOINT ["java","-jar","/opt/lib/demo-app.jar"]
I have tried the following, but, none works
docker run -p 8000:80 demo-app -Dspring.profiles.active=dockerdev
docker run -p 8000:80 demo-app --rm -e JAVA_OPTS='-Dspring.profiles.active=dockerdev'
Please help.
Clarification: I am using multiple profiles. Hence I do not want the active profile to be mentioned within the application or the docker file. Instead, I want to use the same application and docker file and run it in different environments, and pass the active profile to be used in the docker run command. Apologies if anything above did not clarify that.
Solution 1
You can override any property from your configuration by passing it to docker container using -e option. As explained in Externalized configuration the environment variable name should be uppercased and splitted using underscore. So for example to pass spring.profiles.active property you could use SPRING_PROFILES_ACTIVE environment variable during container run :
docker run -p 8000:80 -e SPRING_PROFILES_ACTIVE=dockerdev demo-app
And this variable should be picked automatically by Spring from environment.
Solution 2
Change Dockerfile to :
FROM openjdk:8-jdk-alpine
ARG JAR_FILE=target/demo-app-1.0-SNAPSHOT.jar
# environment variable with default value
ENV SPRING_PROFILE=dev
COPY ${JAR_FILE} /opt/lib/demo-app.jar
EXPOSE 80
#run with environment variable
ENTRYPOINT java -Dspring.profiles.active=$SPRING_PROFILE jar /opt/lib/demo-app.jar
and then run the container passing the environment variable :
docker run -p 8000:80 --rm -e SPRING_PROFILE=dockerdev demo-app
Make use of application.properties in springboot to override any variables from outside. We heavily use this in our production environments.
You need to:
Change your ENTRYPOINT to:
ENTRYPOINT ["java","-jar","/opt/lib/demo-app.jar","--spring.config.additional-location=/application.properties"]
Create application.properties file with contents:
spring.profiles.active=dockerdev
You can also override any variables used in your springboot code using application.properties and can also override springboot specific variables as mentioned here.
Also change your docker run command to:
docker run -itd -v /path/to/application.properties:/application.properties image-name
So that application.properties from your host will get mounted
inside your docker container.
NOTE: If --spring.config.additional-location don't works then try --spring.config.location option.
Hope this helps.
JAVA_TOOL_OPTIONS maybe the right answer.
docker run -p 8000:80 -e JAVA_TOOL_OPTIONS='-Dspring.profiles.active=dockerdev' demo-app
People who are looking for the answer for non Spring (Plain Java Applications)
This is how to send System properties/arguments: Change the Dockerfile to
FROM openjdk:8-jdk-alpine
COPY target/demo-app-1.0-SNAPSHOT.jar demo-app.jar
EXPOSE 8080
ENTRYPOINT java -jar demo-app.jar
Build the image:
docker build -t demo-app .
Then run the docker container using following command:
docker run -e "JAVA_TOOL_OPTIONS=-Xms1024m -Xmx2048m -Dspring.profiles.active=dockerdev" -p 8080:8080 demo-app
you have to provide the JAVA_OPTS inside the docker file, example of a docker file is below.
FROM {{ env "DOCKER_REGISTRY" }}/asdf/osX-jre8:{{ env "BASE_IMAGE_VERSION" }}
ADD target/yourapp.jar /app.jar
#Environment vars
ENV NO_PREFIX true
ENV APP_NAME "xxx"
ENV APP_UUID "81b35e09-2a10-48c3-a091-xxxxxxxxx"
ENV HEALTH_CHECK_URL http://localhost:9000/health
ENV SERVICE_PORT 8080
ENV JAVA_OPTS "-Dsun.net.client.defaultConnectTimeout=2000 -Dsun.net.client.defaultReadTimeout=20000 -XX:+PrintGC -XX:+PrintGCTimeStamps -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/alloc/logs"
You should add the env variable JAVA_OPTS to your Dockerfile
FROM tomcat:8.5.47-jdk8-openjdk
LABEL build_date="2020-07-14" \
name="Ousama EL IDRISSI" \
version="1.0" \
description="Docker Image made by la7ya"
EXPOSE 8080
COPY ./target/la7yaman-0.0.1-SNAPSHOT.war /usr/local/tomcat/webapps/ROOT.war
ENV JAVA_OPTS="-Dspring.profiles.active=docker-demo"
CMD ["catalina.sh", "run"]

how to access mysql docker container in spring boot docker container

i have 2 docker file
1. mysql-dockerfile
FROM mysql:5.5
EXPOSE 3306
ENV MYSQL_ROOT_PASSWORD root
ENV MYSQL_DATABASE ToDoList
command used to build dockerfiles are as below
sudo docker build -t mysql-img -f mysql-dockerfile .
sudo docker run -d --name mysqlcontainer -p 3030:3306 mysql-img
2. java-dockerfile
FROM openjdk:8-jre-alpine
EXPOSE 9090
WORKDIR /usr/src
COPY target/*.war todoApp.war
CMD ["java","-jar","todoApp.war"]
command used to build dockerfiles are as below
sudo docker build -t java-img -f java-dockerfile .
docker run --name javacontainer -d -p 4040:9090 java-img
spring boot application consist jdbc url as follow
spring.datasource.url=jdbc:mysql://localhost:3030/ToDoList
i am not able to start project because spring boot application in docker is not able to connect mysql db which is in another container.
one solution i found is to bring two docker container in one docker network or link docker container.
can anyone please suggest good solution, modified docker run command and modified jdbc url.
Put them into one network and use container names as hostnames:
docker network create foo
docker run --network=foo --name mysqlcontainer -d mysql-img
docker run --network=foo --name javacontainer -d java-img
Dont expose ports - they are exposed automatically between containers inside network.
To connect inside, use mysqlcontainer:3306 and javacontainer:9090.
To connect from host, you will need port exposing.

Install a custom package in a docker container that keeps exiting

I have a docker container which runs a springboot java application. Dockerfile:
# Create container with java preinstalled
FROM openjdk:8-jdk-alpine
# Create app directory
VOLUME /tmp
# Handle Arguments
ARG JAR_FILE
ARG ENV_NAME
ENV SPRING_PROFILES_ACTIVE=${ENV_NAME}
RUN echo ${ENV_NAME}
# Bundle app source
COPY ${JAR_FILE} app.jar
COPY application.yml application.yml
# Run the server
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-Dspring.config.location=application.yml","-jar","app.jar"]
Now, I have a custom library I need to install in that container. I'll need to copy the installation, extract it, run the install script and answer prompts (Y/n)
I understood the easiest way to do this is to connect to the container, install the package and commit the changes.
First - I start the container using:
docker run --name local-jdk8 -d openjdk:8-jdk-alpine
The next step is to copy the data and run the install script, but the container keeps on exiting since the run command is empty ("/bin/sh") which means I can't run
docker exec -it local-jdk8 bash
Any ideas on how I can modify such a container?
Solved it using expect library
My dockerfile :
# Create container with java preinstalled
FROM openjdk:8
# Create app directory
VOLUME /tmp
# Handle Arguments
ARG JAR_FILE
ARG ENV_NAME
ARG DRIVER_FILE
# Environment
ENV SPRING_PROFILES_ACTIVE=${ENV_NAME}
RUN echo ${ENV_NAME}
# Fingerprint Driver
RUN apt-get update -y
RUN apt-get install -y expect
COPY ${DRIVER_FILE} driver.tar.gz
COPY driver-install.exp driver-install.exp
RUN tar -xzf driver.tar.gz
RUN /driver-install.exp
# Copy app source
COPY ${JAR_FILE} app.jar
COPY application.yml application.yml
# Run the server
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-Dspring.config.location=application.yml","-jar","app.jar"]
driver-install.exp is the expect scripts that automatically interacts with the package installation
For what it's worth, here is a little trick that allows you to keep your container running to modify and commit it:
docker run --name local-jdk8 -d openjdk:8-jdk-alpine tail -f /dev/null
Furthermore, there is no bash installed on the container so sh will have to do:
docker exec -it local-jdk8 sh
Nevertheless, modifying Dockerfile is the better approach, since your change is persisted in code, rather than done on an potentially ephemeral container.

Docker container hostname

I'm trying to set docker container hostname (HOSTNAME env var) during startup, this is .sh script specified in ENTRYPOINT of dockerfile:
#!/bin/sh
export HOSTNAME=something-$(hostname)
java $JAVA_OPTS -jar /app.jar
I want this new hostname to be seen for jvm.
All I get is standard docker hostname like that:
/ # env
HOSTNAME=04dbf311a3be
When i set the hostname manually using this export above after the container is started everything works just fine. Everything is being run in swarm using compose stackfile.
EDIT1:
I am not doing this during container build but during startup
EDIT2:
To be clear, what i have:
DOCKER SWARM:
CONTAINER1
HOSTNAME=391fa2c7e184
CONTAINER2
HOSTNAME=39123a43242asd4
CONTAINER3
HOSTNAME=123123123123
what i want:
CONTAINER1
HOSTNAME=APPNAME-391fa2c7e184
CONTAINER2
HOSTNAME=APPNAME-123fa2c7e184
CONTAINER3
HOSTNAME=APPNAME-343fa345e184
And want this to be autmatically set during startup of n-containers
how do i try to achieve this:
I try to set this in start.sh file called on container startup (pointed in ENTRYPOINT command in dockerfile):
#!/bin/sh
export HOSTNAME=something-$(hostname)
java $JAVA_OPTS -jar /app.jar
there is no effect, the HOSTNAME is not being changed
I don't think that the entrypoint script is right place to name your container, as it is already created at that point. You have a couple possibilities. Name it at runtime like this.
~
$ docker run -it -h myContainer 3bee3060bfc8 /bin/bash
[root#myContainer /]# echo $HOSTNAME
myContainer
[root#myContainer /]#
The -h option let's you name your container when you run it.
That value should be valid in your ENTRYPOINT script.
Or, if you want to create your container names more dynamically, you should name them in a docker compose file. Even if you don't use the container_name option, docker-compose will append -1
version: '2'
services:
myService:
container_name: myService-$(envVariable)
I don't know swarm, but as it works with a yaml file, you should get similar naming options.

Docker Compose JVM parameters

I wrote a java application that takes an environment variable that takes an argument to set a key for a JWT token salt key. Is there a way for me to pass the command variables in Docker Compose?
java -Djava.security.egd=file:/dev/./urandom -jar /user-profile-api.jar --key=blah
And to run the docker image you just
docker run -p 8080:8080 docker_image --key=blah
If you already are able to run your docker container using:
docker run -p 8080:8080 docker_image --key=blah
Then you just need to override the command attribute for your service in the compose file to --key=blah. So:
services:
app:
command: --key=blah
...
One way would be to put your java command in a shell script (say, bootstrap.sh), and set that as your command to run in docker compose. And then in bootstrap.sh inject the key via an environment variable which is in your docker-compose.yml.
E.g.
bootstrap.sh
java -Djava.security.egd=file:/dev/./urandom -jar /user-profile-api.jar --key=$SALT_KEY
docker-compose.yml
build: .
environment:
- SALT_KEY=blah
command: /opt/app/bootstrap.sh
Obviously you'd need to package up bootstrap.sh into your container for this to work.

Categories

Resources