Search code examples
sbtsbt-assemblysbt-native-packager

SBT: How to Dockerize a fat jar?


I'm building a Docker image with a fat jar. I use the sbt-assembly plugin to build the jar, and the sbt-native-packager to build the Docker image. I'm not very familiar with SBT and am running into the following issues.

  1. I'd like to declare a dependency on the assembly task from the docker:publish task, such that the fat jar is created before it's added to the image. I did as instructed in the doc, but it's not working. assembly doesn't run until I invoke it.

    publish := (publish dependsOn assembly).value

  2. One of the steps in building the image is copying the fat jar. Since assembly plugin creates the jar in target/scala_whatever/projectname-assembly-X.X.X.jar, I need to know the exact scala_whatever and the jar name. assembly seems to have a key assemblyJarName but I'm not sure how to access it. I tried the following which fails.

    Cmd("COPY", "target/scala*/*.jar /app.jar")

Help!


Solution

  • Answering my own questions, the following works:

    enablePlugins(JavaAppPackaging, DockerPlugin)
    
    assemblyMergeStrategy in assembly := {
      case x => {
        val oldStrategy = (assemblyMergeStrategy in assembly).value
        val strategy = oldStrategy(x)
        if (strategy == MergeStrategy.deduplicate)
          MergeStrategy.first
        else strategy
      }
    }
    
    // Remove all jar mappings in universal and append the fat jar
    mappings in Universal := {
      val universalMappings = (mappings in Universal).value
      val fatJar = (assembly in Compile).value
      val filtered = universalMappings.filter {
        case (file, name) => !name.endsWith(".jar")
      }
      filtered :+ (fatJar -> ("lib/" + fatJar.getName))
    }
    
    dockerRepository := Some("username")
    
    import com.typesafe.sbt.packager.docker.{Cmd, ExecCmd}
    dockerCommands := Seq(
      Cmd("FROM", "username/spark:2.1.0"),
      Cmd("WORKDIR", "/"),
      Cmd("COPY", "opt/docker/lib/*.jar", "/app.jar"),
      ExecCmd("ENTRYPOINT", "/opt/spark/bin/spark-submit", "/app.jar")
    )
    

    I completely overwrite the docker commands because the defaults add couple of scripts that I don't need because I overwrite the entrypoint as well. Also, the default workdir is /opt/docker which is not where I want to put the fat jar. Note that the default commands are shown by show dockerCommands in sbt console.