Quarkus 与树莓派 - 本次是裸机运行
Quarkus 与树莓派 - 本次是裸机运行
四年前,Andrea Battaglia 撰写了一篇精彩的博客文章,介绍如何在树莓派上运行 Quarkus 应用程序(已编译为原生可执行文件)。这是一个有趣的用例,但并非唯一。由于树莓派经常用于家庭和科学项目,因此我特别关注在您想在笔记本电脑上本地开发应用程序,但在裸机树莓派服务器上运行它(即不涉及容器)时的开发者体验。树莓派经常会扩展各种传感器和外围设备。能够直接与这些设备通信,尤其是在开发过程中,非常实用。这就是为什么我们专注于这个方向。
以下文本中引用了一个示例应用程序。该应用程序会定期执行一个命令,读取 CPU 温度并将值写入日志。显然,这并不是树莓派特有的功能,但可以替换为读取任何其他设备文件或 GPIO 值的命令。 |
第一步 - 准备您的开发环境
安装 Raspberry Pi OS
无需特殊准备。您可以使用方便的Raspberry Pi Imager来准备 SD 卡。如果您不需要 GUI(在大多数情况下您不需要),可以使用 Raspberry Pi OS Lite 镜像。此版本将节省一些资源。此外,您还需要启用 SSH 并配置无线局域网。
第二步 - 构建和部署
首先,我们需要在本地构建示例应用程序,并在目标树莓派上运行它。通过一些 bash 技巧就可以轻松实现。
# Build the app
mvn clean package
# Copy the app to the target rpi; you can use rsync or sftp as well
scp -r target/quarkus-app $RPI_HOST:$RPI_PROJECT_PATH (1) (2)
# Start the app on the target host; CTRL+C stops the app
ssh -t $RPI_HOST "cd $RPI_PROJECT_PATH; java -jar quarkus-app/quarkus-run.jar" (3)
1 | target/quarkus-app 目录的内容包含所有 Quarkus 应用程序的组件。 |
2 | RPI_HOST 和 RPI_PROJECT_PATH 是环境变量。第一个定义了目标树莓派的主机/地址。后者定义了部署应用程序的路径;例如,类似 /home/username/rpi-sample 的路径。 |
3 | -t 是至关重要的,它用于强制分配伪终端。我们需要一个用于远程命令的交互式 shell。 |
另请参阅示例应用程序中的build-deploy.sh 文件。 |
通过这种方式,您可以轻松地在本地构建应用程序并在树莓派上运行它。但是,每当您更改应用程序时,都需要重新构建、复制和重新启动应用程序,这有点繁琐。尤其当您习惯了 Quarkus 开发模式的快速周转时。那么还有其他选择吗?
第三步 - 远程开发模式
如您所知,Quarkus 具有远程开发模式。它通常用于在容器环境(如 OpenShift)中运行 Quarkus 应用程序,并使您对本地文件所做的更改在远程应用程序中立即可见。但没有理由不为我们的用例尝试它。
首先,我们需要使用 mutable-jar
格式构建一个可变应用程序。让我们调整一下我们的构建和部署脚本。
# Build the app
# quarkus.live-reload.password=foo must be set in application.properties or passed as system property when the remote side starts
# See https://github.com/quarkusio/quarkus/issues/44933
mvn clean package -Dquarkus.package.jar.type=mutable-jar (1)
# Copy the app to the target rpi; you can use rsync or sftp as well
scp -r target/quarkus-app $RPI_HOST:$RPI_PROJECT_PATH
# Start the app on the target host; CTRL+C stops the app
ssh -t $RPI_HOST "export QUARKUS_LAUNCH_DEVMODE=true; cd $RPI_PROJECT_PATH; java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 -Dquarkus.live-reload.password=foo -jar quarkus-app/quarkus-run.jar" (2) (3)
1 | 可变应用程序可以在开发模式下启动。 |
2 | 我们将 QUARKUS_LAUNCH_DEVMODE 环境变量设置为 true ,以便在开发模式下启动应用程序。 |
3 | -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 部分启用了调试模式,因此您也可以以与本地运行相同的方式调试您的应用程序。唯一不同的是,您需要在附加调试器时使用树莓派的主机/地址。 |
另请参阅示例应用程序中的build-deploy-mutable.sh 文件。 |
请记住,您的 Quarkus 应用程序必须包含 quarkus-vertx-http 扩展才能支持远程开发模式。如果您在生产环境中不需要此扩展,可以添加一个仅在开发期间激活的具有此依赖项的 Maven 配置文件。另请参阅示例应用程序中的pom.xml 文件。 |
一旦我们运行了脚本,远程端就会启动并监听。这比以前花费的时间要长一些,因为我们在开发模式下启动应用程序。而且可变应用程序包含 Quarkus 的部署部分,因此它们会占用更多磁盘空间。
让我们运行本地部分并连接到远程主机
mvn quarkus:remote-dev -Dquarkus.package.jar.type=mutable-jar -Dquarkus.live-reload.password=foo -Dquarkus.live-reload.url=http://$RPI_HOST:8080 (1)
1 | RPI_HOST 是一个环境变量,它定义了目标树莓派的主机/地址。 |
另请参阅示例应用程序中的run-remote-dev-mode.sh 文件。 |
好了。现在每次您向应用程序发送 HTTP 请求时,都应该在远程应用程序中看到您在本地所做的任何更改。如果您的应用程序没有公开 HTTP 端点,您可以简单地在执行部署脚本的终端中按 s
。这将刷新远程应用程序。
出于安全原因,目前在远程开发模式下无法使用 Dev UI。但是,有一个公开的 issue 可以重新引入 Dev UI,可能会通过一个配置项来控制。 |
第四步 - 生产运行
当然,您可以在目标树莓派上的 JVM 中运行您的 Quarkus 应用程序。但是,树莓派的内存量并不大。那么原生镜像呢?这可能是一个完美的解决方案。不过有一个问题——树莓派是基于 ARM 的单板计算机(树莓派 4 型号 B 的 aarch64
架构)。但您的机器可能是基于 Intel 的(x86_64
架构)。换句话说,通常您无法构建您机器的原生镜像并在树莓派上运行。
选项 A - 在树莓派上构建原生镜像
理论上,您可以直接在树莓派上构建原生镜像。但我没有成功,并在我的配备 2GB RAM 的树莓派 4 型号 B 上收到了臭名昭著的“Native Image 构建过程内存不足。请确保您的构建系统有更多可用内存。”错误消息。但是,如果您拥有内存更多、时间充裕且带有良好主动式冷却器的型号,这应该是可行的。
感觉勇敢?好的,您已被警告。但首先,您需要将您的树莓派变成一台功能齐全的开发机器。简单来说,仅仅 JDK 是不够的。您需要一个构建工具,例如 Maven。您还需要 GraalVM native-image,或 Mandrel native-image,或 Docker/Podman 来在容器中构建原生镜像。可能还需要 Git 来 checkout 您的项目。一旦准备就绪,就很简单了。
mvn clean build -Dnative
因此,这种方法的缺点是显而易见的。还有其他选择吗?
选项 B - 使用 QEMU
您也可以尝试使用基于 ARM 的容器镜像在容器中构建原生镜像。Quarkus 提供了可用于此任务的多平台构建器镜像。
以下步骤适用于已安装 Docker 的 Linux 机器(Ubuntu 22.04),目标环境为树莓派 4 型号 B,操作系统为 Lite 12 (Bookworm)。 |
sudo apt-get install binfmt-support qemu-user-static (1)
mvn clean package -Dnative -DskipTests -Dquarkus.native.container-build=true -Dquarkus.native.container-runtime-options=--platform=linux/arm64 -Dquarkus.native.builder-image=quay.io/quarkus/ubi-quarkus-mandrel-builder-image:23.1.5.0-Final-java21-arm64 (2) (3) (4)
1 | 我们首先需要安装 qemu。 |
2 | -Dquarkus.native.container-build=true 指示 Quarkus 使用容器构建原生镜像。 |
3 | -Dquarkus.native.container-runtime-options=--platform=linux/arm64 指示 Docker 使用 QEMU 模拟 ARM 环境。 |
4 | -Dquarkus.native.builder-image=quay.io/quarkus/ubi-quarkus-mandrel-builder-image:23.1.5.0-Final-java21-arm6 指定了应使用基于 ARM 的容器镜像来构建原生镜像。 |
另请参阅示例应用程序中的build-native-image.sh 文件。 |
这种方法的缺点是它非常慢。使用通用硬件从示例应用程序构建原生镜像大约需要 20 分钟。另一方面,您通常只需要为生产环境构建原生镜像。因此,这似乎是可以接受的。