使用OpenJDK 8和Apple M1芯片在Docker shell中进行sbt编译时出现SIGSEGV错误。

11

我刚拿到一台搭载 Apple M1 芯片的 Mac,并正在尝试为我正在开发的项目设置开发环境。我正在使用 Docker Desktop,并将平台标志添加到了 docker-compose.yml (platform: linux/x86_64) 和 Dockerfile (FROM --platform=linux/amd64 openjdk:8-jdk-stretch) 中。我使用的是 OpenJDK 8 和 sbt 0.13.15。

容器创建正常,我可以在 docker shell 中运行 sbt -Dsbt.ivy.home='.ivy2' -Dsbt.global.base='.sbt' -Dsbt.repository.config='.sbt/repositories',它将创建一个 sbt shell,但如果我在该 shell 中运行 compile,就会收到以下错误:

[info] Compiling 153 Scala sources and 2 Java sources to /opt/target/scala-2.10/classes...
#
# A fatal error has been detected by the Java Runtime Environment:
#
#  SIGSEGV (0xb) at pc=0x000000400d9d7447, pid=9, tid=0x00000040b87ab700
#
# JRE version: OpenJDK Runtime Environment (8.0_242-b08) (build 1.8.0_242-b08)
# Java VM: OpenJDK 64-Bit Server VM (25.242-b08 mixed mode linux-amd64 compressed oops)
# Problematic frame:
# J 12257 C2 scala.reflect.internal.Types$$anonfun$57.apply(Ljava/lang/Object;)Ljava/lang/Object; (12 bytes) @ 0x000000400d9d7447 [0x000000400d9d7080+0x3c7]
#
# Failed to write core dump. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again
#
# An error report file with more information is saved as:
# /opt/hs_err_pid9.log
#
# If you would like to submit a bug report, please visit:
#   http://bugreport.java.com/bugreport/crash.jsp
#
qemu: uncaught target signal 6 (Aborted) - core dumped
Aborted

查看它创建的hs_err_pid9.log时,唯一不寻常的片段是:

Event: 66.528 Thread 0x00000040a8020800 Exception <a 'java/io/FileNotFoundException'> (0x00000000f7eaf680) thrown at [/home/openjdk/jdk8u/hotspot/src/share/vm/prims/jni.cpp, line 710]
Event: 66.545 Thread 0x00000040a8020800 Exception <a 'java/io/FileNotFoundException'> (0x00000000f7eb0b38) thrown at [/home/openjdk/jdk8u/hotspot/src/share/vm/prims/jni.cpp, line 710]
Event: 67.214 Thread 0x00000040a801f800 Exception <a 'java/io/FileNotFoundException'> (0x00000000f7d343b8) thrown at [/home/openjdk/jdk8u/hotspot/src/share/vm/prims/jni.cpp, line 710]
Event: 67.219 Thread 0x00000040a801f800 Exception <a 'java/io/FileNotFoundException'> (0x00000000f7d35048) thrown at [/home/openjdk/jdk8u/hotspot/src/share/vm/prims/jni.cpp, line 710]
Event: 68.016 Thread 0x00000040a801f800 Implicit null exception at 0x000000400bd16a9b to 0x000000400bd16c51
Event: 69.405 Thread 0x00000040a801f800 Implicit null exception at 0x000000400c118986 to 0x000000400c118ea5
Event: 69.407 Thread 0x00000040a801f800 Implicit null exception at 0x000000400bdba136 to 0x000000400bdba4ed
Event: 69.556 Thread 0x00000040a801f800 Implicit null exception at 0x000000400bd29275 to 0x000000400bd2945d
Event: 69.567 Thread 0x00000040a801f800 Implicit null exception at 0x000000400be3adaf to 0x000000400be3ae19
Event: 69.835 Thread 0x00000040a801f800 Implicit null exception at 0x000000400be93a35 to 0x000000400be93de1

这里 是代码库,供任何想查看环境设置步骤、位于根目录下的 docker-compose.yml 和 Dockerfile 的人使用。


1
你能否尝试去掉一些变量再重现这个问题?特别是,使用本地的arm64 JVM直接在主机上运行?如果你所遇到的核心问题是Rosetta的x86仿真引起的,那么我们可能无法为你提供修复(除了“不要这样做”)。 - Charles Duffy
我理解如果你正在引入本地依赖项,需要获得一致的运行时环境的必要性。个人而言,在 MacOS 和 Linux 上执行此操作的工具是 Nix,它还会更多地将软件构建过程建模为纯函数,并使用适当的函数式编程语言来实现。 - Charles Duffy
我不确定你的意思,Nix是要替代什么的? - jiamae
1
Docker作为提供一致依赖项集的机制(Nix更有效地启动了这一点——因为Docker RUN步骤与网络访问不隔离,所以无法保证在调用时具有一致的行为;即使是“RUN apt-get update”每次也会有不同的结果)。虽然Nix可以直接用于构建Docker镜像(请参见nixpkgs中的dockerTools),但也可以编写代码来组装所需操作系统的本机依赖项。 - Charles Duffy
2
也发布在 https://forums.docker.com/t/sigsegv-error-from-sbt-comile-in-docker-shell-with-openjdk-8-and-apple-m1-chip/116582 上。 - Arjan
显示剩余4条评论
2个回答

5
很可能你遇到了qemu已知的bug。在模拟ARM芯片上的amd64时会出现问题。这种情况下的解决方案是将工作负载移至amd64系统,或重新为ARM构建镜像。请参阅此处的“已知问题”部分。

https://docs.docker.com/desktop/mac/apple-silicon/

截至2022年2月的相关文本:

然而,在模拟器下运行Intel架构容器时可能会崩溃,因为qemu有时无法运行容器。此外,在qemu模拟器下,文件系统更改通知API(inotify)不起作用。即使容器在模拟器下正确运行,它们也会比本地等效版本慢并且使用更多内存。

总之,在基于Arm的机器上运行Intel架构容器应该只被视为“尽力而为”。我们建议尽可能在Apple Silicon机器上运行arm64容器,并鼓励容器作者生成arm64或多架构版本的容器。随着越来越多的镜像支持多种架构的重建,我们预计这个问题会变得越来越少见。

将镜像重建为ARM架构

由于您正在构建自己的镜像,因此应能够为ARM平台重新构建它们。

您需要从ARM基础镜像开始。您目前使用的那个(openjdk:8-jdk-stretch)没有为ARM分发,但同一存储库的其他版本有(例如openjdk:19-jdk-buster)。

(您也可以自己重新构建当前的基础镜像为ARM,但这需要您找到其源材料——有时可以在GitHub、GitLab等上找到。)

更改Dockerfile以使用可用于ARM的基础镜像,然后将自己的镜像重建为ARM。这很简单——不要指定平台信息,Docker将默认使用您的平台。

多平台构建

为什么要使用amd64镜像?我考虑了两种可能的原因。

  1. 因为你的基础镜像只有amd64版本可用。
  2. 因为你的生产环境是amd64,所以你希望生成的镜像也是amd64。

如果只是第一个原因,那么上面已经涵盖了。如果你重新构建为ARM,则完成了。

对于第二种情况,你可以在开发环境中使用ARM,但在完成工作后,为生产构建amd64版本。

要创建一个多平台镜像并将其推送到你的注册表中,你可以使用以下的buildx命令:

docker buildx build --platform linux/amd64,linux/arm64 \
    -t <your image tag> --push .

多平台镜像很好,因为它们允许您使用 ARM 镜像,这些镜像更快、更稳定。它还允许任何拥有 Apple Silicon 设备的团队成员执行相同操作。但是,您的生产平台(通常是 amd64)和其他在 amd64 平台上的开发人员也可以从同一镜像(但构建为其本地平台)工作。

1

您是否使用某个集成开发环境(IDE)在 shell 中运行编译程序?

如果是这样,您可以尝试打开终端/ shell 并运行错误提示中提到的命令:

ulimit -c unlimited

然后,通过命令从同一终端/ shell 运行编译程序。 要从 IDE 运行它,您需要在 IDE 设置中找到如何设置此参数。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接