Skip to content

Commit

Permalink
add Java 11 support (#160)
Browse files Browse the repository at this point in the history
based on Fedora 29 base image, could be switched
to CentOS 7.6 later when that's available (#193).
  • Loading branch information
vorburger committed Nov 21, 2018
1 parent a000325 commit dad8db3
Show file tree
Hide file tree
Showing 19 changed files with 2,382 additions and 0 deletions.
13 changes: 13 additions & 0 deletions java/images.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,19 @@ fish-pepper:

config:
base:
fedora-java11:
withGradle: true
from: "fedora:29"
user: "jboss"
home: "/opt/jboss"
javaSecurity: "/usr/lib/jvm/java/conf/security/java.security"
description: "Fedora S2I Java builder image with OpenJDK 11"
version:
javaMajor: "11"
java: "" # empty means latest
maven: "3.5.4"
jolokia: "1.6.0"
jmxexporter: "0.3.1"
centos:
withGradle: true
from: "centos:7.5.1804"
Expand Down
122 changes: 122 additions & 0 deletions java/images/fedora-java11/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
FROM fedora:29

ENV JOLOKIA_VERSION="1.6.0" \
PROMETHEUS_JMX_EXPORTER_VERSION="0.3.1" \
PATH=$PATH:"/usr/local/s2i" \
AB_JOLOKIA_PASSWORD_RANDOM="true" \
AB_JOLOKIA_AUTH_OPENSHIFT="true" \
JAVA_DATA_DIR="/deployments/data"

# Some version information
LABEL io.fabric8.s2i.version.maven="3.5.4" \
io.fabric8.s2i.version.jolokia="1.6.0" \
io.fabric8.s2i.version.prometheus.jmx_exporter="0.3.1" \
io.k8s.description="Platform for building and running plain Java applications (fat-jar and flat classpath)" \
io.k8s.display-name="Java Applications" \
io.openshift.tags="builder,java" \
io.openshift.s2i.scripts-url="image:///usr/local/s2i" \
io.openshift.s2i.destination="/tmp" \
org.jboss.deployments-dir="/deployments" \
com.redhat.deployments-dir="/deployments" \
com.redhat.dev-mode="JAVA_DEBUG:false" \
com.redhat.dev-mode.port="JAVA_DEBUG_PORT:5005"

# Temporary switch to root
USER root



# Install Java package & download Maven from Apache
RUN yum install -y \
java-11-openjdk \
java-11-openjdk-devel \
&& curl https://archive.apache.org/dist/maven/maven-3/3.5.4/binaries/apache-maven-3.5.4-bin.tar.gz | \
tar -xzf - -C /opt \
&& ln -s /opt/apache-maven-3.5.4 /opt/maven \
&& ln -s /opt/maven/bin/mvn /usr/bin/mvn \
&& groupadd -r jboss -g 1000 \
&& useradd -u 1000 -r -g jboss -m -d /opt/jboss -s /sbin/nologin -c "JBoss user" jboss \
&& chmod 755 /opt/jboss

ENV JAVA_HOME /etc/alternatives/jre


# Use /dev/urandom to speed up startups & Add jboss user to the root group
RUN echo securerandom.source=file:/dev/urandom >> /usr/lib/jvm/java/conf/security/java.security \
&& usermod -g root -G jboss jboss

# Prometheus JMX exporter agent
RUN mkdir -p /opt/prometheus/etc \
&& curl http://central.maven.org/maven2/io/prometheus/jmx/jmx_prometheus_javaagent/0.3.1/jmx_prometheus_javaagent-0.3.1.jar \
-o /opt/prometheus/jmx_prometheus_javaagent.jar
ADD prometheus-opts /opt/prometheus/prometheus-opts
ADD prometheus-config.yml /opt/prometheus/prometheus-config.yml
RUN chmod 444 /opt/prometheus/jmx_prometheus_javaagent.jar \
&& chmod 444 /opt/prometheus/prometheus-config.yml \
&& chmod 755 /opt/prometheus/prometheus-opts \
&& chmod 775 /opt/prometheus/etc \
&& chgrp root /opt/prometheus/etc

EXPOSE 9779


# Jolokia agent
RUN mkdir -p /opt/jolokia/etc \
&& curl http://central.maven.org/maven2/org/jolokia/jolokia-jvm/1.6.0/jolokia-jvm-1.6.0-agent.jar \
-o /opt/jolokia/jolokia.jar
ADD jolokia-opts /opt/jolokia/jolokia-opts
RUN chmod 444 /opt/jolokia/jolokia.jar \
&& chmod 755 /opt/jolokia/jolokia-opts \
&& chmod 775 /opt/jolokia/etc \
&& chgrp root /opt/jolokia/etc

EXPOSE 8778


# Install rsync
RUN yum install -y rsync


# S2I scripts + README
COPY s2i /usr/local/s2i
RUN chmod 755 /usr/local/s2i/*
ADD README.md /usr/local/s2i/usage.txt

# Add run script as /opt/run-java/run-java.sh and make it executable
COPY run-java.sh /opt/run-java/
RUN chmod 755 /opt/run-java/run-java.sh

# Adding run-env.sh to set app dir
COPY run-env.sh /opt/run-java/run-env.sh
RUN chmod 755 /opt/run-java/run-env.sh


# Copy licenses
RUN mkdir -p /opt/fuse/licenses
COPY licenses.css /opt/fuse/licenses
COPY licenses.xml /opt/fuse/licenses
COPY licenses.html /opt/fuse/licenses
COPY apache_software_license_version_2.0-apache-2.0.txt /opt/fuse/licenses


# Necessary to permit running with a randomised UID
RUN mkdir -p /deployments/data \
&& chmod -R "g+rwX" /deployments \
&& chown -R jboss:root /deployments \
&& chmod -R "g+rwX" /opt/jboss \
&& chown -R jboss:root /opt/jboss \
&& chmod 664 /etc/passwd

# S2I scripts rely on /opt/jboss as working directory
WORKDIR /opt/jboss

# S2I requires a numeric, non-0 UID. This is the UID for the jboss user in the base image

USER 1000
RUN mkdir -p /opt/jboss/.m2
COPY settings.xml /opt/jboss/.m2/settings.xml


# Use the run script as default since we are working as an hybrid image which can be
# used directly to. (If we were a plain s2i image we would print the usage info here)
CMD [ "/usr/local/s2i/run" ]
189 changes: 189 additions & 0 deletions java/images/fedora-java11/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
# Fedora S2I Java builder image with OpenJDK 11

This is a S2I builder image for Java builds whose result can be run directly without any further application server.It's suited ideally for microservices with a flat classpath (including "far jars").

This image also provides an easy integration with an [Jolokia](https://github.com/rhuss/jolokia) agent. See below how to configure this.

The following environment variables can be used to influence the behaviour of this builder image:

## Build Time

* **MAVEN_ARGS** Arguments to use when calling Maven, replacing the default `package hawt-app:build -DskipTests -e`. Please be sure to run the `hawt-app:build` goal (when not already bound to the `package` execution phase), otherwise the startup scripts won't work.
* **MAVEN_ARGS_APPEND** Additional Maven arguments, useful for temporary adding arguments like `-X` or `-am -pl ..`
* **ARTIFACT_DIR** Path to `target/` where the jar files are created for multi module builds. These are added to `${MAVEN_ARGS}`
* **ARTIFACT_COPY_ARGS** Arguments to use when copying artifacts from the output dir to the application dir. Useful to specify which artifacts will be part of the image. It defaults to `-r hawt-app/*` when a `hawt-app` dir is found on the build directory, otherwise jar files only will be included (`*.jar`).
* **MAVEN_CLEAR_REPO** If set then the Maven repository is removed after the artifact is built. This is useful for keeping
the created application image small, but prevents *incremental* builds. The default is `false`

## Run Time


### run-java.sh

This general purpose startup script is optimized for running Java application from within containers. It is called like

```
./run-java.sh <sub-command> <options>
```
`run-java.sh` knows two sub-commands:

* `options` to print out JVM option which can be used for own invocation of Java apps (like Maven or Tomcat). It respects container constraints and includes all magic which is used by this script
* `run` executes a Java application as described below. This is also the default command so you can skip adding this command.

### Running a Java application

When no subcommand is given (or when you provide the default subcommand `run`), then by default this scripts starts up Java application.

The startup process is configured mostly via environment variables:

* **JAVA_APP_DIR** the directory where the application resides. All paths in your application are relative to this directory. By default it is the same directory where this startup script resides.
* **JAVA_LIB_DIR** directory holding the Java jar files as well an optional `classpath` file which holds the classpath. Either as a single line classpath (colon separated) or with jar files listed line-by-line. If not set **JAVA_LIB_DIR** is the same as **JAVA_APP_DIR**.
* **JAVA_OPTIONS** options to add when calling `java`
* **JAVA_MAJOR_VERSION** can be 7,8 or 9. If the version is set then only options suitable for this version are used. Actually only 7 is required to set to remove some options known only to Java > 8
* **JAVA_MAX_MEM_RATIO** is used when no `-Xmx` option is given in `JAVA_OPTIONS`. This is used to calculate a default maximal Heap Memory based on a containers restriction. If used in a Docker container without any memory constraints for the container then this option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio of the container available memory as set here. The default is `25` when the maximum amount of memory available to the container is below 300M, `50` otherwise, which means in that case that 50% of the available memory is used as an upper boundary. You can skip this mechanism by setting this value to 0 in which case no `-Xmx` option is added.
* **JAVA_INIT_MEM_RATIO** is used when no `-Xms` option is given in `JAVA_OPTIONS`. This is used to calculate a default initial Heap Memory based on a containers restriction. If used in a Docker container without any memory constraints for the container then this option has no effect. If there is a memory constraint then `-Xms` is set to a ratio of the container available memory as set here. By default this value is not set.
* **JAVA_MAX_CORE** restrict manually the number of cores available which is used for calculating certain defaults like the number of garbage collector threads. If set to 0 no base JVM tuning based on the number of cores is performed.
* **JAVA_DIAGNOSTICS** set this to get some diagnostics information to standard out when things are happening
* **JAVA_MAIN_CLASS** A main class to use as argument for `java`. When this environment variable is given, all jar files in `$JAVA_APP_DIR` are added to the classpath as well as `$JAVA_LIB_DIR`.
* **JAVA_APP_JAR** A jar file with an appropriate manifest so that it can be started with `java -jar` if no `$JAVA_MAIN_CLASS` is set. In all cases this jar file is added to the classpath, too.
* **JAVA_APP_NAME** Name to use for the process
* **JAVA_CLASSPATH** the classpath to use. If not given, the startup script checks for a file `${JAVA_APP_DIR}/classpath` and use its content literally as classpath. If this file doesn't exists all jars in the app dir are added (`classes:${JAVA_APP_DIR}/*`).
* **JAVA_DEBUG** If set remote debugging will be switched on
* **JAVA_DEBUG_SUSPEND** If set enables suspend mode in remote debugging
* **JAVA_DEBUG_PORT** Port used for remote debugging. Default: 5005
* **HTTP_PROXY** The URL of the proxy server that translates into the `http.proxyHost` and `http.proxyPort` system properties.
* **HTTPS_PROXY** The URL of the proxy server that translates into the `https.proxyHost` and `https.proxyPort` system properties.
* **no_proxy**, **NO_PROXY** The list of hosts that should be reached directly, bypassing the proxy, that translates into the `http.nonProxyHosts` system property.

If neither `$JAVA_APP_JAR` nor `$JAVA_MAIN_CLASS` is given, `$JAVA_APP_DIR` is checked for a single JAR file which is taken as `$JAVA_APP_JAR`. If no or more then one jar file is found, an error is thrown.

The classpath is build up with the following parts:

* If `$JAVA_CLASSPATH` is set, this classpath is taken.
* The current directory (".") is added first.
* If the current directory is not the same as `$JAVA_APP_DIR`, `$JAVA_APP_DIR` is added.
* If `$JAVA_MAIN_CLASS` is set, then
- A `$JAVA_APP_JAR` is added if set
- If a file `$JAVA_APP_DIR/classpath` exists, its content is appended to the classpath. This file
can be either a single line with the jar files colon separated or a multi-line file where each line
holds the path of the jar file relative to `$JAVA_LIB_DIR` (which by default is the `$JAVA_APP_DIR`)
- If this file is not set, a `${JAVA_APP_DIR}/*` is added which effectively adds all
jars in this directory in alphabetical order.

These variables can be also set in a shell config file `run-env.sh`, which will be sourced by the startup script. This file can be located in the directory where the startup script is located and in `${JAVA_APP_DIR}`, whereas environment variables in the latter override the ones in `run-env.sh` from the script directory.

This startup script also checks for a command `run-java-options`. If existent it will be called and the output is added to the environment variable `$JAVA_OPTIONS`.

The startup script also exposes some environment variables describing container limits which can be used by applications:

* **CONTAINER_CORE_LIMIT** a calculated core limit as described in https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt
* **CONTAINER_MAX_MEMORY** memory limit given to the container

Any arguments given to the script are given through directly as argument to the Java application.

Example:

```
# Set the application directory directly
export JAVA_APP_DIR=/deployments
# Set -Xmx based on container constraints
export JAVA_MAX_MEM_RATIO=40
# Start the jar in JAVA_APP_DIR with the given arguments
./run-java.sh --user maxmorlock --password secret
```

### Options

This script can also be used to calculate reasonable, best-practice options for starting Java apps in general. For example, when running Maven in a container it makes sense to respect container Memory constraints.

The subcommand `options` can be used to print options to standard output so that is can be easily used to feed it to another, Java based application.

When no extra arguments are given, all defaults will be used, which can be influenced with the environment variables described above.

You can select specific sets of options by providing additional arguments:

* `--debug` : Java debug options if `JAVA_DEBUG` is set
* `--memory` : Memory settings based on the environment variables given
* `--proxy` : Evaluate proxy environments variables
* `--cpu` : Tuning when the number of cores is limited
* `--gc` : GC tuning parameters
* `--jit` : JIT options
* `--diagnostics` : Print diagnostics options when `JAVA_DIAGNOSTICS` is set
* `--java-default` : Same as `--memory --jit --diagnostic --cpu --gc`

Example:

```
# Call Maven with the proper memory settings when running in an container
export MAVEN_OPTS="$(run-java.sh options --memory)"
mvn clean install
```


#### Jolokia configuration

* **AB_JOLOKIA_OFF** : If set disables activation of Jolokia (i.e. echos an empty value). By default, Jolokia is enabled.
* **AB_JOLOKIA_CONFIG** : If set uses this file (including path) as Jolokia JVM agent properties (as described
in Jolokia's [reference manual](http://www.jolokia.org/reference/html/agents.html#agents-jvm)). If not set,
the `/opt/jolokia/etc/jolokia.properties` will be created using the settings as defined in this document, otherwise
the reset of the settings in this document are ignored.
* **AB_JOLOKIA_HOST** : Host address to bind to (Default: `0.0.0.0`)
* **AB_JOLOKIA_PORT** : Port to use (Default: `8778`)
* **AB_JOLOKIA_USER** : User for basic authentication. Defaults to 'jolokia'
* **AB_JOLOKIA_PASSWORD** : Password for basic authentication. By default authentication is switched off.
* **AB_JOLOKIA_PASSWORD_RANDOM** : Should a random AB_JOLOKIA_PASSWORD be generated? Generated value will be written to `/opt/jolokia/etc/jolokia.pw`
* **AB_JOLOKIA_HTTPS** : Switch on secure communication with https. By default self signed server certificates are generated
if no `serverCert` configuration is given in `AB_JOLOKIA_OPTS`
* **AB_JOLOKIA_ID** : Agent ID to use (`$HOSTNAME` by default, which is the container id)
* **AB_JOLOKIA_DISCOVERY_ENABLED** : Enable Jolokia discovery. Defaults to false.
* **AB_JOLOKIA_OPTS** : Additional options to be appended to the agent configuration. They should be given in the format
"key=value,key=value,..."

Some options for integration in various environments:

* **AB_JOLOKIA_AUTH_OPENSHIFT** : Switch on client authentication for OpenShift TSL communication. The value of this
parameter can be a relative distinguished name which must be contained in a presented client certificate. Enabling this
parameter will automatically switch Jolokia into https communication mode. The default CA cert is set to
`/var/run/secrets/kubernetes.io/serviceaccount/ca.crt`



Application arguments can be provided by setting the variable **JAVA_ARGS** to the corresponding value.

## Spring Boot Automatic Restarts

This image also supports detecting jars with [Spring Boot devtools](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#using-boot-devtools) included, which allows automatic restarts when files on the classpath are updated. Files can be easily updated in OpenShift using command [`oc rsync`](https://docs.openshift.org/latest/dev_guide/copy_files_to_container.html).

To enable automatic restarts, three things are required:

1. Add Spring Boot devtools dependency:

```xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
```

2. Add dependency to the generated fat jar by setting `excludeDevtools` configuration property to false:

```xml
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludeDevtools>false</excludeDevtools>
</configuration>
</plugin>
</plugins>
</build>
```

3. Set environment variables `JAVA_DEBUG=true` or `DEBUG=true` and optionally `JAVA_DEBUG_PORT=<port-number>` or `DEBUG_PORT=<port-number>`, which defaults to 5005. Since the `DEBUG` variable clashes with Spring Boot's recognition of the same variable to enable Spring Boot debug logging, use `SPRINGBOOT_DEBUG` instead.

WARNING: Do not use devtools in production!!! This can be accomplished in Maven using a custom profile.
Loading

0 comments on commit dad8db3

Please sign in to comment.