///////////////////////////////////////////////////////////////////////////////

    Copyright (c) 2018, 2019 Oracle and/or its affiliates. All rights reserved.

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.

///////////////////////////////////////////////////////////////////////////////

= Creating Docker Images 
:description: Helidon Docker Guide
:keywords: helidon, guide, docker
:helidon-tag: https://github.com/oracle/helidon/tree/{helidon-version}
:quickstart-example: {helidon-tag}/examples/quickstarts/helidon-quickstart-se
:docker-multi-stage-url: https://docs.docker.com/develop/develop-images/multistage-build/
:openjdk-docker-217: https://github.com/docker-library/openjdk/issues/217

This guide describes how to create a Docker image for your Helidon
 application, using a Java 8 base image or a custom Java 11 JRE built with
 `jlink`.

== What You Need

|===
|About 10 minutes
| <<about/03_prerequisites.adoc,Helidon Prerequisites>>
|You'll also need Java 11 if you want to create custom JRE's using `jlink`
|===

== Java Packaging

The Helidon team recommends setting-up the class-path inside the
 `META-INF/MANIFEST.MF` file with the `Class-Path` entry pointing to dependency
 jar files in a co-located `lib` directory. See the Maven and Gradle
 link:{quickstart-example}[examples] for more details on how to package your
 Helidon application.

This approach is a good fit for Docker images:

* the application code and dependencies can be separate image layers.
* the image layer containing your dependencies is re-built only when
 dependencies are updated.
* the deployment environment(s) pull the images layer containing your
 dependencies only when changed.

[NOTE]
.Why no fat jars?
====
Fat Jars are jar files that contain the application and its dependencies ; they
 are not optimal for Docker images as it results in a single image layer.
====

== Creating a Java 8 Based Docker Image

This section describes the Dockerfile provided by the
Helidon <<guides/02_quickstart-se.adoc, SE>> and
 <<guides/03_quickstart-mp.adoc, MP>> quickstarts.

The Dockerfile is located at `./Dockerfile` and contains the following:

[source,yaml]
----
# Multistage Docker build. <1>
# 1st stage, build the app <2>
FROM maven:3.5.4-jdk-9 as build

WORKDIR /helidon

# Create a first layer to cache the "Maven World" in the local repository.
# Incremental docker builds will always resume after that, unless you update
# the pom
ADD pom.xml .
RUN mvn package -DskipTests # <3>

# Do the Maven build!
# Incremental docker builds will resume here when you change sources
ADD src src
RUN mvn package -DskipTests # <4>

RUN echo "done!"

# 2nd stage, build the runtime image <5>
FROM openjdk:8-jre-slim # <6>
WORKDIR /helidon

# Copy the binary built in the 1st stage
COPY --from=build /helidon/target/helidon-quickstart-se.jar ./
COPY --from=build /helidon/target/libs ./libs # <5>

CMD ["java", "-jar", "helidon-quickstart-se.jar"] # <7>
----
<1> This is a multi-stage Docker build. See more info
 link:{docker-multi-stage-url}[here]
<2> The first stage that creates the build artifacts
<3> Create a layer, using just the `pom.xml`, that contains the Maven cache.
<4> When resuming here, Maven won't re-download the world.
<5> The final stage that creates the image for our application.
<6> Using a lightweight image.
<7> The command to start the application.

[source,bash]
.Build the project
----
mvn package
----

[source,bash]
.Build the Docker image
----
docker build -t quickstart-se target
----

[source,bash]
.Run the docker container
----
docker run --rm -p 8080:8080 quickstart-se:latest
----

[source,bash]
.Ping the application
----
curl -X GET http://localhost:8080/greet
----

== Creating a Docker Image with a Custom JRE

This section describes how to build an image with a custom Java 11 JRE using
 `jlink`.

Replace `Dockerfile` with the following:

[source,yaml]
----
# Multistage Docker build. <1>
# Stage 1: Build custom Java 11 JRE and put it in /var/tmp/myjre <2>
FROM openjdk:11-slim AS myjre
RUN ["jlink", "--compress=2", "--strip-debug", "--no-header-files", \
     "--add-modules", "java.base,java.logging,java.sql,java.desktop,java.management", \
     "--output", "/var/tmp/myjre"] # <3>

# Work around for https://github.com/docker-library/openjdk/issues/217 <4>
RUN [ "apt", "update"]
RUN [ "apt-get", "install", "-y", "binutils"]
RUN ["strip", "-p", "--strip-unneeded", "/var/tmp/myjre/lib/server/libjvm.so"]
# End work-around

# Stage 2: Build application image using JRE from Stage 1 <5>
FROM debian:sid-slim # <6>
COPY --from=myjre /var/tmp/myjre /opt/jre # <7>
ENV PATH=$PATH:/opt/jre/bin

RUN mkdir /app
COPY libs /app/libs
COPY ${project.artifactId}.jar /app

CMD ["java", "-jar", "/app/${project.artifactId}.jar"]
----
<1> This is a multi-stage Docker build. See more info
 link:{docker-multi-stage-url}[here]
<2> The first stage that creates our custom JRE.
<3> The modules listed in this example are for Helidon SE. See below for
 Helidon MP.
<4> This is a work-around for {openjdk-docker-217}.
<5> The final stage that creates the image for our application.
<6> Use `debian:sid-slim` to match the base image of `openjdk:11-slim`
<7> Copy the JRE from the image of the first stage `myjre`

[source,bash]
.Process the new Dockerfile
----
mvn process-resources
----

[source,bash]
.Build the Docker image
----
docker build -t java11-quickstart-se target
----

[source,bash]
.Run the docker container
----
docker run --rm -p 8080:8080 java11-quickstart-se:latest
----

[source,bash]
.Ping the application
----
curl -X GET http://localhost:8080/greet
----

[source,bash]
.Take a look at the image size
----
docker images java11-quickstart-se:latest
----

[source,bash]
----
REPOSITORY             TAG        IMAGE ID         CREATED             SIZE
java11-quickstart-se   latest     f07a7b8bda78     About a minute ago  136MB
----

NOTE: ~140MB is less than the pre-built OpenJDK slim JRE images. Results might
 differ a bit depending on your platform.

=== What about Helidon MP?

For Helidon MP you need to add a couple more modules to the `jlink` command:

[source,yaml,subs="verbatim,attributes"]
----
RUN ["jlink", "--compress=2", "--strip-debug", "--no-header-files", \
     "--add-modules", \
     "java.base,java.logging,java.sql,java.desktop,java.management,java.naming,jdk.unsupported", \
     "--output", "/var/tmp/myjre"]
----