Minimal example Ktor project with native
platform and jvm
targets and docker images.
Built using:
There are 6 source sets in the src/
directory.
nativeMain
and jvmMain
contain platform specific code for the jvm and platform specific native targets.
All code shared between targets is in commonMain
.
*Test
sources exist for each *Main
source set.
See https://kotlinlang.org/docs/multiplatform-hierarchy.html` for more information.
module.kt
in commonMain
implements an example Ktor module.
It defines 4 routes:
GET /
serves html templated using the HTML DSL.GET /health
empty response with status code200 OK
.GET /platform
responds with a simple json containing eitherNative
orJVM
depending on the platform. Uses Ktor content negotiation and serialization.POST /hash
responds with theSHA3_256
hash of the request body.
Main.kt
in nativeMain
and jvmMain
contains the configuration of the embeddedServer
for each platform.
They both use the CIO
engine, but the JVM engine can be swapped
with other supported engines.
Compile and run the native binary for the current platform:
./gradlew compileKotlinPlatform
./gradlew runReleaseExecutablePlatform
Build and run the uber/shadow Jar:
./gradlew shadowJar
./gradlew runShadow
Execute Tests:
./gradlew allTests
./gradlew jvmTest
./gradlew platformTest
With the --platform linux/amd64
flag, we can build images for x86 targets on ARM.
To build the native image we (once) build a build image. This is very large (~2.8GB) and will download platform specific kotlin native dependencies. Building a separate image to cache these dependencies makes subsequent builds much faster. The build image only has to be updated when dependencies change.
docker buildx build --platform linux/amd64 -f NativeBuild.Dockerfile -t ktor-native:build .
The actual ktor-native
image can then be built:
docker buildx build --platform linux/amd64 -f NativeAlpine.Dockerfile -t ktor-native:alpine .
docker buildx build --platform linux/amd64 -f NativeDebian.Dockerfile -t ktor-native:debian .
docker buildx build --platform linux/amd64 -f NativeUbuntu.Dockerfile -t ktor-native:ubuntu .
and then run with:
docker run --platform linux/amd64 -p 8080:8080 --rm ktor-native:alpine
docker run --platform linux/amd64 -p 8080:8080 --rm ktor-native:debian
docker run --platform linux/amd64 -p 8080:8080 --rm ktor-native:ubuntu
For comparison a JVM image is built using:
docker buildx build --platform linux/amd64 -f JVMAlpine.Dockerfile -t ktor-native:jvm-alpine .
and then run with:
docker run --platform linux/amd64 -p 8080:8080 --rm ktor-native:jvm-alpine
-
The "native target" is called
platform
and notnative
like described in the Ktor native server docs, asnative
leads to this warning:The Default Kotlin Hierarchy Template was not applied to 'root project 'ktor-native-docker'': Source sets created by the following targets will clash with source sets created by the template: [native]
Consider renaming the targets or disabling the default template by adding 'kotlin.mpp.applyDefaultHierarchyTemplate=false' to your gradle.properties
Learn more about hierarchy templates: https://kotl.in/hierarchy-template
-
Calling
./gradlew run
fails withError: Could not find or load main class MainKt
. This currently only allows the jvm application to be run using the shadowJar built for the jvm target. Some configuration is missing on how to configure the main class from the jvmMain source set for the application. -
(On Mac) We have to compile the native binary in an x86 linux container. On a
linux/arm64
build without the--platform linux/amd64
flag, we getCould not find kotlin-native-prebuilt-2.0.0-linux-aarch64.tar.gz
. Even thoughlinuxArm64
is a supported tier 2 native target.