s6, Java, and Gradle - blitterated/docker-dev-env-s6 GitHub Wiki
Create a directory for this experiment and change into it.
mkdir docker-java && cd $_
Download the s6 installer from here.
Ex.
curl -O https://github.com/just-containers/s6-overlay/releases/download/v2.2.0.3/s6-overlay-amd64-installer
Download the latest OpenJDK from here.
Ex.
curl -O https://download.java.net/java/GA/jdk17.0.1/2a2082e5a09d4267845be086888add4f/12/GPL/openjdk-17.0.1_linux-x64_bin.tar.gz
Extract the JDK into the directory. There should be a directory called something like jdk-17.0.1
when it's done.
tar -zvxf openjdk-17.0.1_linux-x64_bin.tar.gz
Download the latest Gradle from here. Not sure if this can be curl
ed or not.
Unzip Gradle.
unzip gradle-7.3.3-bin.zip
Tried to create an /etc/environment
file with paths and variables needed for the JDK and Gradle. But it none of the changes would take. Turns out it is only "read on login, when the PAM stack is activated – specifically pam_env.so
, which reads the file." See the Best Answer here
Instead, here's a .bashrc
to copy into the container.
cat <<"EOF" > bashrc
PATH="$PATH:/opt/jdk-17.0.1/bin:/opt/jdk-17.0.1/db/bin:/opt/jdk-17.0.1/jre/bin:/opt/gradle-7.3.3/bin"
J2SDKDIR="/opt/jdk-17.0.1"
J2REDIR="/opt/jdk-17.0.1"
JAVA_HOME="/opt/jdk-17.0.1"
DERBY_HOME="/opt/jdk-17.0.1/db"
EOF
Create a Dockerfile that copies and installs s6, copies the JDK over, and copies Gradle over as well.
cat <<"EOF" > Dockerfile
FROM ubuntu
RUN apt update && apt --yes upgrade
COPY jdk-17.0.1 /opt/jdk-17.0.1
COPY gradle-7.3.3 /opt/gradle-7.3.3
WORKDIR /root
COPY bashrc .bashrc
COPY s6-overlay-amd64-installer s6-overlay-amd64-installer
RUN chmod +x /root/s6-overlay-amd64-installer && \
/root/s6-overlay-amd64-installer /
ENTRYPOINT ["/init"]
EOF
Build an image.
docker build -t s6_java .
Fire it up.
docker run -it --rm s6_java /bin/bash
Check the java
version.
java --version
openjdk 17.0.1 2021-10-19
OpenJDK Runtime Environment (build 17.0.1+12-39)
OpenJDK 64-Bit Server VM (build 17.0.1+12-39, mixed mode, sharing)
Check JAVA_HOME.
echo $JAVA_HOME
/opt/jdk-17.0.1
Check the Java compiler version.
javac --version
javac 17.0.1
Check the Gradle version.
gradle --version
Welcome to Gradle 7.3.3!
Here are the highlights of this release:
- Easily declare new test suites in Java projects
- Support for Java 17
- Support for Scala 3
For more details see https://docs.gradle.org/7.3.3/release-notes.html
------------------------------------------------------------
Gradle 7.3.3
------------------------------------------------------------
Build time: 2021-12-22 12:37:54 UTC
Revision: 6f556c80f945dc54b50e0be633da6c62dbe8dc71
Kotlin: 1.5.31
Groovy: 3.0.9
Ant: Apache Ant(TM) version 1.10.11 compiled on July 10 2021
JVM: 17.0.1 (Oracle Corporation 17.0.1+12-39)
OS: Linux 5.10.76-linuxkit amd64
Exit the container.
exit
Before we dig in to getting a Gradle Daemon running, take a look at what things look like when it's not running.
Start another container.
docker run -it --rm s6_java /bin/bash
Check on the status of the Gradle Daemon.
gradle --status
You may see a lot of text since Gradle thinks it's running for the first time. But you should see the following message in there:
No Gradle daemons are running.
Exit the container.
exit
Delete the image. We'll create a new one below.
docker rmi s6_java
Create a run
file for s6 that starts the Gradle Daemon. Note that --foreground
needs to be used instead of --daemon
because the latter will exit as soon as the daemon process is launched in the background. This causes s6 to think the service went down, and it will run the finish
script and then try to restart with run
. This results in an endless loop.
cat <<"EOF" > run
#!/usr/bin/with-contenv execlineb
gradle --foreground
EOF
Create a finish
file for s6 that stops the Gradle Daemon.
cat <<"EOF" > finish
#!/usr/bin/with-contenv execlineb
gradle --stop
EOF
execlineb
won't be able to see the changes to the system path made before with .bashrc
since it's not in an interactive shell. The shell is also non-login, but that doesn't matter as much since .bash_profile
calls .bashrc
by default. Move the PATH
and other variables into the Dockerfile with ENV.
Update the Dockerfile so it copies run
and finish
files into an s6 managed service directory, /etc/services.d/gradle/
.
cat <<"EOF" > Dockerfile
FROM ubuntu
RUN apt update && apt --yes upgrade
ENV PATH=$PATH:/opt/jdk-17.0.1/bin:/opt/jdk-17.0.1/db/bin:/opt/jdk-17.0.1/jre/bin:/opt/gradle-7.3.3/bin \
J2SDKDIR=/opt/jdk-17.0.1 \
J2REDIR=/opt/jdk-17.0.1 \
JAVA_HOME=/opt/jdk-17.0.1 \
DERBY_HOME=/opt/jdk-17.0.1/db
COPY jdk-17.0.1 /opt/jdk-17.0.1
COPY gradle-7.3.3 /opt/gradle-7.3.3
WORKDIR /root
COPY s6-overlay-amd64-installer s6-overlay-amd64-installer
RUN chmod +x /root/s6-overlay-amd64-installer && \
/root/s6-overlay-amd64-installer /
COPY run /etc/services.d/gradle/run
COPY finish /etc/services.d/gradle/finish
ENTRYPOINT ["/init"]
EOF
Build an image.
docker build -t s6_gradle .
Fire it up.
docker run -it --rm s6_gradle /bin/bash
Now check on the Gradle Daemon.
gradle --status
It lives!
PID STATUS INFO
181 IDLE 7.3.3
If you manually stop it, s6 will restart it unless you tell it to do otherwise.
gradle --stop
Well, there's a little bit of a race condition with output to STDOUT, but it works.
Daemon is stopping immediately stop command received
Stopping Daemon(s)
1 Daemon stopped
root@cfd395e841be:~# No Gradle daemons are running.
Daemon server started.
One more status check.
gradle --status
PID STATUS INFO
354 IDLE 7.3.3
181 STOPPED (stop command received)
NEXT: how to stop s6 from restarting the daemon
Each case here was mooted because the run
file's hashbang was
#!/usr/bin/execlineb
instead of
#!/usr/bin/with-contenv execlineb
Update the Dockerfile so it copies the run
and finish
files into an s6 managed service directory, /etc/services.d/gradle/
.
cat <<"EOF" > Dockerfile
FROM ubuntu
RUN apt update && apt --yes upgrade
COPY jdk-17.0.1 /opt/jdk-17.0.1
COPY gradle-7.3.3 /opt/gradle-7.3.3
COPY run /etc/services.d/gradle/run
COPY finish /etc/services.d/gradle/finish
WORKDIR /root
COPY bashrc .bashrc
COPY s6-overlay-amd64-installer s6-overlay-amd64-installer
RUN chmod +x /root/s6-overlay-amd64-installer && \
/root/s6-overlay-amd64-installer /
ENTRYPOINT ["/init"]
EOF
run
script:
#!/usr/bin/execlineb
gradle --daemon
s6 reports:
execlineb: fatal: unable to exec gradle: No such file or directory
run
script with explicit path to gradle:
#!/usr/bin/execlineb
/opt/gradle-7.3.3/bin/gradle --daemon
ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation.
It's because the PATH
is set in .bashrc
execlineb
won't be able to see the changes to the system path made before with .bashrc
since it's not in an interactive shell. The shell is also non-login, but that doesn't matter as much since .bash_profile
calls .bashrc
by default. Move the PATH
and other variables into file to be dropped in /etc/profile.d
cat <<"EOF" > java_paths_and_env_vars.sh
PATH="$PATH:/opt/jdk-17.0.1/bin:/opt/jdk-17.0.1/db/bin:/opt/jdk-17.0.1/jre/bin:/opt/gradle-7.3.3/bin"
J2SDKDIR="/opt/jdk-17.0.1"
J2REDIR="/opt/jdk-17.0.1"
JAVA_HOME="/opt/jdk-17.0.1"
DERBY_HOME="/opt/jdk-17.0.1/db"
EOF
Update the Dockerfile so it copies the java_paths_and_env_vars.sh
into the /etc/profile.d/
directory. Also copy the run
and finish
files into an s6 managed service directory, /etc/services.d/gradle/
.
cat <<"EOF" > Dockerfile
FROM ubuntu
RUN apt update && apt --yes upgrade
COPY jdk-17.0.1 /opt/jdk-17.0.1
COPY gradle-7.3.3 /opt/gradle-7.3.3
COPY java_paths_and_env_vars.sh /etc/profile.d/java_paths_and_env_vars.sh
COPY run /etc/services.d/gradle/run
COPY finish /etc/services.d/gradle/finish
WORKDIR /root
COPY s6-overlay-amd64-installer s6-overlay-amd64-installer
RUN chmod +x /root/s6-overlay-amd64-installer && \
/root/s6-overlay-amd64-installer /
ENTRYPOINT ["/init"]
EOF
execlineb
won't be able to see the changes to the system path made before with .bashrc
since it's not in an interactive shell. The shell is also non-login, but that doesn't matter as much since .bash_profile
calls .bashrc
by default. Move the PATH
and other variables into the Dockerfile with ENV.
Update the Dockerfile so it copies run
and finish
files into an s6 managed service directory, /etc/services.d/gradle/
.
cat <<"EOF" > Dockerfile
FROM ubuntu
RUN apt update && apt --yes upgrade
ENV PATH=$PATH:/opt/jdk-17.0.1/bin:/opt/jdk-17.0.1/db/bin:/opt/jdk-17.0.1/jre/bin:/opt/gradle-7.3.3/bin \
J2SDKDIR=/opt/jdk-17.0.1 \
J2REDIR=/opt/jdk-17.0.1 \
JAVA_HOME=/opt/jdk-17.0.1 \
DERBY_HOME=/opt/jdk-17.0.1/db
COPY jdk-17.0.1 /opt/jdk-17.0.1
COPY gradle-7.3.3 /opt/gradle-7.3.3
COPY run /etc/services.d/gradle/run
COPY finish /etc/services.d/gradle/finish
WORKDIR /root
COPY s6-overlay-amd64-installer s6-overlay-amd64-installer
RUN chmod +x /root/s6-overlay-amd64-installer && \
/root/s6-overlay-amd64-installer /
ENTRYPOINT ["/init"]
EOF