4

I'm exploring the initial steps of containerising a Tomcat-based Java project using Docker. With IntelliJ as my preferred IDE, I have successfully:

  • written a proof-of-concept Servlet;
  • set up a build artefact to create the resulting WAR;
  • with the IntelliJ Docker plugin and one of the official Tomcat Docker images, set up a container configuration that includes the WAR contents as one of its mount points;
  • deployed the container to Docker locally through IntelliJ and confirmed that I can successfully hit the Servlet through my local browser.

So in terms of the basic development cycle, I'm up and running.

But when I eventually come to external deployment (and even at some point during the development process), I will need to add libraries and resources and generate a truly self-contained container: in other words, I will need to go from the simple deployment that the IntelliJ plugin is currently doing of an "image with mount points" to having a full-fledged Dockerfile with all the relevant configuration specified, including my mounts effectively being translated into instructions to copy in the relevant content.

Now my question: how do people generally achieve this? Is there tooling built into IntelliJ that will assist with this? In the container deployment configuration settings in IntelliJ (where the mount points, base image etc are specified), there doesn't seem to be an option to configure resources to copy, for example (or an option to "copy into standalone container rather than mount from host FS"). Am I missing a tool/option somewhere, or is the scripting of the Docker file essentially a manual process? Or am I just barking up the wrong tree with my whole approach? I'd appreciate any advice on the process that people generally use for this!

Neil Coffey
  • 21,615
  • 7
  • 62
  • 83

3 Answers3

5

Jib by Google

I think, Jib would provide, what you need. It also provides plugins for both Maven and Gradle, and the respective plugin can be triggered in IntelliJ via Run/Debug Configuration (see the example at the very bottom).

What is Jib?

Jib builds optimized Docker and OCI images for your Java applications without a Docker daemon - and without deep mastery of Docker best-practices. It is available as plugins for Maven and Gradle and as a Java library.

What does Jib do?

Jib handles all steps of packaging your application into a container image. You don't need to know best practices for creating Dockerfiles or have Docker installed. Jib organizes your application into distinct layers; dependencies, resources, and classes; and utilizes Docker image layer caching to keep builds fast by only rebuilding changes. Jib's layer organization and small base image keeps overall image size small, which improves performance and portability.

Configuration

You can check the documentation. It contains a lot of information about different kind of configuration options regarding the creation and deploying a Docker image. Where you can also simply make use of environment variables.

Regarding your question, check this part for example: adding Arbitrary Files to the Image

In the container deployment configuration settings in IntelliJ (where the mount points, base image etc are specified), there doesn't seem to be an option to configure resources to copy, for example (or an option to "copy into standalone container rather than mount from host FS").

Demo

For demonstration purposes, I've created a simple project with Maven, where I also used the base image tomcat:9.0.36-jdk8-openjdk, which is also optional by the way - see Jib WAR Projects:

Servlet:

@WebServlet(urlPatterns = {"/hello-world"})
public class HelloWorld extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.println("Hello World");
    }
}

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>mvn-jib-example</artifactId>
    <version>1.0</version>
    <packaging>war</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <failOnMissingWebXml>false</failOnMissingWebXml>
    </properties>

    <dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <build>
        <finalName>servlet-hello-world</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>com.google.cloud.tools</groupId>
                <artifactId>jib-maven-plugin</artifactId>
                <version>2.5.0</version>
                <configuration>
                    <allowInsecureRegistries>true</allowInsecureRegistries>
                    <from>
                        <image>tomcat:9.0.36-jdk8-openjdk</image>
                    </from>
                    <to>
                        <image>registry.localhost/hello-world</image>
                        <auth>
                            <username>registry_username</username>
                            <password>registry_password</password>
                        </auth>
                        <tags>
                            <tag>latest</tag>
                        </tags>
                    </to>
                    <container>
                        <appRoot>/usr/local/tomcat/webapps/ROOT</appRoot>
                    </container>
                    <extraDirectories>
                        <paths>
                            <path>
                                <from>./src/main/resources/extra-stuff</from>
                                <into>/path/in/docker/image/extra-stuff</into>
                            </path>
                            <path>
                                <from>/absolute/path/to/other/stuff</from>
                                <into>/path/in/docker/image/other-stuff</into>
                            </path>
                        </paths>
                    </extraDirectories>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
  • Executing the following goals will create the docker image on the fly:
mvn clean package jib:dockerBuild
  • Confirm that the image was created:
docker image ls
  • Starting a container from the image
docker run --rm -p 8082:8080 registry.localhost/hello-world:latest

Result:

Hello World from Tomcat docker container

Deployment

To deploy the image to an external docker registry, you can check the sections below:

IDE

Last but not least, since you are working with IntellIJ IDEA, you can simply create a RUN/Debug configuration to automate the image creation and deployment via button button, e.g. one configuration for building the image, one for deploying it to localhost and one for deploying to extern registry and so on.

Here an example for maven (see): Maven and Jib with IntelliJ IDEA

Kenan Güler
  • 1,868
  • 5
  • 16
2

The project that I am doing right now is using Spring-boot which actually has embedded Tomcat inside. And I use Docker Gradle plugin(https://plugins.gradle.org/plugin/com.bmuschko.docker-spring-boot-application) to build and push Docker image to registry which can be docker hub or AWS ECR. The combination is playing well with IntelliJ as it is Gradle task anyway.

Because it is Spring-boot, the plugin can build image based on any basic JRE image(I use https://hub.docker.com/_/adoptopenjdk) with minimum configuration. Do not need to write your own Dockerfile at all.

docker {

        def registryHost = 'xxx.dkr.ecr.us-west-2.amazonaws.com'

        springBootApplication {
               baseImage = "${registryHost}/caelus:springboot-jdk14-openj9"
               images = ["${registryHost}/caelus:app"]
               ports = [8080,8081]
               jvmArgs =['-Djdk.httpclient.allowRestrictedHeaders=content-length']
        }
}
Chayne P. S.
  • 1,558
  • 12
  • 17
0

I advice these questions:

As a summary, IntelliJ, Eclipse, VStudio are just IDEs, so they are not an option for deployment in real environments environments.

If you are talking about external deployment, you need a kind of site to store your docker images and at minimum a continuous integration server(Jenkins, Travis, Bamboo, Circle CI, buddy.works)

Basic Flow

  • Architect, Sysadmin, senior developer or something with infrastructure skills, must create the Dockerfile an other required files.
  • Developer does not need to worry about docker, volumes, ports, etc. Developer only needs to develop code(java in your case).
  • Developer perform a git push
  • Your continuous integration server detects this event and start docker build....
  • After docker build, the continuous integration server, push the new created docker image to you Docker Hub Repository
  • Using some configurations, your continuous integration server knows where the deployment is required (external deployment as you say). Example could be the next classic environment : testing or staging. In this case deployment is just the download of the requested docker image.
  • If Quality Assurance team and automated tests, ensure that everything is fine, your continuous integration server, performs last step: deploy docker image in your production environment, or as I said, just the docker image download.

Your questions

is the scripting of the Docker file essentially a manual process?

As I explained, Dockerfile is cornerstone of all. It's creation is manually and funny or challenging if you need a surgery or your are an artisan like :

  • tomcat user configuration at container start
  • tomcat advance variables configuration
  • any advanced tomcat configuration in which a human is required, but you want to automate it.

Java war inside a tomcat/webapps is a very common requirement, so you will find a lot of Dockerfiles or you could use the generated by your IntelliJ if it meets your requirements.

Fell free to contact me if you don't find a Dockerfile for you java app.

JRichardsz
  • 14,356
  • 6
  • 59
  • 94
  • Thanks for your reply. Just to clarify- it's not that I lack the mental capacity to write a Docker file (hopefully!) if need be, but more that I wanted to understand if was generally a manual process. It might also be that deploying a war to a ready-made PaaS platform is the way I end up going-- again, I just wanted to see what approach people did generally take with Docker if I decide to go down that route. – Neil Coffey Sep 08 '20 at 21:14
  • Unfortunately, it will be me doing this. So I'm also more interested in the technical aspects of the process than "if somebody else did it instead, what spurious job title would you give them" ;-) – Neil Coffey Sep 08 '20 at 21:15