Deploying to Heroku

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

In this guide you will learn how to deploy a Quarkus based web application as a web-dyno to Heroku.

本指南涵盖:

This guide covers:

  • Update Quarkus HTTP Port

  • Install the Heroku CLI

  • Deploy the application to Heroku

  • Deploy the application as container image to Heroku

  • Using Docker

  • Using Podman

  • Deploy the native application as container image to Heroku

Prerequisites

Unresolved directive in deploying-to-heroku.adoc - include::{includes}/prerequisites.adoc[]* A Heroku Account. 免费账户适用。* Heroku CLI installed

Unresolved directive in deploying-to-heroku.adoc - include::{includes}/prerequisites.adoc[] * A Heroku Account. Free accounts work. * Heroku CLI installed

Introduction

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

Heroku is a platform as a service (PaaS) that enables developers to build, run, and operate applications entirely in the cloud. It supports several languages like Java, Ruby, Node.js, Scala, Clojure, Python, PHP, and Go. In addition, it offers a container registry that can be used to deploy prebuilt container images.

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

Heroku can be used in different ways to run a Quarkus application:

  • As a plain Java program running in a container defined by Heroku’s environment

  • As a containerized Java program running in a container defined by the Quarkus build process

  • As a containerized native program running in a container defined by the Quarkus build process

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

All three approaches need to be aware of the port that Heroku assigns to it to handle traffic. Luckily, there’s a dynamic configuration property for it.

Common project setup

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

This guide will take as input an application developed in the Getting Started guide.

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

Make sure you have the getting-started application at hand, or clone the Git repository: git clone {quickstarts-clone-url}, or download an {quickstarts-archive-url}[archive]. The solution is located in the getting-started directory.

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

Heroku can react on changes in your repository, run CI and redeploy your application when your code changes. Therefore, we start with a valid repository already.

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

Also, make sure your Heroku CLI is working:

heroku --version
heroku login

Prepare the Quarkus HTTP Port

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

Heroku picks a random port and assigns it to the container that is eventually running your Quarkus application. That port is available as an environment variable under $PORT. The easiest way to make Quarkus in all deployment scenarios aware of it is using the following configuration:

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

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

This reads as: "Listen on $PORT if this is a defined variable, otherwise listen on 8080 as usual." Run the following to add this to your 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 的构建基础设施内需要的所有库,然后部署该结果,另一个变体使用本地构建过程来创建优化容器。

The first variant uses the Quarkus Maven build to create the quarkus-app application structure containing the runnable "fast-jar" as well as all libraries needed inside Heroku’s build infrastructure and then deploying that result, the other one uses a local build process to create an optimized container.

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

For the first variant, two additional files are needed in your application’s root directory:

  • system.properties to configure the Java version

  • Procfile to configure how Heroku starts your application

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

Quarkus needs JDK 17, so we specify that first:

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

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

We will deploy a web application so we need to configure the type web in the Heroku Procfile like this:

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 运行。

Your application should already be runnable via heroku local web.

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

Let’s create an application in your account and deploy that repository to it:

heroku create
git push heroku master
heroku open

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

The application will have a generated name and the terminal should output that. heroku open opens your default browser to access your new application.

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

To access the REST endpoint via curl, run:

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

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

Of course, you can use the Heroku CLI to connect this repo to your GitHub account, too, but this is out of scope for this guide.

Deploy as container

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

The advantage of pushing a whole container is that we are in complete control over its content and maybe even choose to deploy a container with a native executable running on GraalVM.

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

First, login to Heroku’s container registry:

heroku container:login

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

We need to add an extension to our project to build container images via the Quarkus Maven plugin:

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

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

The image we are going to build needs to be named accordingly to work with Heroku’s registry and deployment. We get the generated name via heroku info and pass it on to the (local) build:

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

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

You can now push the image and release it.

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

The initial push is rather big, as all layers of the image need to be transferred. The following pushes will be smaller.

Pushing through Docker

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

With Docker installed, these steps are simple:

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

Pushing through Podman

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

When you want to use Podman as a drop-in-replacement for Docker, you will have some problems because the Heroku CLI depends on Docker and doesn’t support the OCI format. But there are possible solutions for these problems.

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

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

The problem is obviously that the heroku-cli can’t find docker. This is quite easy to resolve, because the podman cli is docker-compatible. We just need to create a symlink from podman to 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

Instead of doing a normal podman push (OCI format) we must use a workaround in order to push and release our app through Podman and the Heroku CLI in the desired format (v2s2 - Docker Image Manifest Version 2, Schema 2). Also skopeo is needed.

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

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

You can and should check the logs to see if your application is now indeed running from the container:

heroku logs --app $APP_NAME --tail

Deploy as native application inside a container

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

The biggest advantage we take when deploying our app as a container is to deploy a container with the natively compiled application. Why? Because Heroku will stop or sleep the application when there’s no incoming traffic. A native application will wake up much faster from its sleep.

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

The process is pretty much the same. We opt in to compiling a native image inside a local container, so that we don’t have to deal with installing GraalVM locally:

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 推入并发布(见上文),然后检查日志。

After that, push and release again using Docker or Podman (see above) and check the logs.