ship your scala code often and easy with docker
TRANSCRIPT
Ship your Scala code often and easy with Docker
Marcus Lönnberg
[email protected] github.com/marcuslonnberg
@lonnbergm
Agenda• Intro to Docker • Building Docker images with sbt • Manage Docker containers with Scala • Docker.at(SpeedLedger)
Building and shipping Docker images
Machine 1 Dockerfile + files
Machine x
> docker pull myimage > docker run myimage
Registry
> docker build --tag=myimage > docker push myimage
Machine xMachine x
Manually building a Scala application as a Docker image
1. Produce a fat JAR
2. Define a Dockerfile
3. Build a Docker image
0. Current sbt build
name := "fancy-service"organization := "demo"libraryDependencies ++= Seq( "com.typesafe.akka" %% "akka-actor" % "2.3.9", "io.spray" %% "spray-routing" % “1.3.3”, ...)
build.sbt
1. sbt-assembly gives us a fat JARproject/plugins.sbt
> sbt assembly … [info] Packaging out/fancy-service.jar ... … [success] Total time: 6 s
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.13.0")
2. Define a Dockerfile
FROM ubuntu
RUN apt-get update RUN apt-get install -y openjdk-8-jre-headless
COPY fancy-service.jar /app/fancy-service.jar
WORKDIR /app ENTRYPOINT java -jar fancy-service.jar EXPOSE 8080
Dockerfile
2. Define a Dockerfile
FROM ubuntu
RUN apt-get update RUN apt-get install -y openjdk-8
COPY fancy-service.jar
WORKDIR /app ENTRYPOINT java -jar fancy-service.jar EXPOSE 8080
Dockerfile
FROM java:8-jre
WORKDIR /app EXPOSE 8080 ENTRYPOINT java -jar fancy-service.jar
COPY fancy-service.jar /app/fancy-service.jar
good
3. Building a Docker image> docker build -t demo/fancy-service .
Sending build context to Docker daemon Step 0 : FROM java:8-jre ---> 028f36974b77 Step 1 : WORKDIR /app ---> fb8108515091 Step 2 : EXPOSE 8080 ---> 9512be4df795 Step 3 : ENTRYPOINT java -jar fancy-service.jar ---> 839dd75f5a96 Step 4 : COPY fancy-service.jar /app/fancy-service.jar ---> 948eb875c3b1 Removing intermediate container f9eeb9406fc7 Successfully built 948eb875c3b1
sbt-docker
addSbtPlugin("se.marcuslonnberg" % "sbt-docker" % "1.1.0")
project/plugins.sbt
enablePlugins(DockerPlugin)
build.sbt
Dockerfile DSL
def jarFile: File def jarName: Stringnew Dockerfile { from("java:8-jre") workDir("/app") expose(8080) entryPointRaw(s"java -jar $jarName") copy(jarFile, s"/app/$jarName") }
Putting it all together
docker <<= docker.dependsOn(assembly) dockerfile in docker := { val jarFile = (assemblyOutputPath in assembly).value val jarName = name.value + ".jar" new Dockerfile { from("java:8-jre") workDir("/app") expose(8080) entryPointRaw(s"java -jar $jarName") copy(jarFile, s"/app/$jarName") }}
build.sbt
Let’s build an image
> sbt docker … [info] Packaging out/fancy-service.jar ... … [info] Step 0 : FROM java:8-jre [info] ---> 028f36974b77 … [info] Successfully built 30c376822ced [info] Tagging image 30c376822ced with name: demo/fancy-service:0.1-SNAPSHOT [info] Tagging image 30c376822ced with name: demo/fancy-service:latest [success] Total time: 20 s
Image names
imageNames in docker := Seq( ImageName("demo/fancy-service:" + version.value), ImageName("demo/fancy-service:latest") )
build.sbt
Immutable Dockerfile
def jarFile: File def jarName: StringDockerfile.empty .from("java:8-jre") .workDir("/app") .expose(8080) .entryPointRaw(s"java -jar $jarName") .copy(jarFile, s"/app/$jarName")
Build options
buildOptions in docker := BuildOptions( cache = true, removeIntermediateContainers = BuildOptions.Remove.OnSuccess, pullBaseImage = BuildOptions.Pull.Always)
build.sbt
sbt-docker
• Define Dockerfiles with a DSL
• Dynamically name your images
• Push images to a registry
github.com/marcuslonnberg/sbt-docker
Build pipelineDev machine
Build server
Test
Production
Registrygit repo
> git push
> sbt dockerBuildAndPush
> docker pull image
Dev machine
Tagging Docker images with git
imageNames in docker := Seq( ImageName("demo/fancy-service:" + git.gitHeadCommit.value.get), ImageName("demo/fancy-service:" + git.gitCurrentBranch.value))
Labels with build info
val labels = Map( "build.commitId" -> git.gitHeadCommit.value.get, "build.branch" -> git.gitCurrentBranch.value, "build.appName" -> name.value, "build.buildTime" -> Platform.currentTime.toString) new Dockerfile { … label(labels) …}
Docker >= 1.6
Internal sbt plugin• Adds common libraries
• Generates build info
• Configures logging
• Adds monitoring
• Sets good runtime properties
• Defines a Dockerfile template
What we have learned
• Order instructions in Dockerfiles for fast builds
• Docker is very easy to use except for networking and file persistence
• Define own base images but use official when ones exist
• Building tools around Docker is easy and fun
Links• github.com/marcuslonnberg/sbt-docker
• github.com/marcuslonnberg/scala-docker
• github.com/sbt/sbt-assembly
• github.com/sbt/sbt-native-packager