如何使用 jre-alpine 基础镜像创建 Scala Play 应用程序的 Docker 镜像

Posted

技术标签:

【中文标题】如何使用 jre-alpine 基础镜像创建 Scala Play 应用程序的 Docker 镜像【英文标题】:How to create a Docker image of a Scala Play application using jre-alpine base image 【发布时间】:2019-12-21 23:26:28 【问题描述】:

我想利用jre-alpine Docker 基础镜像来缩小我必须推送到我的 Docker 注册表的 docker 镜像的大小,但我经常遇到这个错误:

[info] Step 10/16 : RUN id -u demiourgos728 2> /dev/null || useradd --system --create-home --uid 1001 --gid 0 demiourgos728
[info]  ---> Running in 696dcf40530a
[info] /bin/sh: useradd: not found
[info] Removing intermediate container 696dcf40530a
[error] The command '/bin/sh -c id -u demiourgos728 2> /dev/null || useradd --system --create-home --uid 1001 --gid 0 demiourgos728' returned a non-zero code: 127
[error] java.lang.RuntimeException: Nonzero exit value: 127
[error]     at com.typesafe.sbt.packager.docker.DockerPlugin$.publishLocalDocker(DockerPlugin.scala:483)
[error]     at com.typesafe.sbt.packager.docker.DockerPlugin$.$anonfun$projectSettings$33(DockerPlugin.scala:187)
[error]     at com.typesafe.sbt.packager.docker.DockerPlugin$.$anonfun$projectSettings$33$adapted(DockerPlugin.scala:185)
[error]     at scala.Function1.$anonfun$compose$1(Function1.scala:44)
[error]     at sbt.internal.util.$tilde$greater.$anonfun$$u2219$1(TypeFunctions.scala:40)
[error]     at sbt.std.Transform$$anon$4.work(System.scala:67)
[error]     at sbt.Execute.$anonfun$submit$2(Execute.scala:269)
[error]     at sbt.internal.util.ErrorHandling$.wideConvert(ErrorHandling.scala:16)
[error]     at sbt.Execute.work(Execute.scala:278)
[error]     at sbt.Execute.$anonfun$submit$1(Execute.scala:269)
[error]     at sbt.ConcurrentRestrictions$$anon$4.$anonfun$submitValid$1(ConcurrentRestrictions.scala:178)
[error]     at sbt.CompletionService$$anon$2.call(CompletionService.scala:37)
[error]     at java.util.concurrent.FutureTask.run(FutureTask.java:266)
[error]     at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
[error]     at java.util.concurrent.FutureTask.run(FutureTask.java:266)
[error]     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
[error]     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
[error]     at java.lang.Thread.run(Thread.java:748)
[error] (Docker / publishLocal) Nonzero exit value: 127

涉及的版本:

播放 2.7.3 Scala 2.12.8 Docker 19.03.1 sbt 1.2.8

在我的 build.sbt 中我添加了:

enablePlugins(AshScriptPlugin)
dockerBaseImage := "openjdk:jre-alpine"

按照此处提供的说明进行操作:https://www.scala-sbt.org/sbt-native-packager/formats/docker.html#busybox-ash-support

Play 尝试执行的 Docker 命令:

show dockerCommands
[info] Wrote /Users/xxxxx/projects/together/togrx/target/scala-2.12/together-rx_2.12-0.7.0-a.pom
[info] * Cmd(FROM,WrappedArray(openjdk:8-jre-alpine, as, stage0))
[info] * Cmd(WORKDIR,WrappedArray(/opt/docker))
[info] * Cmd(COPY,WrappedArray(opt /opt))
[info] * Cmd(USER,WrappedArray(root))
[info] * ExecCmd(RUN,List(chmod, -R, u=rX,g=rX, /opt/docker))
[info] * ExecCmd(RUN,List(chmod, u+x,g+x, /opt/docker/bin/together-rx))
[info] * DockerStageBreak
[info] * Cmd(FROM,WrappedArray(openjdk:8-jre-alpine))
[info] * Cmd(USER,WrappedArray(root))
[info] * Cmd(RUN,List(id, -u, demiourgos728, 2>, /dev/null, ||, useradd, --system, --create-home, --uid, 1001, --gid, 0, demiourgos728))
[info] * Cmd(WORKDIR,WrappedArray(/opt/docker))
[info] * Cmd(COPY,WrappedArray(--from=stage0 --chown=demiourgos728:root /opt/docker /opt/docker))
[info] * Cmd(EXPOSE,WrappedArray(4300 4301))
[info] * Cmd(USER,WrappedArray(1001))
[info] * ExecCmd(ENTRYPOINT,List(/opt/docker/bin/together-rx))
[info] * ExecCmd(CMD,List())

【问题讨论】:

【参考方案1】:

alpine 镜像中没有useradd 命令,请改用adduser,见下:

$ docker run -it openjdk:jre-alpine /bin/sh
Unable to find image 'openjdk:jre-alpine' locally
jre-alpine: Pulling from library/openjdk
[DEPRECATION NOTICE] registry v2 schema1 support will be removed in an upcoming release. Please contact admins of the docker.io registry NOW to avoid future disruption.
8e3ba11ec2a2: Pull complete
311ad0da4533: Pull complete
391a6a6b3651: Pull complete
Digest: sha256:016a7989474f1e685da966631ba6403cb349548621ebb8e4a6205f7c5fa88320
Status: Downloaded newer image for openjdk:jre-alpine
/ # useradd
/bin/sh: useradd: not found
/ # echo $?
127
/ # adduser
BusyBox v1.28.4 (2018-05-30 10:45:57 UTC) multi-call binary.

Usage: adduser [OPTIONS] USER [GROUP]

Create new user, or add USER to GROUP

        -h DIR          Home directory
        -g GECOS        GECOS field
        -s SHELL        Login shell
        -G GRP          Group
        -S              Create a system user
        -D              Don't assign a password
        -H              Don't create home directory
        -u UID          User id
        -k SKEL         Skeleton directory (/etc/skel)
/ #

因此,您需要修改build.sbt 或其他内容以确保使用adduser 而不是useradd

更新:

如你所说,useradd是play framework生成的,那么我建议你自定义自己的图片添加useradd,它在shadow包中,那么play framework就透明了,像下一个:

Dockerfile:

FROM openjdk:jre-alpine

RUN apk add shadow

在本地 docker 主机中构建映像:

docker build -t myimage .

最后,在 build.sbt 中:

dockerBaseImage := "myimage"

【讨论】:

你知道我应该如何修改那个命令吗?我猜有一个文件定义了这些命令。 这个项目不是你写的? grep -rn "useradd" * 找不到?我猜通常它在 build.sbt、Build.scala 等东西中。 我自己编写了应用程序,但该命令来自 Play 框架 2.7.3。 我正在尝试测试您的解决方案,但问题是我必须从头开始重写所有命令才能使用 dockerCommands += Cmd("RUN","apk add shadow") 在正确的位置添加“阴影”=> @987654322 @ 不,你不需要那样做。您可以认为 myimage 是您构建之外的基础设施,与 scala 代码无关。你只需构建这个镜像,并在 build.sbt 中指定它dockerBaseImage := "myimage"。我猜当你做dockerBaseImage := openjdk:jre-alpine时,插件会从dockerhub下载openjdk:jre-alpine,现在你只需使用你自己的图像,其中预装了useradd

以上是关于如何使用 jre-alpine 基础镜像创建 Scala Play 应用程序的 Docker 镜像的主要内容,如果未能解决你的问题,请参考以下文章

Docker基础

dockerfile:如何使用基础镜像中的 CMD 或 ENTRYPOINT

当我使用 Alpine 作为基础镜像时,如何添加用户?

带有混合基础镜像的 Docker 镜像

如何把ISO镜像转换成Docker镜像

如何交互式地创建一个Docker镜像