├── .gitignore ├── .scalafmt.conf ├── README.md ├── build.sbt ├── core └── src │ └── main │ └── scala │ └── com │ └── softwaremill │ └── graalvm │ └── Hello.scala ├── graalvm-native-image ├── Dockerfile └── build.sh ├── project ├── build.properties └── plugins.sbt ├── run-native-image └── Dockerfile └── version.sbt /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | *.log 3 | 4 | .cache 5 | .history 6 | .lib/ 7 | dist/* 8 | target/ 9 | lib_managed/ 10 | src_managed/ 11 | project/boot/ 12 | project/plugins/project/ 13 | 14 | .idea* 15 | -------------------------------------------------------------------------------- /.scalafmt.conf: -------------------------------------------------------------------------------- 1 | version=2.0.0-RC7 2 | maxColumn = 140 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Docker + GraalVM native-image 2 | 3 | 1. create locally the `graalvm-native-image` container using `graalvm-native-image/build.sh`. This container will be used to build the native image. 4 | 2. run sbt 5 | 3. run in sbt: `dockerGraalvmNative`: this will generate the `docker-graalvm-native-test` container 6 | 4. run in sbt: `docker:publishLocal`: this will generate the `docker-test` continer 7 | 5. run in shell: `time docker run --rm docker-graalvm-native-test` 8 | 6. run in shell: `time docker run --rm docker-test` and compare the timing 9 | 7. run `docker images | grep docker-` and compare the image size -------------------------------------------------------------------------------- /build.sbt: -------------------------------------------------------------------------------- 1 | import java.nio.file.{Files, StandardCopyOption} 2 | 3 | val dockerGraalvmNative = taskKey[Unit]("Create a docker image containing a binary build with GraalVM's native-image.") 4 | val dockerGraalvmNativeImageName = settingKey[String]("Name of the generated docker image, containing the native binary.") 5 | 6 | lazy val commonSettings = commonSmlBuildSettings ++ Seq( 7 | organization := "com.softwaremill.graalvm", 8 | scalaVersion := "2.12.8" 9 | ) 10 | 11 | lazy val rootProject = (project in file(".")) 12 | .settings(commonSettings: _*) 13 | .settings(publishArtifact := false, name := "graalvm-tests") 14 | .aggregate(core) 15 | 16 | lazy val core: Project = (project in file("core")) 17 | .settings(commonSettings: _*) 18 | .enablePlugins(DockerPlugin) 19 | .enablePlugins(JavaServerAppPackaging) 20 | .settings( 21 | name := "core", 22 | packageName in Docker := "docker-test", 23 | dockerUpdateLatest := true, 24 | dockerGraalvmNativeImageName := "docker-graalvm-native-test", 25 | dockerGraalvmNative := { 26 | val log = streams.value.log 27 | 28 | val stageDir = target.value / "native-docker" / "stage" 29 | stageDir.mkdirs() 30 | 31 | // copy all jars to the staging directory 32 | val cpDir = stageDir / "cp" 33 | cpDir.mkdirs() 34 | 35 | val classpathJars = Seq((packageBin in Compile).value) ++ (dependencyClasspath in Compile).value.map(_.data) 36 | classpathJars.foreach(cpJar => Files.copy(cpJar.toPath, (cpDir / cpJar.name).toPath, StandardCopyOption.REPLACE_EXISTING)) 37 | 38 | val resultDir = stageDir / "result" 39 | resultDir.mkdirs() 40 | val resultName = "out" 41 | 42 | val className = (mainClass in Compile).value.getOrElse(sys.error("Could not find a main class.")) 43 | 44 | val runNativeImageCommand = Seq( 45 | "docker", 46 | "run", 47 | "--rm", 48 | "-v", 49 | s"${cpDir.getAbsolutePath}:/opt/cp", 50 | "-v", 51 | s"${resultDir.getAbsolutePath}:/opt/graalvm", 52 | "graalvm-native-image", 53 | "-cp", 54 | "/opt/cp/*", 55 | "--static", 56 | // "--report-unsupported-elements-at-runtime", 57 | s"-H:Name=$resultName", 58 | className 59 | ) 60 | 61 | log.info("Running native-image using the 'graalvm-native-image' docker container") 62 | log.info(s"Running: ${runNativeImageCommand.mkString(" ")}") 63 | 64 | sys.process.Process(runNativeImageCommand, resultDir) ! streams.value.log match { 65 | case 0 => resultDir / resultName 66 | case r => sys.error(s"Failed to run docker, exit status: " + r) 67 | } 68 | 69 | val buildContainerCommand = Seq( 70 | "docker", 71 | "build", 72 | "-t", 73 | dockerGraalvmNativeImageName.value, 74 | "-f", 75 | (baseDirectory.value.getParentFile / "run-native-image" / "Dockerfile").getAbsolutePath, 76 | resultDir.absolutePath 77 | ) 78 | log.info("Building the container with the generated native image") 79 | log.info(s"Running: ${buildContainerCommand.mkString(" ")}") 80 | 81 | sys.process.Process(buildContainerCommand, resultDir) ! streams.value.log match { 82 | case 0 => resultDir / resultName 83 | case r => sys.error(s"Failed to run docker, exit status: " + r) 84 | } 85 | 86 | log.info(s"Build image ${dockerGraalvmNativeImageName.value}") 87 | }, 88 | graalVMNativeImageOptions := Seq("--report-unsupported-elements-at-runtime"), 89 | libraryDependencies ++= Seq( 90 | "io.monix" %% "monix" % "3.0.0-RC2" 91 | ) 92 | ) 93 | -------------------------------------------------------------------------------- /core/src/main/scala/com/softwaremill/graalvm/Hello.scala: -------------------------------------------------------------------------------- 1 | package com.softwaremill.graalvm 2 | 3 | import monix.eval.Task 4 | import monix.execution.Scheduler.Implicits.global 5 | 6 | object Hello extends App { 7 | val task = Task(println(s"Hello, world from thread: ${Thread.currentThread().getName}!")) 8 | (for { 9 | fiber1 <- task.start 10 | _ <- task 11 | _ <- fiber1.join 12 | } yield ()).runSyncUnsafe() 13 | } 14 | -------------------------------------------------------------------------------- /graalvm-native-image/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM oracle/graalvm-ce:19.0.0 2 | WORKDIR /opt/graalvm 3 | RUN gu install native-image 4 | ENTRYPOINT ["native-image"] -------------------------------------------------------------------------------- /graalvm-native-image/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | docker build -t graalvm-native-image . -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.2.8 2 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("com.softwaremill.sbt-softwaremill" % "sbt-softwaremill" % "1.6.1") 2 | addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.3.21") -------------------------------------------------------------------------------- /run-native-image/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.9.4 2 | COPY out /opt/docker/out 3 | RUN chmod +x /opt/docker/out 4 | CMD ["/opt/docker/out"] -------------------------------------------------------------------------------- /version.sbt: -------------------------------------------------------------------------------- 1 | version in ThisBuild := "0.1-SNAPSHOT" 2 | --------------------------------------------------------------------------------