Creating a Distroless Java 17 Container Image

About This Task

With an eLxr Server 12 development host, you can either obtain the source or create your own to build a distroless container image that runs a Java application as described in this procedure.

When planning your container image, certain frameworks such as Spring Boot, Quarkus, and Micronaut, prefer a build that produces a single runnable JAR, or an exploded application layout, over copying the artifact into the distroless image. This procedure provides such an example.

Tip

In your Java environment, consider setting JVM options for for memory limits, GC tuning, and so on, via ENTRYPOINT or an environment variable.

Before You Begin

  • You must have a development host with Docker installed.

  • You must have the Java 17 Java Development Kit (JDK) installed.

  • If you require a distroless container for more complex applications, you will need Apache Maven or Gradle to build the application prior to creating the container image.

Procedure

  1. Create a directory for the container project and navigate to it.

    $ mkdir -p my-distroless-app && cd my-distroless-app
    
  2. Create a HelloServer.java file.

    This is for example purposes. For your own container application, substitute this code with you own.

    java
    import com.sun.net.httpserver.HttpServer;
    import com.sun.net.httpserver.HttpHandler;
    import com.sun.net.httpserver.HttpExchange;
    import java.io.OutputStream;
    import java.net.InetSocketAddress;
    
    public class HelloServer {
        public static void main(String[] args) throws Exception {
            int port = Integer.parseInt(System.getenv().getOrDefault("PORT", "8080"));
            HttpServer server = HttpServer.create(new InetSocketAddress(port), 0);
            server.createContext("/", new HttpHandler() {
                @Override
                public void handle(HttpExchange exchange) {
                    try {
                        String resp = "Hello from Java 17 Distroless!";
                        exchange.sendResponseHeaders(200, resp.getBytes().length);
                        try (OutputStream os = exchange.getResponseBody()) {
                            os.write(resp.getBytes());
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
            server.start();
            System.out.println("Server running on port " + port);
        }
    }
    

    This example uses the lightweight bundled com.sun.net.httpserver.HttpServer available in the JDK, which keeps the application dependency-free.

  3. Build the application locally or in a build stage.

    Note

    The following is for example only. Consult Java documentation concerning build requirements for a given application.

    Option 1: Small application

    For a small application, you can compile and package with using javac and jar. This requires a Main-Class manifest with cfe. For additional information, consult Java documentation.

    $ javac -d out HelloServer.java
    $ jar cfe app.jar HelloServer -C out .
    

    Option 2: Large applications

    For larger, more complex applications that require an executable (also called a fat JAR), use Apache Maven or Gradle to produce it.

  4. Create a multi-stage Dockerfile

    This Dockerfile builds the application using a JDK image and then copies the resulting artifact into a distroless Java 17 runtime image. Copy the following contents in to a text editor and save the file as java17-distroless-example.Dockerfile.

    Example 1: Application built with javac and jar

    Dockerfile
    # Build stage
    FROM eclipse-temurin:17-jdk AS build
    WORKDIR /app
    COPY HelloServer.java ./
    RUN javac -d out HelloServer.java && \
        jar cfe app.jar HelloServer -C out .
    
    # Production stage (distroless Java 17)
    FROM elxrlinux/elxr:latest-distroless-java17
    WORKDIR /app
    COPY --from=build /app/app.jar /app/app.jar
    ENTRYPOINT ["java","-jar","/app/app.jar"]
    
    # If your distroless base uses a different entrypoint pattern, use:
    # CMD ["-jar","/app/app.jar"]
    # and set ENTRYPOINT ["java"] as appropriate for that image.
    

    Example 2: Application built with Maven

    Dockerfile
    # Build stage
    FROM maven:3.8.8-eclipse-temurin-17 AS build
    WORKDIR /app
    COPY pom.xml ./
    COPY src ./src
    RUN mvn -DskipTests package
    
    # Production stage (distroless Java 17)
    FROM elxrlinux/elxr:latest-distroless-java17
    WORKDIR /app
    COPY --from=build /app/app.jar /app/app.jar
    ENTRYPOINT ["java","-jar","/app/app.jar"]
    
    # If your distroless base uses a different entrypoint pattern, use:
    # CMD ["-jar","/app/app.jar"]
    # and set ENTRYPOINT ["java"] as appropriate for that image.
    
    In both examples:
    • The Build stage uses either an official JDK image or the Maven environment to compile and package the application

    • The Production stage uses a distroless Java 17 image and copies only the jar file

  5. Build the Docker image.

    $ docker build -t java17-distroless-example .
    
  6. Run the container.

    $ docker run -p 8080:8080 java17-distroless-example
    
  7. Visit http://localhost:8080 to see the application respond.

    Hello from Java 17 Distroless!
    

Results

Now that you have completed the distroless Java container image, consider learning about creating distroless nodejs images. For details, see Creating a Distroless Node.js 18 Container Image.