You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为Scala多项目的各子项目分别构建独立Docker镜像

多模块SBT Scala项目子模块独立打包容器化方案

三种成熟落地路径,均不需要打全项目统一包,每个子模块生成的产物可以直接拷贝到JDK基础镜像独立运行。


方案1:sbt-assembly 按子模块单独打可执行fat jar(适配成本最低、最稳妥)

很多人误以为sbt-assembly只能给整个项目打全量fat jar,实际上该插件原生支持多项目构建下的子模块独立打包,只要避免全局配置、给非部署模块配置跳过规则即可,打出来的jar只包含当前子模块的代码+它自身的传递依赖,不会混入其他无关子模块的内容,是生产环境使用最广泛的方案。

  • 首先在project/plugins.sbt中引入插件:
    addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.1.5")
    
  • 在Build.scala中按规则配置:根项目和公共依赖模块配置跳过打包,仅给需要独立部署的子模块添加assembly配置,示例:
    import sbt._
    import Keys._
    import sbtassembly.AssemblyPlugin.autoImport._
    
    object Build extends Build {
      // 可部署子模块复用的公共打包配置
      lazy val deployableSettings = Seq(
        assembly / assemblyMergeStrategy := {
          case PathList("META-INF", _*) => MergeStrategy.discard
          case _ => MergeStrategy.first
        },
        assembly / assemblyJarName := s"${name.value}-${version.value}.jar"
      )
    
      // 根项目仅做聚合,跳过所有打包
      lazy val root = Project(id = "root", base = file("."))
        .aggregate(util, core, serviceA, serviceB)
        .settings(
          publish / skip := true,
          assembly / skip := true
        )
    
      // 公共依赖模块(被其他模块引用、不需要独立部署),跳过打包
      lazy val util = Project(id = "util", base = file("util"))
        .settings(
          publish / skip := true,
          assembly / skip := true
        )
    
      lazy val core = Project(id = "core", base = file("core"))
        .dependsOn(util)
        .settings(
          publish / skip := true,
          assembly / skip := true
        )
    
      // 需要独立部署的子模块A
      lazy val serviceA = Project(id = "service-a", base = file("service-a"))
        .dependsOn(core, util)
        .settings(deployableSettings: _*)
        .settings(
          assembly / mainClass := Some("com.yourorg.serviceA.Main")
        )
    
      // 需要独立部署的子模块B
      lazy val serviceB = Project(id = "service-b", base = file("service-b"))
        .dependsOn(core, util)
        .settings(deployableSettings: _*)
        .settings(
          assembly / mainClass := Some("com.yourorg.serviceB.Main")
        )
    }
    
  • 打包时指定子模块执行命令即可,比如打serviceA的包:
    sbt serviceA/assembly
    
    生成的可执行jar在service-a/target/scala-<版本号>/目录下,直接拷贝到JDK镜像中,执行java -jar <jar包名>即可运行。

方案2:sbt-native-packager 打分层依赖包(镜像构建效率更高)

如果觉得fat jar合并过程繁琐、镜像层复用率低,可以用这个插件给每个子模块生成分层结构的分发包,不需要做jar合并,打包速度更快,依赖层可以被Docker缓存复用,适合迭代频繁的生产环境。

  • 在project/plugins.sbt中引入插件:
    addSbtPlugin("com.github.sbt" % "sbt-native-packager" % "1.9.16")
    
  • 给需要部署的子模块开启Java应用打包能力,公共模块和根项目同样配置跳过:
    // 以serviceA为例
    lazy val serviceA = Project(id = "service-a", base = file("service-a"))
      .dependsOn(core, util)
      .enablePlugins(JavaAppPackaging)
      .settings(
        Compile / mainClass := Some("com.yourorg.serviceA.Main")
      )
    
  • 执行打包命令:
    sbt serviceA/stage
    
    生成的产物在service-a/target/universal/stage/目录下,结构为:
    • bin/:自动生成的各平台启动脚本
    • lib/:当前子模块代码+所有传递依赖的jar包
    • conf/:配置文件目录
      把整个stage目录拷贝到JDK基础镜像中,直接执行bin/service-a即可启动。

方案3:sbt-pack 打轻量分发包(配置最简单)

如果只需要简单的依赖收集+启动脚本生成,不需要native-packager的多余功能,可以选择sbt-pack,配置更轻量,打包速度更快,该插件不会做jar内容合并,完全不需要配置合并规则,能省下调适merge冲突的时间,适合依赖关系复杂的项目,同样支持子模块独立打包。

  • 引入插件后,给需要部署的子模块添加对应配置,执行sbt serviceA/pack即可生成产物,产物结构和native-packager的stage输出类似,在service-a/target/pack/目录下,拷贝到容器中运行bin目录下的启动脚本即可。

容器化实操注意事项

  • 只要在Project定义中通过dependsOn声明了子模块间的依赖关系,以上三个插件都会自动把依赖模块的代码和传递依赖打进产物,不需要手动处理
  • 不要直接执行全局的sbt assembly/sbt stage/sbt pack命令,必须带上子模块名前缀,避免给不需要部署的模块生成无用产物
  • 基础镜像建议选JRE版本而非全量JDK版本,缩小镜像体积,比如用eclipse-temurin:17-jre-jammy即可满足绝大多数Scala应用的运行需求
  • 以fat jar方案为例,对应Dockerfile参考:
    FROM eclipse-temurin:17-jre-jammy
    WORKDIR /app
    COPY service-a/target/scala-2.13/service-a-1.0.0.jar app.jar
    ENTRYPOINT ["java", "-jar", "app.jar"]
    
    分层打包方案的Dockerfile参考:
    FROM eclipse-temurin:17-jre-jammy
    WORKDIR /app
    COPY service-a/target/universal/stage/ ./
    ENTRYPOINT ["bin/service-a"]
    

内容的提问来源于stack exchange,提问作者Kotesh Meesala

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 14:48:23