Docker Compose JVM parameters - java

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.

Related

How to call java main class from inside docker image using argument parameter

I have one apps which contains 10 java main class . Each main class has own responsibility.
I want to call each class in docker using class name parameter.
My Docker Image like below.
FROM azul/zulu-openjdk-alpine:11
ADD xyzproject/target/abc.jar abc.jar
CMD ["java", "-cp", "abc.jar" ,"Class1"]
I want to run multiple class using docker run argument parameter
Please suggest and help me to resolved
You can supply an alternate CMD when you launch the container:
docker run ... xyzproject/abc \
java -cp abc.jar Class2
The Compose command: and Kubernetes Pod args: similarly replace the CMD part.
If the java command is too involved, you can write a wrapper script to launch each embedded application:
#!/bin/sh
# run_class3.sh
exec java -cp abc.jar Class3 "$#"
docker run ... xyzproject/abc \
./run_class3.sh

Docker stack - wait for it

I would like to start two services in a stack.
Mysql
Spring boot app
The main problem is that spring boot starts before database (or starts when connection to database is not allowed). Then in logs I could see: java.net.UnknownHostException: database.
We could use startup order:
https://docs.docker.com/compose/startup-order/
So what I do? I copy wait-for-it.sh to file with docker-compose, add line
command: ["./wait-for-it.sh", "database:3306", "--", "java -Dspring.profiles.active=prod -jar app.jar"]
The result is:
java.lang.IllegalArgumentException: Invalid argument syntax: --
My entrypoint in backend Dockerfile:
ENTRYPOINT ["java","-Dspring.profiles.active=prod", "-jar","app.jar"]
How to make that spring boot app will wait for MySQL database under docker stack?
When you run the container, the ENTRYPOINT and CMD are combined. In your example you've set ENTRYPOINT to run the Java process, but then override CMD in the docker-compose.yml: instead of actually running the wait-for-it.sh script, it just gets passed as extra parameters to the JVM.
A typical pattern for using both of these together is to have ENTRYPOINT be some sort of wrapper that does first-time setup, then takes CMD as additional parameters. For this to work CMD needs to be a complete shell command. Change the Dockerfile to look like:
COPY wait-for-it.sh entrypoint.sh .
# ENTRYPOINT _must_ be in JSON-array form
ENTRYPOINT ["./entrypoint.sh"]
# CMD may be either string or JSON-array form
# (This is exactly what you originally had as ENTRYPOINT)
CMD ["java", "-Dspring.profiles.active=prod", "-jar", "app.jar"]
The entrypoint script can be very simple:
#!/bin/sh
# Wait for the database to be up
if [ -n "$MYSQL_HOST" ]; then
./wait-for-it.sh "$MYSQL_HOST:3306"
fi
# Run the CMD
exec "$#"
The important detail here is that I've configured the database host to be passed as an environment variable. This requires a shell to run to expand it, which is tricky to do in the JSON-array ENTRYPOINT syntax, so I've moved it into a separate script.
Finally, in the docker-compose.yml, do not override command: (or entrypoint:), but do make sure to set the environment variable for the script to be able to find the database.
version: '3.8'
services:
database: { ... }
application:
environment:
MYSQL_HOST: database
depends_on:
- database
# no command: override
The wrapper here will run whenever the container starts up, so if you docker-compose run application bash to get an interactive shell based on the image, it will still wait for the database to be up.
If you control both the Dockerfile and the docker-compose.yml, you shouldn't usually need to override command: in the Compose settings. I find the entrypoint-wrapper pattern useful enough that I generally default to using CMD in my Dockerfiles (there is no requirement to have an ENTRYPOINT).

Docker file ENV variable substitution

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

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"]

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.

Categories

Resources