Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Problem with docker:publishLocal for alpine #1202

Closed
Mokrousov opened this issue Feb 18, 2019 · 8 comments
Closed

Problem with docker:publishLocal for alpine #1202

Mokrousov opened this issue Feb 18, 2019 · 8 comments
Labels

Comments

@Mokrousov
Copy link

Expected behaviour

sbt docker:publishLocal
...
[info] Successfully built 15d0b8df36ec
[info] Successfully tagged akka:0.2
[info] Built image akka with tags [0.2]
[success] Total time: 5 s, completed Feb 18, 2019 5:52:12 PM

Actual behaviour

sbt docker:publishLocal
...
[info] Step 9/15 : RUN id -u demiourgos728 2> /dev/null || useradd --system --create-home --uid 1001 --gid 0 demiourgos728
[info] ---> Running in 8251a433d260
[info] /bin/sh: useradd: not found
[info] Removing intermediate container 8251a433d260
[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:482)
[error] at com.typesafe.sbt.packager.docker.DockerPlugin$.$anonfun$projectSettings$34(DockerPlugin.scala:186)
[error] at com.typesafe.sbt.packager.docker.DockerPlugin$.$anonfun$projectSettings$34$adapted(DockerPlugin.scala:184)
[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

Information

  • dockerBaseImage := "java:8-jre-alpine"

  • sbt-native-packager 1.3.18

  • sbt version 1.2.8

  • MacOS

  • docker

  • alpine

@Mokrousov
Copy link
Author

With sbt-native-packager 1.3.17 builds fine

@borice
Copy link
Contributor

borice commented Feb 19, 2019

It doesn't really work (for me) with 1.3.17. Both 1.3.17 and 1.3.18 have their own problems. Details below:

I have a Play Framework app I want to containerize using sbt-native-packager.
WIth version 1.3.17, this is the Dockerfile generated (via sbt docker:stage)

FROM anapsix/alpine-java:8 as stage0
WORKDIR /opt/docker
COPY opt /opt
USER root
RUN ["chmod", "-R", "u=rX,g=rX", "/opt/docker"]

FROM anapsix/alpine-java:8
RUN id -u daemon || useradd --system --create-home --uid 1001 --gid 0 daemon
WORKDIR /opt/docker
COPY --from=stage0 --chown=daemon:root /opt/docker /opt/docker
EXPOSE 9000
USER 1001
ENTRYPOINT ["/opt/docker/bin/my-play-app"]
CMD []

There are 2 problems with the above.

  1. The script /opt/docker/bin/my-play-app is not executable inside the container (so the container cannot start)
  2. The anapsix/alpine-java:8 image does not contain a useradd command (but luckily that line doesn't execute since it does contain a user daemon)

Now, with the 1.3.18 version, here's the generated Dockerfile:

FROM anapsix/alpine-java:8 as stage0
WORKDIR /opt/docker
COPY opt /opt
USER root
RUN ["chmod", "-R", "u=rX,g=rX", "/opt/docker"]
RUN ["chmod", "u+x,g+x", "/opt/docker/bin/my-play-app"]

FROM anapsix/alpine-java:8
USER root
RUN id -u demiourgos728 2> /dev/null || useradd --system --create-home --uid 1001 --gid 0 demiourgos728
WORKDIR /opt/docker
COPY --from=stage0 --chown=demiourgos728:root /opt/docker /opt/docker
EXPOSE 9000
USER 1001
ENTRYPOINT ["/opt/docker/bin/my-play-app"]
CMD []

This image fixes the executable script issues from 1.3.17, but now tries to use a different user for running the processes instead of daemon, however this new user demiourgos728 does not exist in the alpine image, so the line trying to create the user via useradd is ran... however, the useradd binary does not exist in the alpine image. (there is an adduser command available, though)

These are the Docker settings I used in build.sbt for the above trials:

lazy val dockerSettings = Seq(
  dockerBaseImage := "anapsix/alpine-java:8",
  dockerExposedPorts := Seq(9000),
  dockerUpdateLatest := true
)

@borice
Copy link
Contributor

borice commented Feb 19, 2019

I should note that the documentation does say that the (new) default user is demiourgos728 (however there's still an old reference in the documentation for the dockerBaseImage setting saying that

...and include the user configured by daemonUser (daemon, by default)

(emphasis mine)

I was able to work around it by adding

daemonUserUid in Docker := None,
daemonUser in Docker := "daemon",

to my dockerSettings from before, but this only side-stepped the issue. If someone wanted to use a different user, the useradd vs adduser problem still remains.

@muuki88 muuki88 added the docker label May 3, 2019
@muuki88
Copy link
Contributor

muuki88 commented May 3, 2019

Thanks for all the details and sharing your working buil.sbt 😍 The docker ecosystem is quite complex and changes rather frequently.

Your proposed solution is IMHO the way to go. As you said the useradd command is still an issue.

There are two options from here.

  1. Enhance the documentation with your findings and keep things as is.
  2. Make the user add command configurable. Ideally we would then collect all alpine customisations in a separate DockerAlpinePlugin

WDYT?

Thanks for your patience. I'm currently on parental leave.

@borice
Copy link
Contributor

borice commented May 16, 2019

I would vote on the second option -- make the add user (and add group) command configurable. This is because we're assuming that there is a useradd (and groupadd) binary, and clearly on some images that's not the case. However, I think we might cover most use cases by providing support for the two major user/group-management flavors possible: useradd / adduser and groupadd / addgroup. The adduser and addgroup commands are the result of BusyBox-based systems... and most images that want to be "slim" make use of it.

Here's an attempt at a solution:

In the latest sbt-native-packager (version 1.3.21) this is the line generated for creating the user:
RUN id -u demiourgos728 2> /dev/null || (( getent group 0 || groupadd -g 0 root ) && useradd --system --create-home --uid 1001 --gid 0 demiourgos728 )

if we change this to the following, it should work on both flavors (standard and BusyBox):

RUN id -u demiourgos728 &> /dev/null || (( getent group 0 &> /dev/null || ( type -p groupadd &> /dev/null && groupadd -g 0 root || addgroup -g 0 -S root )) && ( type -p useradd &> /dev/null && useradd --system --create-home --uid 1001 --gid 0 demiourgos728 || adduser -S -u 1001 -G root demiourgos728 ))

This updated command works just like the original one, except it checks if the groupadd and useradd commands exist, and if they do then uses them to create the user/group, and if they're missing it attempts to create the user using the adduser/addgroup commands typical of BusyBox-based systems.

If neither of these binaries exist, then an error will be thrown by Docker when building the image.
I just checked it and it seems to work fine for both flavors.

What do you think? It's fairly crude, but should work in most cases.

muuki88 pushed a commit that referenced this issue Jun 12, 2019
* Possible improvement for #1202

* Fixed code formatting
@muuki88 muuki88 closed this as completed Jun 12, 2019
@alexandru
Copy link

In case anybody else bumps into this problem, what I did to solve it was to simply install the useradd command, not sure how correct it is, but seems to work ¯_(ツ)_/¯

    dockerBaseImage := "adoptopenjdk/openjdk11:x86_64-alpine-jre-11.0.6_10" ,
    dockerCommands := dockerCommands.value.flatMap {
      case cmd@Cmd("FROM",_) => List(cmd, Cmd("RUN", "apk update && apk add bash && apk add shadow"))
      case other => List(other)
    },

@razem-io
Copy link

razem-io commented Apr 17, 2020

In case anybody else bumps into this problem and does not want to install useradd and bash due to security / size concerns:

dockerBaseImage := "adoptopenjdk/openjdk11:x86_64-alpine-jre-11.0.6_10",
daemonUserUid in Docker := None,
daemonUser in Docker    := "daemon",

and activate the docker / ash shell plugin

.enablePlugins(DockerPlugin, AshScriptPlugin)

No need to install anything else.

@enragedginger
Copy link

@alexandru Thanks for that code snippet. That mostly worked for me. For some reason, with the latest version of sbt-native-packager (version 1.7.4), none of the commands were matching that first case, so I had to change it to this instead:

case cmd: Cmd if cmd.cmd.equalsIgnoreCase("FROM") =>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

6 participants