java vm options and AWS::ECS::TaskDefinition - java

I would like to run my docker container as following:
docker run java \
-XX:+UnlockExperimentalVMOptions \
-XX:+UseCGroupMemoryLimitForHeap
Where should I pass vm arguments when writing AWS::ECS::TaskDefinition in cloudformation stack?

I have done the same thing at work, you could pass the flags directly in Dockerfile as per below.
ENV JAVA_OPTS="-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap"
ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar" ]

Related

systemd for java process issue with heap dump

My application is written in java spring boot, to run the application on linux system i have written a systemd file as below.
[Unit]
Description=Service Module For microservice Service.
After=network.target auditd.service
[Install]
WantedBy=multi-user.target
[Service]
Type=idle
Environment=service_name=/home/ec2-user/test-1.0-SNAPSHOT.jar
Environment=env=dev
ExecStart=/usr/bin/java -jar ${service_name} \
--spring.profiles.active=${env} \
--Xmx=1300M \
-XX:+HeapDumpOnOutOfMemoryError \
-Xloggc:gc.log \
-XX:+PrintGCDetails \
-XX:+PrintGCTimeStamps \
-XX:+UseGCLogFileRotation \
-XX:NumberOfGCLogFiles=5 \
-XX:GCLogFileSize=10M
ExecStop=/bin/kill -s TERM $MAINPID
LimitNOFILE=30000
Restart=always
RestartSec=500
StartLimitBurst=3
StartLimitInterval=120
MemoryLimit=1300M
[Manager]
When this systemd file is started, i can see in process mentioned process is running as expected.
/usr/bin/java -jar /home/ec2-user/test-1.0-SNAPSHOT.jar --spring.profiles.active=dev --Xmx=1300M -XX:+HeapDumpOnOutOfMemoryError -Xloggc:gc.log -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=10M
Issue is when the application fails with "java.lang.OutOfMemoryError: Java heap space" errors in the logs and does not export heap dump and gc logs in the location specified. Has someone used jar with systemd and faced similar issues.
Maybe if you set WorkingDirectory=/home/ec2-user/ to the directory where you want your logs/dumps to appear. Because without that I'm not sure where systemd is trying to create the gc.log you specified.
Or alternativly you could try to specifiy the whole path to the gc.log: -Xloggc:/home/ec2-user/gc.log
Well i have found out that the problem was with the new line and \ characters, If we place all the jvm arguments in the single line it works!!!.
ex:
ExecStart=/usr/bin/java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/location/heapdump.bin -Xloggc:/location/gc.log -jar test.jar --Xms=512M --Xmx=1300M --spring.profiles.active=${env}
Note: Some of the arguments are deprecated in Java 11, so check document arguments to include as per use case.
https://docs.oracle.com/en/java/javase/11/tools/java.html#GUID-3B1CE181-CD30-4178-9602-230B800D4FAE

How to specify a list of JVM parameters for Dockerized SpringBoot REST jar execution

I have a simple REST application developed using SpringBoot and this application jar has been deployed in a docker container. The end goal is to test the latency of this application under different JVM flag value combinations. I need to know how I can specify a long list of JVM flag values that can be repeatedly changed?
I know that you can specify one or two flags like this:
Dockerfile
FROM openjdk:9
ADD target/java-container.jar /usr/src/myapp/
WORKDIR /usr/src/myapp
EXPOSE 8080
CMD java -XX:+PrintFlagsFinal $JAVA_OPTIONS -jar java-container.jar
and running the command:
$ docker run -d --name mycontainer8g -p 8080:8080 -m 800M -e JAVA_OPTIONS='-Xmx300m' rafabene/java-container:openjdk-env
using the JAVA_OPTIONS. But I have a very long list of JVM flags as shown below:
-XX:+UseSerialGC -XX:+ResizePLAB -XX:-ResizeOldPLAB -XX:-AlwaysPreTouch -XX:-ParallelRefProcEnabled -XX:+ParallelRefProcBalancingEnabled -XX:+UseTLAB -XX:-ResizeTLAB -XX:-ZeroTLAB -XX:-FastTLABRefill -XX:+NeverActAsServerClassMachine -XX:-AlwaysActAsServerClassMachine -XX:+UseAutoGCSelectPolicy -XX:+UseAdaptiveSizePolicy -XX:+UsePSAdaptiveSurvivorSizePolicy -XX:-UseAdaptiveGenerationSizePolicyAtMinorCollection -XX:+UseAdaptiveGenerationSizePolicyAtMajorCollection -XX:+UseAdaptiveSizePolicyWithSystemGC -XX:+UseAdaptiveGCBoundary -XX:+UseAdaptiveSizePolicyFootprintGoal -XX:-UseAdaptiveSizeDecayMajorGCCost -XX:+UseGCOverheadLimit -XX:+DisableExplicitGC -XX:-CollectGen0First -XX:+BindGCTaskThreadsToCPUs -XX:+UseGCTaskAffinity -XX:YoungPLABSize=3397 -XX:OldPLABSize=1123 -XX:GCTaskTimeStampEntries=240 -XX:TargetPLABWastePct=6 -XX:PLABWeight=75 -XX:OldPLABWeight=46 -XX:MarkStackSize=4617021 -XX:MarkStackSizeMax=713160576 -XX:RefDiscoveryPolicy=0 -XX:InitiatingHeapOccupancyPercent=48 -XX:MaxRAM=139765086242 -XX:ErgoHeapSizeLimit=0 -XX:MaxRAMFraction=4 -XX:DefaultMaxRAMFraction=4 -XX:MinRAMFraction=2 -XX:InitialRAMFraction=61 -XX:AutoGCSelectPauseMillis=5557 -XX:AdaptiveSizeThroughPutPolicy=0 -XX:AdaptiveSizePausePolicy=0 -XX:AdaptiveSizePolicyInitializingSteps=28 -XX:AdaptiveSizePolicyOutputInterval=0 -XX:AdaptiveSizePolicyWeight=12 -XX:AdaptiveTimeWeight=19 -XX:PausePadding=0 -XX:PromotedPadding=3 -XX:SurvivorPadding=3 -XX:ThresholdTolerance=10 -XX:AdaptiveSizePolicyCollectionCostMargin=49 -XX:YoungGenerationSizeIncrement=16 -XX:YoungGenerationSizeSupplement=104 -XX:YoungGenerationSizeSupplementDecay=9 -XX:TenuredGenerationSizeIncrement=22 -XX:TenuredGenerationSizeSupplement=117 -XX:TenuredGenerationSizeSupplementDecay=2 -XX:MaxGCPauseMillis=13557897735059052544 -XX:GCPauseIntervalMillis=0 -XX:MaxGCMinorPauseMillis=16119267456708329472 -XX:GCTimeRatio=73 -XX:AdaptiveSizeDecrementScaleFactor=4 -XX:AdaptiveSizeMajorGCDecayTimeScale=11 -XX:MinSurvivorRatio=1 -XX:InitialSurvivorRatio=6 -XX:BaseFootPrintEstimate=272901592 -XX:GCHeapFreeLimit=2 -XX:PrefetchCopyIntervalInBytes=654 -XX:PrefetchScanIntervalInBytes=748 -XX:PrefetchFieldsAhead=1 -XX:ProcessDistributionStride=3
How can this be achieved?
As A. Wolf said, I'd put all jvm options in a file and run
docker run --rm -d --name mycontainer8g -p 8080:8080 -m 800M -e JAVA_OPTIONS="$(cat myjavaoptions)" rafabene/java-container:openjdk-env
While passing values using .sh is valid, you have so many params so I think that the clearer way is using docker-compose.
Just install it from here then you can specify in your docker-compose.yml something like this:
environment:
- JAVA_OPTS=-Xmx128m -Xms128m
You will be able to change your configuration and then restart the container just typing:
docker-compose down
docker-compose up -d
You can use --env-file parameter for passing multiple env variables.
docker run --env-file ./env.list image-name
Here env.list file will be list of env values you want to set inside docker container.

Run Multiple Main Methods from the same Dockerfile

I have a large scale java application with 5 Main methods in different classes. I want to run this application as a docker container. From DockerHub OpenJDK Image, I started my Dockerfile as follows
FROM openjdk:latest
COPY . /usr/src/APP
WORKDIR /usr/src/APP`
and I want to add the lines to run the main methods. Without Docker, I run the app using the below lines
echo 'Starting App'
nohup $JAVA_HOME/bin/java .:./App.jar path.to.main.class1 >>
/path/to/nohup/nohup.out 2>&1 &
nohup $JAVA_HOME/bin/java .:./App.jar path.to.main.class2 >>
/path/to/nohup/nohup.out 2>&1 &
nohup $JAVA_HOME/bin/java .:./App.jar path.to.main.class3 >>
/path/to/nohup/nohup.out 2>&1 &
nohup $JAVA_HOME/bin/java .:./App.jar path.to.main.class4 >>
/path/to/nohup/nohup.out 2>&1 &
nohup $JAVA_HOME/bin/java .:./App.jar path.to.main.class5 >>
/path/to/nohup/nohup.out 2>&1 &
echo 'App Started Successfully'`
Is it possible to run the above scenario in one docker container? If possible, how can it be done while there can only be one ENTRYPOINT and CMD instructions in a Dockerfile ?
The usual answer to "how do I run multiple processes from one image" is to run multiple containers. Given the Dockerfile you show this is fairly straightforward:
# Build the image (once)
docker build -t myapp .
# Then run the five containers as background processes
docker run -d --name app1 java .:./App.jar path.to.main.class1
docker run -d --name app2 java .:./App.jar path.to.main.class2
docker run -d --name app3 java .:./App.jar path.to.main.class3
docker run -d --name app4 java .:./App.jar path.to.main.class4
docker run -d --name app5 java .:./App.jar path.to.main.class5
Since all of the commands are pretty similar, you could write a script to run them
#!/bin/sh
# Use the first command-line argument as the main class
MAIN_CLASS="$1"
shift
# Can also set JAVA_OPTS, other environment variables, ...
# Run the application
exec java -jar App.jar "path.to.main.$MAIN_CLASS" "$#"
copy that into the image
COPY run_main.sh /usr/local/bin
and then when you launch the containers just run that wrapper
docker run -d --name app1 run_main.sh class1

Dockerfile Spring Boot property from variable not working

I can't seem to get Spring boot properties to work via variable in my Dockerfile. This is what I am doing:
ENTRYPOINT exec java -Dapp-version=$app_version -jar /app.jar
If I do RUN echo "App Version: $app_version" inside of my Dockerfile then I get then I get the correct output like App Version: 1.70.0.
If I manually put the version like this: ENTRYPOINT exec java -Dapp-version=1.70.0 -jar /app.jar then the value is injected correctly.
In fact, if I do RUN echo "ENTRYPOINT exec java -Dapp-version=$app_version -jar /app.jar" then I get output like
Step 9/10 : RUN echo "ENTRYPOINT exec java -D******ion=$app_version -jar /app.jar"
---> Running in b6c3cd9bb69a
ENTRYPOINT exec java -D******ion=1.70.0 -jar /app.jar
The value inside of Spring is being set as an empty string when I use the Dockerfile variable. When I hard code it to 1.70.0 then it is being set correctly. What am I missing?
I have tried many different things including using {}, quotes, etc.
Edit: Added Dockerfile
FROM java:8
ARG app_version
RUN echo -------------------
RUN echo "App Version: $app_version"
RUN echo -------------------
VOLUME /tmp
COPY ./build/libs/mango-sticky-rice-1.0.0-SNAPSHOT.jar /app.jar
RUN bash -c 'touch /app.jar'
ENTRYPOINT exec java -Dapp-version=$app_version -jar /app.jar
This answer worked: https://stackoverflow.com/a/49889134/3088642. This is what my Dockerfile looks like:
FROM java:8
ARG app_version
RUN echo -------------------
RUN echo "App Version: ${app_version}"
RUN echo -------------------
VOLUME /tmp
COPY ./build/libs/mango-sticky-rice-1.0.0-SNAPSHOT.jar /app.jar
RUN bash -c 'touch /app.jar'
RUN echo "#!/bin/bash \n java -Dapp-version=${app_version} -jar /app.jar" > ./entrypoint.sh
RUN chmod +x ./entrypoint.sh
RUN cat ./entrypoint.sh
ENTRYPOINT ["./entrypoint.sh"]

Dockerfile of console application that accept arguments in Commons CLI style

I have a java console application that I package as jar and run it as
java -jar target/myProject-1.0-SNAPSHOT.jar -arg1 145 -arg2 345 -arg3 99
I want to run the same command inside a container and pass these arguments (arg1, arg2, arg3) to docker run command. My docker file look like:
FROM openjdk:8-jdk-alpine
VOLUME /tmp
ADD target/myProject-1.0-SNAPSHOT.jar myProject-1.0-SNAPSHOT.jar
ENV JAVA_OPTS=""
ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /myProject-1.0-SNAPSHOT.jar" ]
then I try to run the image as follows:
docker run myProject:0.3 -e -arg1 145 -arg2 345 -arg3 99
but my program don't get the arguments. what I'm missing ?
You have to add the ENV command in the DOCKERFILE so that you can receive the arguments that you are passing in and then pass that onto the ENTRYPOINT script
Dockerfile will look something like this
FROM openjdk:8-jdk-alpine
VOLUME /tmp
ENV arg1
ENV arg2
ENV arg3
ADD target/myProject-1.0-SNAPSHOT.jar myProject-1.0-SNAPSHOT.jar
ENV JAVA_OPTS=""
ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /myProject-1.0-SNAPSHOT.jar ${arg1} ${arg2} ${arg3}" ]
Let me know if you have any questions
The arguments you pass to docker run are the command it's running, which it appends to the end of the entry point. So what you're doing is equivalent to running:
sh -c "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /myProject-1.0-SNAPSHOT.jar" -arg1 145 -arg2 345 -arg3 99
Presented like this, you can see that the arguments are going to sh and not to java. If you want to combine a set of options that you define when you build the image with a set of options that you can append at runtime, you'll need to use a wrapper script or something similar.
Create file wrapper.sh and make it executable:
#!/bin/sh
exec java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /myProject-1.0-SNAPSHOT.jar "$#"
Add it to your container with ADD wrapper.sh /bin in the Dockerfile
Change your entrypoint to ["/bin/wrapper.sh"]
Now, when you run the image, it will append the arguments to the java command line

Categories