Skip to content

Commit

Permalink
Optimize docker layers (#1425)
Browse files Browse the repository at this point in the history
* Optimize docker layers

- add classpath jar to artifacts
- move start scripts to the same layer as artifacts, because those contain references to artifacts
- move /jre (JLink) to a separate layer
- move /conf to a separate layer as well, because it's not something changing often

* additional comment regarding ClasspathJarPlugin usage added

Co-authored-by: Nepomuk Seiler <muuki88@users.noreply.github.com>
  • Loading branch information
412b and muuki88 committed Jul 1, 2021
1 parent 7025884 commit 45a9be0
Show file tree
Hide file tree
Showing 7 changed files with 42 additions and 27 deletions.
21 changes: 18 additions & 3 deletions src/main/scala/com/typesafe/sbt/packager/docker/DockerPlugin.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import java.util.concurrent.atomic.AtomicBoolean
import sbt._
import sbt.Keys.{clean, mappings, name, organization, publish, publishLocal, sourceDirectory, streams, target, version}
import com.typesafe.sbt.packager.Keys._
import com.typesafe.sbt.packager.archetypes.jar.ClasspathJarPlugin
import com.typesafe.sbt.packager.linux.LinuxPlugin.autoImport.{daemonUser, defaultLinuxInstallLocation}
import com.typesafe.sbt.packager.universal.UniversalPlugin
import com.typesafe.sbt.packager.universal.UniversalPlugin.autoImport.stage
Expand Down Expand Up @@ -105,18 +106,32 @@ object DockerPlugin extends AutoPlugin {
dockerGroupLayers := {
val dockerBaseDirectory = (defaultLinuxInstallLocation in Docker).value
// Ensure this doesn't break even if the JvmPlugin isn't enabled.
val artifacts = projectDependencyArtifacts.?.value.getOrElse(Nil).map(_.data).toSet
var artifacts = projectDependencyArtifacts.?.value.getOrElse(Nil).map(_.data).toSet

// add the classpath jar to the project artifacts to improve layer caching as it is created by
// the ClasspathJarPlugin and is not part of the projectDependencyArtifacts
ClasspathJarPlugin.autoImport.packageJavaClasspathJar.?.value match {
case Some(p) => artifacts += p
case _ =>
}

val oldFunction = (dockerLayerGrouping in Docker).value

// By default we set this to a function that always returns None.
val oldPartialFunction = Function.unlift((tuple: (File, String)) => oldFunction(tuple._2))

val libDir = dockerBaseDirectory + "/lib/"
val binDir = dockerBaseDirectory + "/bin/"
val jreDir = dockerBaseDirectory + "/jre/"
val confDir = dockerBaseDirectory + "/conf/"

oldPartialFunction.orElse {
case (file, _) if artifacts(file) => 2
case (_, path) if path.startsWith(libDir) || path.startsWith(binDir) => 1
// bin directory contains start scripts which are containing artifacts / classpath jar,
// so should be together with actual artifacts
case (file, path) if artifacts(file) || path.startsWith(binDir) => 4
case (_, path) if path.startsWith(jreDir) => 3
case (_, path) if path.startsWith(libDir) => 2
case (_, path) if path.startsWith(confDir) => 1
}
},
dockerAliases := {
Expand Down
24 changes: 12 additions & 12 deletions src/sbt-test/docker/file-permission/build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,19 @@ lazy val root = (project in file("."))
assert(lines(2).substring(0, 25) == "LABEL snp-multi-stage-id=") // random generated id is hard to test
assertEquals(lines.drop(3),
"""WORKDIR /opt/docker
|COPY 1/opt /1/opt
|COPY 2/opt /2/opt
|COPY 4/opt /4/opt
|USER root
|RUN ["chmod", "-R", "u=rX,g=rX", "/1/opt/docker"]
|RUN ["chmod", "-R", "u=rX,g=rX", "/2/opt/docker"]
|RUN ["chmod", "u+x,g+x", "/1/opt/docker/bin/file-permission-test"]
|RUN ["chmod", "-R", "u=rX,g=rX", "/4/opt/docker"]
|RUN ["chmod", "u+x,g+x", "/4/opt/docker/bin/file-permission-test"]
|
|FROM fabric8/java-centos-openjdk8-jdk as mainstage
|USER root
|RUN id -u demiourgos728 1>/dev/null 2>&1 || (( getent group 0 1>/dev/null 2>&1 || ( type groupadd 1>/dev/null 2>&1 && groupadd -g 0 root || addgroup -g 0 -S root )) && ( type useradd 1>/dev/null 2>&1 && useradd --system --create-home --uid 1001 --gid 0 demiourgos728 || adduser -S -u 1001 -G root demiourgos728 ))
|WORKDIR /opt/docker
|COPY --from=stage0 --chown=demiourgos728:root /1/opt/docker /opt/docker
|COPY --from=stage0 --chown=demiourgos728:root /2/opt/docker /opt/docker
|COPY --from=stage0 --chown=demiourgos728:root /4/opt/docker /opt/docker
|USER 1001:0
|ENTRYPOINT ["/opt/docker/bin/file-permission-test"]
|CMD []""".stripMargin.linesIterator.toList)
Expand All @@ -45,8 +45,8 @@ lazy val root = (project in file("."))
|USER root
|RUN id -u demiourgos728 1>/dev/null 2>&1 || (( getent group 0 1>/dev/null 2>&1 || ( type groupadd 1>/dev/null 2>&1 && groupadd -g 0 root || addgroup -g 0 -S root )) && ( type useradd 1>/dev/null 2>&1 && useradd --system --create-home --uid 1001 --gid 0 demiourgos728 || adduser -S -u 1001 -G root demiourgos728 ))
|WORKDIR /opt/docker
|COPY 1/opt /opt
|COPY 2/opt /opt
|COPY 4/opt /opt
|USER 1001:0
|ENTRYPOINT ["/opt/docker/bin/file-permission-test"]
|CMD []""".stripMargin.linesIterator.toList)
Expand All @@ -60,8 +60,8 @@ lazy val root = (project in file("."))
|USER root
|RUN id -u demiourgos728 1>/dev/null 2>&1 || (( getent group 5000 1>/dev/null 2>&1 || ( type groupadd 1>/dev/null 2>&1 && groupadd -g 5000 sbt || addgroup -g 5000 -S sbt )) && ( type useradd 1>/dev/null 2>&1 && useradd --system --create-home --uid 1001 --gid 5000 demiourgos728 || adduser -S -u 1001 -G sbt demiourgos728 ))
|WORKDIR /opt/docker
|COPY 1/opt /opt
|COPY 2/opt /opt
|COPY 4/opt /opt
|USER 1001:5000
|ENTRYPOINT ["/opt/docker/bin/file-permission-test"]
|CMD []""".stripMargin.linesIterator.toList)
Expand All @@ -75,8 +75,8 @@ lazy val root = (project in file("."))
|USER root
|RUN id -u demiourgos728 1>/dev/null 2>&1 || (( getent group 0 1>/dev/null 2>&1 || ( type groupadd 1>/dev/null 2>&1 && groupadd -g 0 root || addgroup -g 0 -S root )) && ( type useradd 1>/dev/null 2>&1 && useradd --system --create-home --uid 1001 --gid 0 demiourgos728 || adduser -S -u 1001 -G root demiourgos728 ))
|WORKDIR /opt/docker
|COPY 1/opt /opt
|COPY 2/opt /opt
|COPY 4/opt /opt
|RUN ["chmod", "-R", "u=rX,g=rX", "/opt/docker"]
|RUN ["chmod", "u+x,g+x", "/opt/docker/bin/file-permission-test"]
|USER 1001:0
Expand All @@ -90,8 +90,8 @@ lazy val root = (project in file("."))
assertEquals(lines,
"""FROM fabric8/java-centos-openjdk8-jdk as mainstage
|WORKDIR /opt/docker
|COPY --chown=daemon:root 1/opt /opt
|COPY --chown=daemon:root 2/opt /opt
|COPY --chown=daemon:root 4/opt /opt
|USER daemon
|ENTRYPOINT ["/opt/docker/bin/file-permission-test"]
|CMD []""".stripMargin.linesIterator.toList)
Expand All @@ -106,19 +106,19 @@ lazy val root = (project in file("."))
assert(lines(2).substring(0, 25) == "LABEL snp-multi-stage-id=") // random generated id is hard to test
assertEquals(lines.drop(3),
"""WORKDIR /opt/docker
|COPY 1/opt /1/opt
|COPY 2/opt /2/opt
|COPY 4/opt /4/opt
|USER root
|RUN ["chmod", "-R", "u=rwX,g=rwX", "/1/opt/docker"]
|RUN ["chmod", "-R", "u=rwX,g=rwX", "/2/opt/docker"]
|RUN ["chmod", "u+x,g+x", "/1/opt/docker/bin/file-permission-test"]
|RUN ["chmod", "-R", "u=rwX,g=rwX", "/4/opt/docker"]
|RUN ["chmod", "u+x,g+x", "/4/opt/docker/bin/file-permission-test"]
|
|FROM fabric8/java-centos-openjdk8-jdk as mainstage
|USER root
|RUN id -u demiourgos728 1>/dev/null 2>&1 || (( getent group 0 1>/dev/null 2>&1 || ( type groupadd 1>/dev/null 2>&1 && groupadd -g 0 root || addgroup -g 0 -S root )) && ( type useradd 1>/dev/null 2>&1 && useradd --system --create-home --uid 1001 --gid 0 demiourgos728 || adduser -S -u 1001 -G root demiourgos728 ))
|WORKDIR /opt/docker
|COPY --from=stage0 --chown=demiourgos728:root /1/opt/docker /opt/docker
|COPY --from=stage0 --chown=demiourgos728:root /2/opt/docker /opt/docker
|COPY --from=stage0 --chown=demiourgos728:root /4/opt/docker /opt/docker
|USER 1001:0
|ENTRYPOINT ["/opt/docker/bin/file-permission-test"]
|CMD []""".stripMargin.linesIterator.toList)
Expand Down
2 changes: 1 addition & 1 deletion src/sbt-test/docker/test-executableScriptName/test
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Generate the Docker image locally
> docker:publishLocal
$ exists target/docker/stage/Dockerfile
$ exists target/docker/stage/1/opt/docker/bin/docker-exec
$ exists target/docker/stage/4/opt/docker/bin/docker-exec
> checkDockerfile
$ exec bash -c 'docker run docker-package:0.1.0 | grep -q "Hello world"'
4 changes: 2 additions & 2 deletions src/sbt-test/docker/test-layer-groups/build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ TaskKey[Unit]("checkDockerfile") := {
val dockerfile = IO.read((stagingDirectory in Docker).value / "Dockerfile")
val copyLines = dockerfile.linesIterator.toList.filter(_.startsWith("COPY --from=stage0"))
assertEquals(copyLines,
"""COPY --from=stage0 --chown=demiourgos728:root /1/opt/docker /opt/docker
|COPY --from=stage0 --chown=demiourgos728:root /2/opt/docker /opt/docker
"""COPY --from=stage0 --chown=demiourgos728:root /2/opt/docker /opt/docker
|COPY --from=stage0 --chown=demiourgos728:root /4/opt/docker /opt/docker
|COPY --from=stage0 --chown=demiourgos728:root /54/opt/docker /opt/docker
|COPY --from=stage0 --chown=demiourgos728:root /opt/docker /opt/docker""".stripMargin.linesIterator.toList)
}
Expand Down
14 changes: 7 additions & 7 deletions src/sbt-test/docker/test-layer-groups/test
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# Generate the Docker image locally
> docker:publishLocal
$ exists target/docker/stage/Dockerfile
$ exists target/docker/stage/1/opt/docker/bin/docker-groups
$ exists target/docker/stage/1/opt/docker/lib/org.slf4j.slf4j-api-1.7.30.jar
-$ exists target/docker/stage/1/opt/docker/lib/com.example.docker-groups-0.1.0.jar
$ exists target/docker/stage/4/opt/docker/bin/docker-groups
$ exists target/docker/stage/2/opt/docker/lib/org.slf4j.slf4j-api-1.7.30.jar
-$ exists target/docker/stage/2/opt/docker/lib/com.example.docker-groups-0.1.0.jar
$ exists target/docker/stage/opt/docker/other
$ exists target/docker/stage/2
-$ exists target/docker/stage/2/opt/docker/lib/org.slf4j.slf4j-api-1.7.30.jar
$ exists target/docker/stage/2/opt/docker/lib/com.example.docker-groups-0.1.0.jar
-$ exists target/docker/stage/1/opt/docker/lib/org.slf4j.slf4j-api-1.7.30.jar
$ exists target/docker/stage/4/opt/docker/lib/com.example.docker-groups-0.1.0.jar
$ exists target/docker/stage/54/opt/docker/spark

$ exec bash -c 'docker run --rm --entrypoint=ls docker-groups:0.1.0 |tr "\n" "," | grep -q "bin,lib,other,spark"'
Expand All @@ -20,10 +20,10 @@ $ copy-file changes/nolayers.sbt layers.sbt
> docker:publishLocal
$ exists target/docker/stage/opt/docker/bin
$ exists target/docker/stage/opt/docker/spark
-$ exists target/docker/stage/1
-$ exists target/docker/stage/2
-$ exists target/docker/stage/4
-$ exists target/docker/stage/54
$ exists target/docker/stage/opt/docker/lib/org.slf4j.slf4j-api-1.7.30.jar
$ exists target/docker/stage/opt/docker/lib/com.example.docker-groups-0.1.0.jar
$ exec bash -c 'docker run --rm --entrypoint=ls docker-groups:0.1.0 |tr "\n" "," | grep -q "bin,lib,other,spark"'
> checkDockerfileWithNoLayers
> checkDockerfileWithNoLayers
2 changes: 1 addition & 1 deletion src/sbt-test/docker/test-packageName-universal/test
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Generate the Docker image locally
> docker:publishLocal
$ exists target/docker/stage/Dockerfile
$ exists target/docker/stage/1/opt/docker/bin/docker-test
$ exists target/docker/stage/4/opt/docker/bin/docker-test
> checkDockerfile
$ exec bash -c 'docker run docker-package:0.1.0 | grep -q "Hello world"'
2 changes: 1 addition & 1 deletion src/sbt-test/docker/test-packageName/test
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Generate the Docker image locally
> docker:publishLocal
$ exists target/docker/stage/Dockerfile
$ exists target/docker/stage/1/opt/docker/bin/docker-test
$ exists target/docker/stage/4/opt/docker/bin/docker-test
> checkDockerfile
$ exec bash -c 'docker run docker-package:0.1.0 | grep -q "Hello world"'

0 comments on commit 45a9be0

Please sign in to comment.