Vert.x Docker Images

Execute Vert.x applications in Docker containers.

Introduction

Docker lets you deploy applications inside lightweight and isolated software containers. Applications run side by side in isolated Linux containers. If you never used Docker before check this online tutorial.

Vert.x provides Docker images that you can use to run your applications. Two Docker images are provided:

  • vertx/vertx3 is the base image you need to extend to run your own application

  • vertx/vertx3-exec is providing the vertx command to your system without having to install vert.x yourself.

The images are available from Docker Hub.

This guide presents how to use these two images but also how to automate Docker image creation using Maven, generate Fabric8 metadata and use fat jars.

The base image

The base image (vertx/vertx3) lets you run your vert.x application in a Docker container. For this, you must extend the image to deploy your own application - i.e. create your own Dockerfile inheriting from vertx/vertx3. Your application is then launched using the vertx command (but in the container).

Deploying a JavaScript verticle in a docker container

Let’s start with a simple JavaScript verticle such as:

hello-verticle.js
vertx.createHttpServer().requestHandler(function (request) {
    request.response().end("Hello world");
}).listen(8080);

Create in the same directory a Dockerfile with the following content:

Dockerfile
# Extend vert.x image                       (1)
FROM vertx/vertx3

# Set the name of the verticle to deploy    (2)
ENV VERTICLE_NAME hello-verticle.js

# Set the location of the verticles         (3)
ENV VERTICLE_HOME /usr/verticles

EXPOSE 8080

# Copy your verticle to the container       (4)
COPY $VERTICLE_NAME $VERTICLE_HOME/

# Launch the verticle                       (5)
WORKDIR $VERTICLE_HOME
ENTRYPOINT ["sh", "-c"]
CMD ["exec vertx run $VERTICLE_NAME -cp $VERTICLE_HOME/*"]
  1. First extend the image provided by vert.x

  2. Set the name of the verticle

  3. It sets the location in the container where the verticle file is placed

  4. Copy the verticle files

  5. Instructions to launch the verticle

Then do build the image, just launch:

docker build -t sample/vertx-javascript .

Then, run it with:

docker run -t -i -p 8080:8080 sample/vertx-javascript

To see the result, open a browser to http://localhost:8080 or http://192.168.59.103:8080 if your are using boot2docker.

Note
The container is launched with the '-t -i` flags meaning interactive. Stop the container by hitting CTRL+C . More details about the Docker run command are available here.

You may have noticed the EXPOSE 8080 in the Dockerfile and the -p 8080:8080 in the run command. The first one is an optional information telling that the application wants to listen the port 8080. The second one is mandatory and instructs docker to forward the port 8080 from the host to the 8080 of the container.

Note
You may also have noticed the convoluted way to launch the application. Instead of calling vertx directly, it uses sh -c along with exec. sh -c is to turn around a Docker limitation not expanding variables in CMD. This way the launched shell does. More details on the Docker builder documentation. exec is to make the vertx command process replace the shell, so that it gets pid 1 and receives signals, like SIGTERM when running docker stop. Without exec the shell keeps running along with the vertx command process, and the vertx command does not get signals, thus preventing graceful shutdown.

Deploying a Groovy verticle in a docker container

Running a groovy verticle in a docker container is not much different. Instead of the hello-verticle.js from the previous example, we now have a hello-verticle.groovy:

hello-verticle.groovy
vertx.createHttpServer().requestHandler({ request ->
    request.response().end("Groovy world")
}).listen(8080)

So, in the Dockerfile we just change the verticle file name:

Dockerfile
# Extend vert.x image
FROM vertx/vertx3

# Set the name of the verticle to deploy
ENV VERTICLE_NAME hello-verticle.groovy

# Set the location of the verticles
ENV VERTICLE_HOME /usr/verticles

EXPOSE 8080

# Copy your verticle to the container
COPY $VERTICLE_NAME $VERTICLE_HOME/

# Launch the verticle
WORKDIR $VERTICLE_HOME
ENTRYPOINT ["sh", "-c"]
CMD ["exec vertx run $VERTICLE_NAME -cp $VERTICLE_HOME/*"]

The build and run instructions are very close to the previous ones:

> docker build -t sample/vertx-groovy .
....
> docker run -t -i -p 8080:8080 sample/vertx-groovy

Deploying a Ruby verticle in a docker container

Running a groovy verticle in a docker container is not much different. Instead of the hello-verticle.js/groovy from the previous examples, we now have a hello-verticle.rb:

hello-verticle.rb
$vertx.create_http_server().request_handler() { |request|
    request.response().end("A ruby world full of gems")
}.listen(8080)

So, in the Dockerfile we just change the verticle file name:

Dockerfile
# Extend vert.x image
FROM vertx/vertx3

# Set the name of the verticle to deploy
ENV VERTICLE_NAME hello-verticle.rb

# Set the location of the verticles
ENV VERTICLE_HOME /usr/verticles

EXPOSE 8080

# Copy your verticle to the container
COPY $VERTICLE_NAME $VERTICLE_HOME/

# Launch the verticle
WORKDIR $VERTICLE_HOME
ENTRYPOINT ["sh", "-c"]
CMD ["exec vertx run $VERTICLE_NAME -cp $VERTICLE_HOME/*"]

The build and run instructions are very close to the previous ones:

> docker build -t sample/vertx-ruby .
....
> docker run -t -i -p 8080:8080 sample/vertx-ruby

Deploying a Java verticle in a docker container

So, now let’s see how to deploy a Java verticle. Again it’s not different from the previous examples, except that we copy the verticle jar file to the container. Let’s take the following verticle:

io.vertx.sample.hello.HelloVerticle
package io.vertx.sample.hello;

import io.vertx.core.AbstractVerticle;

public class HelloVerticle extends AbstractVerticle {

  @Override
  public void start() throws Exception {
    vertx.createHttpServer().requestHandler(request -> {
      request.response().end("Hello Java world");
    }).listen(8080);
  }
}

Let’s now imagine that this verticle is packaged into the target/hello-verticle-1.0.-SNAPSHOT.jar jar file. So the Dockerfile needs to copy this file but also gives to vert.x the verticle class name:

Dockerfile
# Extend vert.x image
FROM vertx/vertx3

#                                                       (1)
ENV VERTICLE_NAME io.vertx.sample.hello.HelloVerticle
ENV VERTICLE_FILE target/hello-verticle-1.0-SNAPSHOT.jar

# Set the location of the verticles
ENV VERTICLE_HOME /usr/verticles

EXPOSE 8080

# Copy your verticle to the container                   (2)
COPY $VERTICLE_FILE $VERTICLE_HOME/

# Launch the verticle
WORKDIR $VERTICLE_HOME
ENTRYPOINT ["sh", "-c"]
CMD ["exec vertx run $VERTICLE_NAME -cp $VERTICLE_HOME/*"]
  1. Unlike the previous examples, here we set the verticle class name and the jar file

  2. The jar file is copied.

Build and run instructions do not change:

> docker build -t sample/vertx-java .
....
> docker run -t -i -p 8080:8080 sample/vertx-java

Configuration

The previous `Dockerfile`s did not configure vert.x. Let’s see how such configuration can be added.

Configuring the Java Virtual Machine

You can configure the Java Virtual Machine using the JAVA_OPTS environment variable. So in the Dockerfile adds:

ENV JAVA_OPTS "-Dfoo=bar"

VERTX_OPTS

System variables specific to vert.x can be configured using the VERTX_OPTS environment variable:

ENV VERTX_OPTS "-Dvertx.options.eventLoopPoolSize=26 -Dvertx.options.deployment.worker=true"

Classpath

You can configure the classpath of the application using either the -cp parameter of the vert.x command or the CLASSPATH environment variable:

ENV CLASSPATH "/usr/verticles/libs/foo.jar:/usr/verticles/libs/bar.jar:"

Logging

To configure the logging.properties file (that let you customize JUL loggers`), set the VERTX_JUL_CONFIG environment variables:

COPY ./logging.properties $VERTICLE_HOME/                       (1)
ENV VERTX_JUL_CONFIG $VERTICLE_HOME/logging.properties          (2)
  1. Copy your logging.properties file

  2. Set the VERTX_JUL_CONFIG environment variable

Clustering

You can provide your own cluster.xml file, and add it to the classpath. To build a dynamic classpath from all the file contained in $VERTICLE_HOME you can use:

COPY ./cluster.xml $VERTICLE_HOME/
# ...
CMD [export CLASSPATH=`find $VERTICLE_HOME -printf '%p:' | sed 's/:$//'`; exec vertx run $VERTICLE_NAME"]

Notice the export CLASSPATH=…​; part in the CMD instruction. It builds the value of the CLASSPATH variable from the content of the $VERTICLE_HOME directory. This tricks is useful to compute large and dynamic classpath.

Build Docker Images with Maven

There are a couple of Maven plugins to build your Docker images during your Maven build process. This example uses the docker-maven-plugin from Spotify.

First, create your Java project as usual. So your sources are located in src/main/java…​ Then create a src/main/docker directory and create a Dockerfile inside:

.
├── pom.xml
├── src
│   └── main
│       ├── docker
│       │   └── Dockerfile
│       └── java
│           └── io
│               └── vertx
│                   └── example
│                       └── HelloWorldVerticle.java
├── target

In the pom.xml file add the following plugin configuration

<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>0.2.8</version>
<executions>
  <execution>
    <id>docker</id>
    <phase>package</phase>
    <goals>
      <goal>build</goal>
    </goals>
  </execution>
</executions>
<configuration>
  <dockerDirectory>${project.basedir}/src/main/docker</dockerDirectory>
  <!-- Configure the image name -->
  <imageName>sample/vertx-hello</imageName>
  <resources>
    <resource>
      <targetPath>/verticles</targetPath>
      <directory>${project.build.directory}</directory>
      <includes>
        <include>${project.artifactId}-${project.version}.jar</include>
      </includes>
    </resource>
    <!-- don't forget to also add all the dependencies required by your application -->
  </resources>
</configuration>
</plugin>

The plugin copies the listed content into target/docker. Each resource is copied into the set targetPath. So edit the src/main/docker/Dockerfile and add the following content:

FROM vertx/vertx3

ENV VERTICLE_HOME /usr/verticles
ENV VERTICLE_NAME io.vertx.example.HelloWorldVerticle

COPY ./verticles $VERTICLE_HOME

ENTRYPOINT ["sh", "-c"]
CMD ["exec vertx run $VERTICLE_NAME -cp $VERTICLE_HOME/*"]

It’s basically the same content as we saw above. The copy is a bit different as the plugin have placed files in the same directory as the Dockerfile.

Once configured the image is built using: mvn clean package

Build Docker Images for Fabric 8

Fabric 8 is an open source set of micro-services that run on top of Kubernetes and OpenShift V3 to provide management, continuous delivery and iPaas facilities. You can execute vert.x application on top of Fabric 8 by packaging them into a Docker image. However, additional metadata is required. In this example, we are going to use the docker-maven-plugin from Roland Huß.

Let’s set up the following structure:

.
├── pom.xml
├── src
│   └── main
│       ├── docker
│       │   └── assembly.xml
│       └── java
│           └── io
│               └── vertx
│                   └── example
│                       └── HelloWorldVerticle.java
└── target

Unlike the maven plugin from Spotify, this plugin takes an assembly.xml as input. The file lists all the files that need to be copied to the docker container such as:

 <assembly>
   <dependencySets>
     <dependencySet>
       <includes>
         <include>:${project.artifactId}</include>
       </includes>
       <outputDirectory>.</outputDirectory>
     </dependencySet>
   </dependencySets>
 </assembly>

The rest of the Dockerfile configuration is given in the `pom.xml file. Add the following plugin to the pom.xml file:

 <plugin>
  <groupId>org.jolokia</groupId>
  <artifactId>docker-maven-plugin</artifactId>
  <version>0.11.5</version>
  <executions>
    <execution>
      <id>build</id>
      <phase>package</phase>
      <goals>
        <goal>build</goal>
      </goals>
    </execution>
  </executions>
  <configuration>
    <images>
      <image>
        <name>${docker.image}</name>
        <build>
          <from>vertx/vertx3</from>
          <tags>
            <tag>${project.version}</tag>
          </tags>
          <ports>
            <port>8080</port>
          </ports>
          <command>vertx run io.vertx.example.HelloWorldVerticle -cp
            /usr/verticles/${project.artifactId}-${project.version}.jar
          </command>
          <assembly>
            <mode>dir</mode>
            <basedir>/usr/verticles</basedir>
            <descriptor>assembly.xml</descriptor>
          </assembly>
        </build>
      </image>
    </images>
  </configuration>
 </plugin>

To configure the container more finely, check the manual. All the instructions we put in the Dockerfile can be set from the plugin.

Note
The previous pom.xml file use a properties called docker.image setting the image name. Don’t forget to set it in your pom.xml` file.

Once you have this configuration in place, we need a second plugin to generate the metadata required by Fabric8:

<plugin>
<groupId>io.fabric8</groupId>
<artifactId>fabric8-maven-plugin</artifactId>
<version>2.1.4</version>
<executions>
  <execution>
    <id>json</id>
    <phase>generate-resources</phase>
    <goals>
      <goal>json</goal>
    </goals>
  </execution>
  <execution>
    <id>attach</id>
    <phase>package</phase>
    <goals>
      <goal>attach</goal>
    </goals>
  </execution>
</executions>
</plugin>

Once set up, you can build your docker image with: mvn clean package. It creates the kubernates.json file required by Fabric8. Then push your image on the Docker Registry provided by Fabric8:

docker push $DOCKER_REGISTRY/sample/vertx-hello

Don’t forget to set the DOCKER_REGISTRY url to point on the registry managed by Fabric8. The last step is to apply it with:

mvn io.fabric8:fabric8-maven-plugin:2.1.4:apply

The executable image

The vertx/vertx3-exec image provides the vertx command to the host. So no need to install vert.x on your machine, you can just use this docker image.

For instance:

> docker run -i -t vertx/vertx3-exec -version
3.6.2

To run a verticle:

docker run -i -t -p 8080:8080 \
    -v $PWD:/verticles vertx/vertx3-exec \
    run io.vertx.sample.RandomGeneratorVerticle \
    -cp /verticles/MY_VERTICLE.jar

This command mounts the current directory (PWD) into /verticles and then launch the vertx run command. Notice the -cp parameter reusing the /verticles directory.

Customizing the stack

The vertx/vertx3-exec image provides the default "full" Vert.x stack. You may want to customize this stack and create your own exec image. First, create a vertx-stack.json file:

{
  "variables": {
    "vertx.version": "3.3.3"
  },
  "dependencies": [
    {
      "groupId": "io.vertx",
      "artifactId": "vertx-web",
      "version": "${vertx.version}",
      "included": true
    },
    {
      "groupId": "io.vertx",
      "artifactId": "vertx-lang-js",
      "version": "${vertx.version}",
      "included": true
    }
  ]
}

You can list any dependency you need, not just the Vert.x artifacts (refer to the Stack Manager documentation for details).

Then write a Dockerfile for your custom executable image:

FROM vertx/vertx3-exec                                     (1)

COPY vertx-stack.json ${VERTX_HOME}/vertx-stack.json       (2)

RUN vertx resolve && rm -rf ${HOME}/.m2                    (3)
  1. Extend the Vert.x executable image

  2. Replace the stack file in the Vert.x command installation

  3. Resolve dependencies

You should know be able to build your custom executable image:

docker build -t mycompany/my-vertx3-exec .

And run your verticle:

docker run -i -t -p 8080:8080 \
    -v $PWD:/verticles mycompany/my-vertx3-exec \
    run io.vertx.sample.RandomGeneratorVerticle \
    -cp /verticles/MY_VERTICLE.jar

Deploying a fat jar

It is also possible to deploy a Vert.x application packaged as a fat jar into a docker container. For this you don’t need the images provided by Vert.x, you can directly use a base Java image. Let’s have a look.

First, be sure your application is packaged as a fat jar. Then, use the following Dockerfile:

FROM openjdk:8-jre-alpine                                           (1)

ENV VERTICLE_FILE hello-verticle-fatjar-3.0.0-SNAPSHOT-fat.jar      (2)

# Set the location of the verticles
ENV VERTICLE_HOME /usr/verticles

EXPOSE 8080

# Copy your fat jar to the container
COPY target/$VERTICLE_FILE $VERTICLE_HOME/                          (3)

# Launch the verticle
WORKDIR $VERTICLE_HOME
ENTRYPOINT ["sh", "-c"]
CMD ["exec java -jar $VERTICLE_FILE"]                               (4)
  1. Extend the image providing OpenJDK 8, use the one you want

  2. Set the VERTICLE_FILE to point on the fat jar

  3. Copy the Fat jar from target. Change it if you don’t use Maven.

  4. Launch the application using the java executable (instead of vertx

It’s basically the same Dockerfile as before. However, this time we extend java:8 instead of the vertx/vertx3 image. Then we copy the fat jar to the container, and launch it with the java executable. All the configuration settings presented above are still valid.

Build and run the container with:

> docker build -t sample/vertx-java-fat .
....
> docker run -t -i -p 8080:8080 sample/vertx-java-fat