In this post, we’ll walk through the configuration of the Ktor plugin for deploying a Ktor Docker image to Container Registry via a generated Gradle task. We’ll also explore several other Gradle tasks that make working with a containerized version of our Ktor service easier.
This post is part of a series on managing Ktor service deployments using Gradle:
- How to Publish a Ktor Docker Image to Artifact Registry Using the Ktor Plugin
- Deploy a Ktor Service Docker Image from Artifact Registry to Cloud Run Using Gradle
Ktor is a framework from JetBrains enabling developers to build client and/or server applications using Kotlin. For organizations wanting to use Kotlin for server-side development, Ktor is an appealing choice of framework as it’s built from the ground up to leverage Kotlin language features such as coroutines.
In many organizations today, especially those undergoing “cloud transformation” through adoption of serverless technologies, containers are a popular choice for packaging, sharing, and deploying applications. Those containers can be used with tools like Docker to simplify the development, testing, and coordination of services.
Containers are a key aspect of many serverless deployment workflows within Google Cloud Platform. Servless solutions like Cloud Run rely on containers to respond quickly to increases in server load by deploying new containers, when needed, to handle the incoming traffic, and then spin down those containers when traffic subsides.
If using Ktor for modern serverless development, it’s likely you’ll need to create a container image for your Ktor application and be able to deploy that image to some external repository where it may then be deployed and start receiving traffic.
It’s possible to do this in an entirely manual fashion; defining our own Dockerfile, building the container locally, and then pushing it to some external registry such as Google’s Container Registry.
However, the Ktor plugin provides mechanisms for simplifying this process in a way that makes it easy for those without extensive Docker, container, or GCP knowledge to leverage.
If you need to publish your Ktor Docker image to Artifact Registry, check out this related post.
Prerequisites
Since we’ll be deploying our Ktor service container to Container Registry, that comes with a new Google Cloud Platform (GCP) prerequisites.
- We need to have access to a GCP organization
- How to setup a GCP organization?
- You’ll eventually need to set up a billing account for your organization. To try out GCP for learning purposes, there are often ways to find free, or discounted, billing credits
- We need to have a project set up within that organization
- Container Registry must be enabled within that project
How to configure the Ktor plugin to build a Docker image?
To configure a Docker image, and publishing tasks, for our Ktor project, we’ll use the Ktor plugin dsl in our project’s Gradle buildscript. This will leverage the jib Gradle plugin under the hood to configure and build images for your project.
// build.gradle.kts ktor { docker { // container configuration goes here } }
To start, we’ll specify a Java Runtime Environment (JRE) version to use when constructing the container image.
ktor { docker { // set a JRE version that will be used to build the container jreVersion.set(io.ktor.plugin.features.JreVersion.JRE_17) } }
Next, we’ll configure port mappings for the image.
ktor { docker { // set a JRE version that will be used to build the container jreVersion.set(io.ktor.plugin.features.JreVersion.JRE_17) // setup port mapping for the container/service portMappings.set(listOf( io.ktor.plugin.features.DockerPortMapping( outsideDocker = 8080, insideDocker = 8080, io.ktor.plugin.features.DockerPortMappingProtocol.TCP ) )) } }
And finally, we’ll configure the external registry used when publishing the container. In our example, we’ll configure it to use Container Registry.
ktor { docker { // set a JRE version that will be used to build the container jreVersion.set(io.ktor.plugin.features.JreVersion.JRE_17) // setup port mapping for the container/service portMappings.set(listOf( io.ktor.plugin.features.DockerPortMapping( outsideDocker = 8080, insideDocker = 8080, io.ktor.plugin.features.DockerPortMappingProtocol.TCP ) )) // configure how the container should be deployed to Container Registry externalRegistry.set( DockerImageRegistry.googleContainerRegistry( projectName = provider { "<your gcp project name>" }, appName = provider { "<desired repository name>" }, username = provider { "" }, password = provider { "" } ) ) } }
There are several key pieces of information needed for this Container Registry configuration:
- projectName should be the name of your existing GCP project in which Container Registry has been enabled and where you want to upload the built container
- appName will be used to name the repository within Container Registry. If the repository doesn’t exist, it will be created when the container image is published.
- A reasonable default here could be the name of your Ktor project. You could pull this from the Gradle project using project.name
- username and password we leave blank here. In the next section we’ll discuss how authentication happens when deploying the container image
Once this configuration is applied to the project, we can build our Docker image using the buildImage
Gradle task. Check out this FAQ from the jib Gradle plugin repo for more details on what an equivalent Dockerfile would look like for the generated image.
How to deploy a Ktor Docker image to Container Registry?
With the Ktor Docker configuration applied to the project, we can publish a Docker image for our project using the publishImage
Gradle task.
This task requires some form of authentication against the GCP project we are attempting to access. Specifically, we need access to the following resource url based on our project configuration: gcr.io/<project name>/<app name>
As the Ktor Docker configuration uses the jib plugin under the hood, we can review documentation from jib on how to authenticate with different container registries.
For Container Registry, there straightforward options for authenticating the requests to publish our image:
- Setup Google Cloud Application Default credentials on your machine using an account that has access to necessary GCP project
- Setup gcloud as a credential helper for any Docker-supported registries
To setup auth via application default credentials, run the following terminal command before running the publishImage
task: gcloud auth application-default login
To setup auth via gcloud Docker credential helper, run the following terminal commands before running the publishImage
task:
gcloud auth configure-docker gcr.io
gcloud auth login
Once one of these (or another) authentication methods are applied, running the publishImage
task should successfully publish the built image to Container Registry.
How to deploy a Ktor Docker image to local Docker service?
Using the Ktor Docker configuration, we can also streamline publishing of our project’s Docker image to a local Docker.
- Start Docker on your development machine
- Run the
publishImageToLocalRegistry
Gradle task
How to run the Ktor service container using generated Gradle tasks?
Using the Ktor Docker configuration, we can build an image and start running it with the local Docker daemon by running the runDocker
Gradle task.
Conclusion
The Ktor plugin’s Docker configuration provides a streamlined workflow for developers working with Ktor services via Docker.
By configuring the plugin to publish images to Google Cloud’s Container Registry, developers can publish. This task can be used by all developers, and by CI/CD workflows to provide a consistent deployment workflow.