Ktor 3.0.1 Help

Docker

In this section, we'll see how to use the Ktor Gradle plugin for packaging, running, and deploying applications using Docker.

Install the Ktor plugin

To install the Ktor plugin, add it to the plugins block of your build.gradle.(kts) file:

plugins { id("io.ktor.plugin") version "3.0.1" }
plugins { id "io.ktor.plugin" version "3.0.1" }

Plugin tasks

After installing the plugin, the following tasks are available for packaging, running, and deploying applications:

  • buildImage: builds a project's Docker image to a tarball. This task generates a jib-image.tar file in the build directory. You can load this image to a Docker daemon using the docker load command:

    docker load < build/jib-image.tar
  • publishImageToLocalRegistry: builds and publishes a project's Docker image to a local registry.

  • runDocker: builds a project's image to a Docker daemon and runs it. Executing this task will launch the Ktor server, responding on http://0.0.0.0:8080 by default. If your server is configured to use another port, you can adjust port mapping.

  • publishImage: builds and publishes a project's Docker image to an external registry such as Docker Hub or Google Container Registry. Note that you need to configure the external registry using the ktor.docker.externalRegistry property for this task.

Note that by default, these tasks build the image with the ktor-docker-image name and latest tag. You can customize these values in the plugin configuration.

Configure the Ktor plugin

To configure the Ktor plugin settings related to Docker tasks, use the ktor.docker extension in your build.gradle.(kts) file:

ktor { docker { // ... } }

JRE version

The jreVersion property specifies the JRE version to use in the image:

ktor { docker { jreVersion.set(JavaVersion.VERSION_17) } }

Image name and tag

If you need to customize the image name and tag, use the localImageName and imageTag properties, respectively:

ktor { docker { localImageName.set("sample-docker-image") imageTag.set("0.0.1-preview") } }

Port mapping

By default, the runDocker task publishes the 8080 container port to the 8080 Docker host port. If required, you can change these ports using the portMappings property. This might be useful if your server is configured to use another port.

The example below shows how to map the 8080 container port to the 80 Docker host port.

ktor { docker { portMappings.set(listOf( io.ktor.plugin.features.DockerPortMapping( 80, 8080, io.ktor.plugin.features.DockerPortMappingProtocol.TCP ) )) } }

In this case, you can access the server on http://0.0.0.0:80.

External registry

Before publishing a project's Docker image to an external registry using the publishImage task, you need to configure the external registry using the ktor.docker.externalRegistry property. This property accepts the DockerImageRegistry instance, which provides configuration for the required registry type:

  • DockerImageRegistry.dockerHub: creates a DockerImageRegistry for Docker Hub.

  • DockerImageRegistry.googleContainerRegistry: creates a DockerImageRegistry for Google Container Registry.

The example below shows how to configure the Docker Hub registry:

ktor { docker { externalRegistry.set( io.ktor.plugin.features.DockerImageRegistry.dockerHub( appName = provider { "ktor-app" }, username = providers.environmentVariable("DOCKER_HUB_USERNAME"), password = providers.environmentVariable("DOCKER_HUB_PASSWORD") ) ) } }

Note that the Docker Hub name and password are fetched from the environment variables, so you need to set these values before running the publishImage task:

export DOCKER_HUB_USERNAME=yourHubUsername export DOCKER_HUB_PASSWORD=yourHubPassword
setx DOCKER_HUB_USERNAME yourHubUsername setx DOCKER_HUB_PASSWORD yourHubPassword

Manual image configuration

If required, you can provide your own Dockerfile to assemble an image with a Ktor application.

Package the application

As a first step, you need to package your application along with its dependencies. For example, this might be a fat JAR or an executable JVM application.

Prepare Docker image

To dockerize the application, we'll use multi-stage builds:

  1. First, we'll set up caching for Gradle/Maven dependencies. This step is optional, but recommended as it improves the overall build speed.

  2. Then, we'll use the gradle/maven image to generate a fat JAR with the application.

  3. Finally, the generated distribution will be run in the environment created based on the JDK image.

In the root folder of the project, create a file named Dockerfile with the following contents:

# Stage 1: Cache Gradle dependencies FROM gradle:latest AS cache RUN mkdir -p /home/gradle/cache_home ENV GRADLE_USER_HOME /home/gradle/cache_home COPY build.gradle.* gradle.properties /home/gradle/app/ WORKDIR /home/gradle/app RUN gradle clean build -i --stacktrace # Stage 2: Build Application FROM gradle:latest AS build COPY --from=cache /home/gradle/cache_home /home/gradle/.gradle COPY . /usr/src/app/ WORKDIR /usr/src/app COPY --chown=gradle:gradle . /home/gradle/src WORKDIR /home/gradle/src # Build the fat JAR, Gradle also supports shadow # and boot JAR by default. RUN gradle buildFatJar --no-daemon # Stage 3: Create the Runtime Image FROM amazoncorretto:22 AS runtime EXPOSE 8080:8080 RUN mkdir /app COPY --from=build /home/gradle/src/build/libs/*.jar /app/ktor-docker-sample.jar ENTRYPOINT ["java","-jar","/app/ktor-docker-sample.jar"]
# Stage 1: Cache Maven dependencies FROM maven:3.8-amazoncorretto-21 AS cache WORKDIR /app COPY pom.xml . RUN mvn dependency:go-offline # Stage 2: Build Application FROM maven:3.8-amazoncorretto-21 AS build WORKDIR /app COPY --from=cache /root/.m2 /root/.m2 COPY . . RUN mvn clean package # Stage 3: Create the Runtime Image FROM amazoncorretto:21-slim AS runtime EXPOSE 8080 WORKDIR /app COPY --from=build /app/target/*-with-dependencies.jar app.jar ENTRYPOINT ["java", "-jar", "app.jar"]

The first stage ensures dependencies will be re-downloaded only when there is a change to the build related files. If the first stage is not used, or dependencies are not cached in other stages, dependencies will be installed in every build.

In the second stage the fat JAR is built. Note that Gradle also supports shadow and boot JAR by default.

The third stage of the build works in the following way:

  • Indicates what image is going to be used.

  • Specifies the exposed port (this does not automatically expose the port, which is done when running the container).

  • Copies the contents from the build output to the folder.

  • Runs the application (ENTRYPOINT).

Build and run the Docker image

The next step is to build and tag the Docker image:

docker build -t my-application .

Finally, start the image:

docker run -p 8080:8080 my-application

This will launch the Ktor server, responding on https://0.0.0.0:8080.

Last modified: 11 September 2024