Deploying to Heroku

本指南将介绍如何基于 Quarkus 的 Web 应用程序部署为 Heroku 的 Web-dyno。 本指南涵盖:

  • Update Quarkus HTTP Port

  • Install the Heroku CLI

  • 将应用程序部署到 Heroku

  • 将应用程序作为容器镜像部署到 Heroku

  • Using Docker

  • Using Podman

  • 将本机应用程序作为容器镜像部署到 Heroku

Prerequisites

如要完成本指南,您需要:

  • Roughly 15 minutes

  • An IDE

  • 安装了 JDK 17+,已正确配置 JAVA_HOME

  • Apache Maven ${proposed-maven-version}

  • 如果你想使用 Quarkus CLI, 则可以选择使用

  • 如果你想构建一个本机可执行文件(或如果你使用本机容器构建,则使用 Docker),则可以选择安装 Mandrel 或 GraalVM 以及 configured appropriately

Introduction

Heroku 是平台即服务 (PaaS),它使开发人员能够完全在云中构建、运行和操作应用程序。它支持多种语言,如 Java、Ruby、Node.js、Scala、Clojure、Python、PHP 和 Go。此外,它还提供一个容器注册表,可用于部署预构建的容器镜像。

Heroku 可通过以下不同的方式用于运行 Quarkus 应用程序:

  • 作为普通 Java 程序,在 Heroku 的环境定义的容器中运行

  • 作为容器化的 Java 程序,在由 Quarkus 构建过程定义的容器中运行

  • 作为容器化的本机程序,在由 Quarkus 构建过程定义的容器中运行

所有这三种方法都需要知道 Heroku 分配给它的端口,以处理流量。幸运的是,为此提供了一个动态配置属性。

Common project setup

本指南将以在 Getting Started guide中开发的应用程序作为输入。

确保您已准备好入门应用程序,或克隆 Git 存储库:git clone $${quickstarts-base-url}.git,或下载一个 $${quickstarts-base-url}/archive/main.zip[存档]。解决方案位于 `getting-started`目录中。

Heroku 可以响应存储库中的更改,运行 CI 并在代码更改时重新部署应用程序。因此,我们从现有的有效存储库开始。

此外,请确保你的 Heroku CLI 正常工作:

heroku --version
heroku login

Prepare the Quarkus HTTP Port

Heroku 会选择一个随机端口,并将其分配给最终运行你 Quarkus 应用程序的容器。该端口作为环境变量在 `$PORT`下可用。在所有部署场景中使 Quarkus 意识到它的最简单方法是使用以下配置:

quarkus.http.port=${PORT:8080}

这可以理解为:“如果这是一个已定义的变量,则在 $PORT`上侦听,否则在 8080 上侦听(与通常的情况一样)。”运行以下命令将其添加到你的 `application.properties

echo "quarkus.http.port=\${PORT:8080}" >> src/main/resources/application.properties
git commit -am "Configure the HTTP Port."

Deploy the repository and build on Heroku

第一个变体使用 Quarkus Maven 构建来创建 _quarkus-app_应用程序结构,其中包含可运行的“fast-jar”以及在 Heroku 的构建基础设施内需要的所有库,然后部署该结果,另一个变体使用本地构建过程来创建优化容器。

对于第一个变体,你的应用程序根目录中需要两个其他文件:

  • system.properties,用于配置 Java 版本

  • Procfile,用于配置 Heroku 启动你的应用程序的方式

Quarkus 需要 JDK 17,所以我们首先指定它:

echo "java.runtime.version=17" >> system.properties
git add system.properties
git commit -am "Configure the Java version for Heroku."

我们将部署一个 Web 应用程序,因此我们需要在 Heroku Procfile 中配置类型 web 的应用程序,如下所示:

echo "web: java \$JAVA_OPTS -jar target/quarkus-app/quarkus-run.jar" >> Procfile
git add Procfile
git commit -am "Add a Procfile."

您的应用程序应该已经可以通过 heroku local web 运行。

让我们在您的账户中创建一个应用程序并向其部署该存储库:

heroku create
git push heroku master
heroku open

该应用程序将有一个生成的名称,终端应该输出该名称。 heroku open 打开您的默认浏览器以访问您的新应用程序。

要通过 curl 访问 REST 端点,请运行:

APP_NAME=`heroku info | grep  "=== .*" |sed "s/=== //"`
curl $APP_NAME.herokuapp.com/hello

当然,您还可以使用 Heroku CLI 将此存储库连接到您的 GitHub 账户,但这不是本指南的讨论范围。

Deploy as container

推送完整容器的优势在于,我们可以完全控制其内容,甚至可以选择部署一个在 GraalVM 上运行本机可执行文件的容器。

首先,登录到 Heroku 的容器注册表:

heroku container:login

我们需要向我们的项目添加一个扩展,以通过 Quarkus Maven 插件构建容器映像:

mvn quarkus:add-extension -Dextensions="container-image-docker"
git add pom.xml
git commit -am "Add container-image-docker extension."

我们要构建的映像需要以符合 Heroku 的注册表和部署的方式进行命名。我们可以通过 heroku info 获得生成的名称,并将其传递到(本地)构建:

APP_NAME=`heroku info | grep  "=== .*" |sed "s/=== //"`
mvn clean package\
  -Dquarkus.container-image.build=true\
  -Dquarkus.container-image.group=registry.heroku.com/$APP_NAME\
  -Dquarkus.container-image.name=web\
  -Dquarkus.container-image.tag=latest

Push and release the image

你现在可以推送映像并发布它。

初始推送相当大,因为映像的所有层都需要传输。后续推送会较小。

Pushing through Docker

安装 Docker 后,这些步骤很简单:

docker push registry.heroku.com/$APP_NAME/web
heroku container:release web --app $APP_NAME

Pushing through Podman

如果您想将 Podman 用作 Docker 的替代品,您将遇到一些问题,因为 Heroku CLI 依赖于 Docker,并且不支持 OCI 格式。但是,这些问题有可能的解决方案。

Example 1. Cannot find docker, please ensure docker is installed.

很明显,问题在于 heroku-cli 找不到 docker。这很容易解决,因为 podman cli 与 docker 兼容。我们只需要从 podman 创建一个到 docker 的符号链接:

sudo ln -s $(which podman) /usr/local/bin/docker
Example 2. Error writing manifest: Error uploading manifest latest to registry.heroku.com/$APP_NAME/web: unsupported

我们必须使用一种解决方法,以便通过 Podman 和 Heroku CLI 以所需的格式(v2s2 - Docker 映像清单版本 2,架构 2)推送和发布我们的应用程序,而不是执行常规的 podman 推送(OCI 格式)。还需要 skopeo

CONTAINER_DIR="target/container-dir"
mkdir $CONTAINER_DIR
podman push --format=v2s2 "registry.heroku.com/$APP_NAME/web" dir:$CONTAINER_DIR
skopeo --debug copy dir:$CONTAINER_DIR "docker://registry.heroku.com/$APP_NAME/web:latest"
heroku container:release web --app "$APP_NAME"
rm -rf $CONTAINER_DIR

Check the logs

您可以且应该检查日志以查看您的应用程序是否现在确实正在从容器运行:

heroku logs --app $APP_NAME --tail

Deploy as native application inside a container

在将我们的应用程序部署为容器时,我们获得的最大优势是部署包含本机编译应用程序的容器。为什么?因为当没有传入流量时,Heroku 将停止或让应用程序进入休眠状态。本机应用程序将从休眠状态更快地唤醒。

该过程几乎完全相同。我们选择在一个本地容器内编译一个本机映像,以便我们不必在本地安装 GraalVM:

APP_NAME=`heroku info | grep  "=== .*" |sed "s/=== //"`
mvn clean package\
  -Dquarkus.container-image.build=true\
  -Dquarkus.container-image.group=registry.heroku.com/$APP_NAME\
  -Dquarkus.container-image.name=web\
  -Dquarkus.container-image.tag=latest\
  -Dnative\
  -Dquarkus.native.container-build=true

然后再次使用 Docker 或 Podman 推入并发布(见上文),然后检查日志。