vlambda博客
学习文章列表

读书笔记《hands-on-cloud-native-applications-with-java-and-quarkus》创建应用程序的容器图像

Creating a Container Image of Your Application

在上一章中,我们通过运行传统的 JVM 应用程序,然后将其转换为原生构建,从而了解了 Quarkus 应用程序的强大功能。不过,Quarkus 不仅仅是精简的可执行文件和低资源使用率,因此,在本章中,我们将继续学习如何创建应用程序的容器镜像,然后将其部署到 Kubernetes 原生环境中。为此,我们的待办事项清单包括安装 Docker 工具和 OpenShift 的社区版本,称为 Kubernetes Origin 社区分发版,或简称为 OKD。然后,我们将学习如何扩展我们的应用程序,以便我们可以进一步提高其响应时间。

在本章中,我们将介绍以下主题:

  • Setting up Docker in your environment
  • Starting a Quarkus application in a container
  • Running a native executable in a container
  • Deploying your container image on OpenShift
  • Scaling our application to improve its throughput

Setting up Docker

Docker 是一种工具,可让我们简化环境中容器的创建和执行。反过来,每个容器将应用程序及其依赖项包装成一个标准化单元,其中包括运行所需的一切,即系统工具、代码和其他所需的库。这保证了您的应用程序将始终通过共享一个简单的容器映像以相同的方式执行。 Docker 有两个版本:

  • Community Edition (CE): The Docker CE, which we will be using in this book, is ideal for developers and small teams looking for a quick start with Docker and container-based applications.
  • Enterprise Edition (EE): The EE features additional capabilities such as a certified infrastructure, image management, and image security scanning.

尽管我们将使用 Docker 的社区版本,但这不会降低您的应用程序的全部潜力,因为我们将能够通过原生 Kubernetes 平台利用高级容器功能,这是运行关键业务应用程序的理想解决方案规模化生产。

https://docs.docker.com/install/ 中完整记录了 Docker 的安装。简而言之,您可以根据需要遵循几种安装策略:

让我们按照以下步骤继续安装 Docker:

  1. The automated script can be downloaded from https://get.docker.com/, as follows:
$ curl -fsSL https://get.docker.com -o get-docker.sh
  1. Now, execute it with the following command:
$ sh get-docker.sh
Important! Just like any other shell script, verify its content before executing it! Its content needs to match with the install.sh script located at https://github.com/docker/docker-install. If the content doesn't match, verify whether the automated script is still being maintained by going to the Docker install page.
  1. If you would like to run Docker as a non-privileged user, you should consider adding your user to the docker group by executing the following command:
$ sudo usermod $(whoami) -G docker -a
  1. For this to take effect, you will need to log out and log in again. We can check that our user is now in the Docker group by checking the output of the following command:
$ groups $(whoami)
  1. The output should include docker in the list of groups. Now, you can verify that you can run Docker commands without a root user (or sudo):
$ docker run hello-world
  1. The preceding command will pull the hello-world test image from the Docker repository, and run it in a container. When the test image starts, it prints an informative message and exits:
Status: Downloaded newer image for hello-world:latest
Hello from Docker!

此消息表明您的安装似乎工作正常。

Running Quarkus applications in a container

安装 Docker 后,您就可以从 Java 或本机可执行应用程序构建 Docker 映像了。为此,我们将快速构建另一个简单的应用程序,该应用程序检查一些环境变量以确定应用程序运行所在的容器 ID。

本章的源代码位于本书 GitHub 存储库的 Chapter03/hello-okd 文件夹中。我们建议您在继续之前将项目导入您的 IDE。

让我们从 REST 端点类 (HelloOKD) 开始深入代码,它从 上下文和依赖注入 (CDI) 返回一些信息服务:

 package com.packt.quarkus.chapter3;
 
 import javax.ws.rs.GET;
 import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.MediaType;
 import javax.inject.Inject;
 
 @Path("/getContainerId")
 public class HelloOKD {
     
     @Inject
     ContainerService containerService;
 
     @GET
     @Produces(MediaType.TEXT_PLAIN)
     public String hello() {
         return "You are running on "  + 
           containerService.getContainerId();
     }
 } 

以下代码用于注入 REST 端点的 ContainerService 类:

package com.packt.quarkus.chapter3;
 
import javax.enterprise.context.ApplicationScoped;
 
@ApplicationScoped
public class ContainerService {
 
     public String getContainerId() {
         return System.getenv().getOrDefault("HOSTNAME", "unknown");
     }
}
 

此示例显示了对注入对象使用 CDI @ApplicationScoped 注释。定义为 @ApplicationScoped 的对象在应用程序的生命周期内创建一次。在我们的例子中,它返回 HOSTNAME 环境变量,默认为 Docker 容器 ID。

为了测试我们的简单 REST 服务,以下 HelloOKDTest 已包含在 src/test/java 路径下的项目中。通过它的 testHelloEndpoint 方法,我们验证 REST 调用的状态码是成功的:

package com.packt.quarkus.chapter3;
 
import io.quarkus.test.junit.QuarkusTest;
import org.junit.jupiter.api.Test;
 
import static io.restassured.RestAssured.given;
import static org.hamcrest.CoreMatchers.is;
 
@QuarkusTest
public class HelloOKDTest {
 
    @Test
    public void testHelloEndpoint() {
        given()
          .when().get("/getContainerId")
          .then()
             .statusCode(200);
   }
 
} 

在我们开始我们的 Docker 之旅之前,让我们检查一下前面的测试是否通过。当我们运行项目的 install 目标时,测试阶段将自动启动:

$ mvn install

成功的测试应产生以下日志:

[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 3.174 s - in com.packt.quarkus.chapter3.HelloOKDTest
2019-11-17 19:15:16,227 INFO  [io.quarkus] (main) Quarkus stopped in 0.041s
[INFO] 
[INFO] Results:
[INFO] 
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

现在,让我们继续看看 Docker。如果您查看 src/main/docker 文件夹,您会注意到一些文件已自动添加到您的项目中:

$ tree src/main/docker
 src/main/docker
 ├── Dockerfile.jvm
 └── Dockerfile.native

列表中的第一个文件 Dockerfile.jvm 是专门为 JVM 环境编写的 Dockerfile。其内容如下:

FROM fabric8/java-alpine-openjdk8-jre
ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
ENV AB_ENABLED=jmx_exporter
COPY target/lib/* /deployments/lib/
COPY target/*-runner.jar /deployments/app.jar
EXPOSE 8080

# run with user 1001 and be prepared for be running in OpenShift too
RUN adduser -G root --no-create-home --disabled-password 1001 \
  && chown -R 1001 /deployments \
  && chmod -R "g+rwX" /deployments \
  && chown -R 1001:root /deployments
USER 1001

ENTRYPOINT [ "/deployments/run-java.sh" ]
A Dockerfile is a plain text file that contains a set of commands that we can use to assemble an image so that it can be executed by Docker. A Dockerfile needs to match with a specific format and set of instructions that have been documented in the Dockerfile reference ( https://docs.docker.com/engine/reference/builder/).

在我们的示例中,Dockerfile 包含使用 Fabric8 Java Base Image 构建 Java 环境的说明并启用 JMX 导出器(https:// /github.com/prometheus/jmx_exporter) 以公开流程指标。现在,我们将为我们的容器构建镜像,如下所示:

$ docker build -f src/main/docker/Dockerfile.jvm -t quarkus/hello-okd .

在您的控制台中,您可以验证 Docker 拉取过程将被触发,并且 Dockerfile 中的所有命令都有助于构建 quarkus/hello-okd 容器映像的中间层:

Step 1/9 : FROM fabric8/java-alpine-openjdk8-jre
Trying to pull repository docker.io/fabric8/java-alpine-openjdk8-jre ... 
sha256:b27090f384b30f0e3e29180438094011db1fa015bbf2e69decb921bc2486604f: Pulling from docker.io/fabric8/java-alpine-openjdk8-jre
9d48c3bd43c5: Pull complete 
. . . . . . .
Status: Downloaded newer image for docker.io/fabric8/java-alpine-openjdk8-jre:latest
 ---> fe776eec30ad
Step 2/9 : ENV JAVA_OPTIONS "-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
 ---> Running in c5d31bae859e
 ---> 01c99aac17db
Removing intermediate container c5d31bae859e
Step 3/9 : ENV AB_ENABLED jmx_exporter
 ---> Running in c867300baaf0
 ---> 52deadd505bc
Removing intermediate container c867300baaf0
Step 4/9 : COPY target/lib/* /deployments/lib/
 ---> aa11b2b30f16
Removing intermediate container dcbd13a3ae0f
Step 5/9 : COPY target/*-runner.jar /deployments/app.jar
 ---> 2f2e1218eff8
Removing intermediate container 4b3861ba33d9
Step 6/9 : EXPOSE 8080
 ---> Running in 93eebaee5495
 ---> 4008a4fdbb9c
Removing intermediate container 93eebaee5495
Step 7/9 : RUN adduser -G root --no-create-home --disabled-password 1001   && chown -R 1001 /deployments   && chmod -R "g+rwX" /deployments   && chown -R 1001:root /deployments
 ---> Running in 2a86b3aeaeae
 ---> b21be209f09e
Removing intermediate container 2a86b3aeaeae
Step 8/9 : USER 1001
 ---> Running in fac8d64b8793
 ---> 94077bb5396a
Removing intermediate container fac8d64b8793
Step 9/9 : ENTRYPOINT /deployments/run-java.sh
 ---> Running in 7bacd02dd631
 ---> 9f269b2041d3
Removing intermediate container 7bacd02dd631
Successfully built 9f269b2041d3

现在,让我们通过执行 docker images 命令检查本地 Docker 存储库中的镜像是否可用:

$ docker images | grep hello-okd

您应该看到以下输出:

quarkus/hello-okd                                        latest               9f269b2041d3        2 minutes ago       98.9 MB

如您所见,本地缓存的映像现在可以在本地 Docker 存储库中使用。您可以使用以下命令运行它:

$ docker run -i --rm -p 8080:8080 quarkus/hello-okd

run 命令中,我们添加了一些额外的标志,例如 --rm,它会在容器退出后自动删除。 -i 标志将容器连接到终端。最后,-p 标志将端口8080 暴露在外部,从而映射到主机上的端口8080

由于我们将在主机端口上导出服务,即 8080,因此请检查是否没有其他服务正在使用该端口!您应该能够在控制台上收集此输出,这是代理启动的日志,底部是我们的 hello-okd 服务的日志:

exec java -Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager -javaagent:/opt/agent-bond/agent-bond.jar=jmx_exporter{{9779:/opt/agent-bond/jmx_exporter_config.yml}} -XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -XX:MinHeapFreeRatio=20 -XX:MaxHeapFreeRatio=40 -XX:+ExitOnOutOfMemoryError -cp . -jar /deployments/app.jar
2019-11-11 10:29:12,505 INFO  [io.quarkus] (main) hello-okd 1.0-SNAPSHOT (running on Quarkus 1.0.0.Final) started in 0.666s. Listening on: http://0.0.0.0:8080
2019-11-11 10:29:12,525 INFO  [io.quarkus] (main) Profile prod activated. 
2019-11-11 10:29:12,525 INFO  [io.quarkus] (main) Installed features: [cdi, resteasy]

Docker 进程现在正在运行,可以通过以下命令确认。此命令将显示运行容器的 Image 名称:

 $ docker ps --format '{{.Image}}'

以下是运行上述命令的输出:

quarkus/hello-okd

您可以使用以下命令测试应用程序是否在容器中运行:

$ curl http://localhost:8080/getContainerId

您应该能够在输出中看到 docker ps 命令打印的相同容器 ID:

You are running on a333f52881a1

现在,让我们重建我们的容器镜像,以便我们可以使用本机可执行文件。

Running the native executable process in a container

正如我们所看到的,Quarkus Maven 插件还生成了 src/main/docker/Dockerfile.native,它可以用作模板,以便我们可以在容器中运行我们的原生可执行文件。这是这个文件的内容:

FROM registry.access.redhat.com/ubi8/ubi-minimal
WORKDIR /work/
COPY target/*-runner /work/application
RUN chmod 775 /work
EXPOSE 8080
CMD ["./application", "-Dquarkus.http.host=0.0.0.0"]

由于不需要使用 JDK 层来启动我们的应用程序,我们容器的基础层将是一个精简的 RHEL 映像,称为 ubi-minimal

Red Hat Universal Base Images (UBI) 是符合 OCI 的容器操作系统映像,包含免费运行时可自由再分发的语言和其他软件包。

在构建 Docker 映像之前,通过包含 -Dnative-image.docker-build 选项来打包您的应用程序:

$ mvn package -Pnative -Dnative-image.docker-build=true 

检查构建是否成功,然后使用以下命令构建映像:

$ docker build -f src/main/docker/Dockerfile.native -t quarkus/hello-okd-native .

在控制台中,您将看到容器的创建方式与 Java 应用程序的创建方式大致相同,但使用不同的初始映像 (ubi-minimal):

Sending build context to Docker daemon 32.57 MB
Step 1/6 : FROM registry.access.redhat.com/ubi8/ubi-minimal
 ---> 8c980b20fbaa
Step 2/6 : WORKDIR /work/
 ---> Using cache
 ---> 0886c0b19e07
Step 3/6 : COPY target/*-runner /work/application
 ---> 7e66ae6447ce
Removing intermediate container 2ddc91992af5
Step 4/6 : RUN chmod 775 /work
 ---> Running in e8d6ffbbc14e
 ---> 780f6562417d
Removing intermediate container e8d6ffbbc14e
Step 5/6 : EXPOSE 8080
 ---> Running in d0d48475565f
 ---> 554f79b4cbb2
Removing intermediate container d0d48475565f
Step 6/6 : CMD ./application -Dquarkus.http.host=0.0.0.0
 ---> Running in e0206ff3971f
 ---> 33021bdaf4a4
Removing intermediate container e0206ff3971f
Successfully built 33021bdaf4a4

让我们检查一下镜像在 Docker 存储库中是否可用:

$ docker images | grep hello-okd-native

您应该看到以下输出:

quarkus/hello-okd-native                                        latest               33021bdaf4a4        59 seconds ago      113 MB

quarkus/hello-okd-native 图像现在可用。现在,使用以下命令运行容器映像:

$ docker run -i --rm -p 8080:8080 quarkus/hello-okd-native

控制台上不会显示额外的 JVM 层。在这里,我们可以看到我们的服务仅在几毫秒内启动:

2019-11-11 11:59:46,817 INFO  [io.quarkus] (main) hello-okd 1.0-SNAPSHOT (running on Quarkus 1.0.0.CR1) started in 0.005s. Listening on: http://0.0.0.0:8080
2019-11-11 11:59:46,817 INFO  [io.quarkus] (main) Profile prod activated. 
2019-11-11 11:59:46,817 INFO  [io.quarkus] (main) Installed features: [cdi, resteasy]y]

验证应用程序在请求 getContainerId URI 时是否返回容器 ID:

curl http://localhost:8080/getContainerId

在我们的例子中,输出如下:

You are running on ff6574695d68

伟大的!我们只是设法将本机应用程序作为 Docker 映像运行。我们的下一个任务是将我们的镜像部署到 Kubernetes 原生环境中。

Deploying Quarkus applications on a Kubernetes-native platform

现在我们已经验证了在容器中运行 Quarkus 应用程序是多么简单,我们将把我们的应用程序部署到 Kubernetes 原生环境中。即使 Kubernetes 本身足以编排您的服务,您也可以通过安装 OpenShift 大大扩展其功能。除了利用 Kubernetes 功能,OpenShift 还提供以下功能:

  • Better management of container images through the use of image streams, which decouple the actual image from your application
  • Advanced CI/CD capabilities to make the whole CI/CD workflow a lot easier, also including a Jenkins certified image
  • A simpler build process as it's easier to build a docker image inside OpenShift through the BuildConfig component, which can perform automated image builds and push them to its internal registry
  • A wealth of certified plugins, such as storage/networking/monitoring plugins
  • Support for multitenancy through the Resource Scheduler component, which will determine where to run Pods
  • A large set of certified databases and middleware products
  • A simpler UI web application from where you can easily manage your cluster of services and create new applications

OpenShift 有多种版本:

  • Red Hat OpenShift Container Platform (requires a subscription): The supported Kubernetes platform that lets you build, deploy, and manage your container-based applications consistently across cloud and on-premises infrastructures.
  • Red Hat OpenShift Dedicated (requires a subscription): This provides a supported, private, high-availability Red Hat OpenShift cluster hosted on Amazon Web Services or Google Cloud Platform.
  • Red Hat OpenShift Online (several plans are available): It provides on-demand access to Red Hat OpenShift so that you can manage containerized applications.
  • Origin Community Distribution of Kubernetes (OKD): This is the Community version of the Red Hat OpenShift Container Platform that you can freely use in any environment.

就本书而言,我们将安装 Minishift,这是 OKD 的简化版本,以在虚拟机中启动单节点集群。这是开始并在本地计算机上尝试 OpenShift 的最简单方法。

当前版本的 Minishift 基于 Openshift 的 3.x 版本。强烈建议将大多数高级示例迁移到 Openshift 4.x 平台,例如开发基于云的反应式应用程序,这将在本书的最后一章中讨论。

Minishift 的安装非常简单:您需要做的就是下载并解压缩它的最新发行版。但是,确实存在一些先决条件,因为您需要通过安装管理程序来准备系统,这是启动配置 OKD 的虚拟环境所必需的。

Installing Minishift

在本节中,我们将学习如何在运行 Fedora 的机器上安装 Minishift。如果您没有在您的机器上运行 Fedora,您可以在 https://docs.okd.io/latest/minishift/getting-started/preparing-to-install.html

首先,您需要安装管理各种虚拟化平台所需的两个内核模块(libvirtqemu-kvm)。这些符合基于内核的虚拟机 (KVM) 技术。请按照以下步骤执行此操作:

  1. From the shell, execute the following command:
$ sudo dnf install libvirt qemu-kvm
  1. Then, to run the virtualization platform with your user, add it to the libvirt group:
$ sudo usermod -a -G libvirt $(whoami)
  1. Next, configure the group membership with the user you are currently logged in as:
$ newgrp libvirt
  1. Finally, you will need to download and make the KVM driver for your Docker machine executable. As the root user, execute the following commands:
$ sudo curl -L https://github.com/dhiltgen/docker-machine-kvm/releases/download/v0.10.0/docker-machine-driver-kvm-centos7 -o /usr/local/bin/docker-machine-driver-kvm

$ sudo chmod +x /usr/local/bin/docker-machine-driver-kvm
  1. Once your user has been set up, download and unpack the latest Minishift release package from the official GitHub repository: https://github.com/minishift/minishift/releases. At the time of writing, this is the latest version of Minishift that can be downloaded:
$ wget https://github.com/minishift/minishift/releases/download/v1.33.0/minishift-1.33.0-linux-amd64.tgz 
  1. Once the download has completed, unpack the .tar file into a destination folder. For example, to unpack it into your home (~) directory, execute the following command:
$ tar xvf minishift-1.33.0-linux-amd64.tgz -C ~

在这个包中,您将找到 minishift 可执行文件,可用于启动您的 Minishift 环境。

  1. Next, we will run the minishift command to start the installation process:
$ ./minishift start
  1. Once complete, you should see a message similar to the following in your Terminal:
-- Starting profile 'minishift'
-- Check if deprecated options are used ... OK
-- Checking if https://github.com is reachable ... OK
-- Checking if requested OpenShift version 'v3.11.0' is valid ... OK
-- Checking if requested OpenShift version 'v3.11.0' is supported ... OK
-- Checking if requested hypervisor 'kvm' is supported on this platform ... OK
-- Checking if KVM driver is installed ...
. . . .
OpenShift server started.

The server is accessible via web console at:
https://192.168.42.103:8443/console
The server is accessible at:
https://192.168.42.190:8443
You are logged in as: User: developer Password:
To log in as administrator:
oc login -u system:admin

就是这样! Minishift 已安装在您的环境中!

建议您在 $PATH 环境变量中包含以下文件夹:

  • The folder where you have unpacked the minishift tool.
  • The folder where the oc client tool is located. This tool is a command-line utility that you can use to manage your Minishift cluster. Once you start up the cluster, this tool is copied to ~/.minishift/cache/oc/<oc-version>/linux.

因此,例如,如果您在主目录中解压 Minishift,请将 oc-version 替换为您的工具版本并执行以下命令:

export PATH=$PATH:~/minishift-1.33.0-linux-amd64:~/.minishift/cache/oc/<oc-version>/linux

您可以通过在默认浏览器中打开 OpenShift Web 控制台(在我们的示例中为 https://192.168.42.190:8443)或将 console 参数传递给minishift 工具:

$ minishift console

由于控制台在安全连接上运行,因此会警告您在浏览器中未找到签名证书。为您的浏览器添加一个安全例外,以便您登陆登录页面:

读书笔记《hands-on-cloud-native-applications-with-java-and-quarkus》创建应用程序的容器图像

使用 developer/developer 登录进入仪表板:

读书笔记《hands-on-cloud-native-applications-with-java-and-quarkus》创建应用程序的容器图像

恭喜!您已经安装了 Minishift 并对其进行了验证。下一步将在其上部署我们的示例应用程序。

Building and deploying a Quarkus application on OKD

Minishift 的仪表板包含一组模板,可用于快速构建我们的应用程序。在撰写本文时,还没有 Quarkus 模板。但是,我们可以轻松地将我们的映像构建和部署为一个二进制构建,它传达我们已经测试过的 Dockerfile。

二进制构建是一项允许开发人员从二进制源上传工件而不是从 Git 存储库 URL 拉取源的功能。

为此,我们将使用 oc 客户端工具,这是用于配置 OpenShift 及其对象的瑞士军刀。

The following set of commands is contained in the deploy-openshift.sh file, which is located in the Chapter03 directory of this book's GitHub repository. If you are impatient to see your application in the cloud, simply execute the script and check that the output matches what we've written in this paragraph.

我们需要做的第一件事是为我们的项目创建一个命名空间,它将在我们当前的 OpenShift 命名空间中创建。您可以使用以下命令创建 quarkus-hello-okd 命名空间:

$ oc new-project quarkus-hello-okd

我们需要做的第一件事是使用 oc new-build 命令定义一个二进制构建对象:

$ oc new-build --binary --name=quarkus-hello-okd -l app=quarkus-hello-okd

前面的命令将生成一个镜像二进制构建,该构建将被推送到 Minishift 的内部注册表中。以下输出描述了为此目的创建的资源:

* A Docker build using binary input will be created
* The resulting image will be pushed to image stream tag "quarkus-hello-okd:latest"
* A binary build was created, use 'start-build --from-dir' to trigger a new build

--> Creating resources with label app=quarkus-hello-okd ...
    imagestream.image.openshift.io "quarkus-hello-okd" created
    buildconfig.build.openshift.io "quarkus-hello-okd" created
--> Success

现在构建配置已经创建,我们可以通过查询 bc 别名(代表构建配置)来检查它的可用性:

$  oc get bc

您应该看到以下输出:

NAME                TYPE      FROM      LATEST
quarkus-hello-okd   Docker    Binary    0

事实上,二进制构建不包含对我们的 Dockerfile 的任何引用。我们可以使用 oc patch 命令添加此信息,这是一个有用的快捷方式,我们可以使用它来编辑资源。在我们的例子中,我们需要将引用 dockerStrategy 元素的 dockerfilePath 属性设置为我们的 Dockerfile 所在的位置。从 Quarkus 项目的根目录,执行以下命令:

$ oc patch bc/quarkus-hello-okd -p '{"spec":{"strategy":{"dockerStrategy":{"dockerfilePath":"src/main/docker/Dockerfile.native"}}}}'

将返回以下输出:

buildconfig.build.openshift.io/quarkus-hello-okd patched

如果您检查二进制构建描述,您将看到 Dockerfile 路径已包​​含在内:

$ oc describe bc/quarkus-hello-okd

输出有点冗长;但是,它应包含以下信息:

Strategy:           Docker
Dockerfile Path:    src/main/docker/Dockerfile.native

现在,我们准备开始构建过程,它将以项目的根文件夹 (.) 作为输入,并将 ImageStream 上传到您的 Minishift 环境中。执行以下命令:

$ oc start-build quarkus-hello-okd --from-dir=. --follow

输出将通知您映像已构建并推送到 Minishift 注册表:

Uploading finished
build.build.openshift.io/quarkus-hello-okd-1 started
Receiving source from STDIN as archive ...
Caching blobs under "/var/cache/blobs".
Pulling image registry.access.redhat.com/ubi8/ubi-minimal ...
. . . .
Writing manifest to image destination
Storing signatures
STEP 1: FROM registry.access.redhat.com/ubi8/ubi-minimal
STEP 2: WORKDIR /work/
2d94c8983e7ec259aa0e0207c66b0e48fdd9544f66e3c17724a10f838aaa50ab
STEP 3: COPY target/*-runner /work/application
a6d9d0a228023d29203c20de7bcfd9019de00f62b4a4c3fdc544648f8f61988a
STEP 4: RUN chmod 775 /work
e7bf7467d2191478f0cdccd9b480d10ebd19eeae254a7fa0608646cba28c5a97
STEP 5: EXPOSE 8080
29e6198d99e27508aa75cd073290f66fbca605b8ff95cbb6287b4ecbfc1807e5
STEP 6: CMD ["./application","-Dquarkus.http.host=0.0.0.0"]
6c44a4a542ea96450ed578afd4c1859564926405036f61fbbf2e3660660e5f5e
STEP 7: ENV "OPENSHIFT_BUILD_NAME"="quarkus-hello-okd-1" "OPENSHIFT_BUILD_NAMESPACE"="myproject"
9aa01e4bc2585a05b37e09a10940e326eec6cc998010736318bbe5ed1962503b
STEP 8: LABEL "io.openshift.build.name"="quarkus-hello-okd-1" "io.openshift.build.namespace"="myproject"
STEP 9: COMMIT temp.builder.openshift.io/myproject/quarkus-hello-okd-1:d5dafe08
5dafe14a20fffb50b151efbfc4871218a9b1a8516b618d5f4b3874a501d80bcd

Pushing image image-registry.openshift-image-registry.svc:5000/myproject/quarkus-hello-okd:latest ...
. . .
Successfully pushed image-registry.openshift-image-registry.svc:5000/myproject/quarkus-hello-okd@sha256:9fc48bb4b92081c415342407e8df41f38363a4bf82ad3a4319dddced19eff1b3
Push successful

作为概念证明,让我们使用别名 is 检查默认项目中可用的图像流列表:

$ oc get is

您应该看到以下输出:

NAME                IMAGE                                                             
quarkus-hello-okd   image-registry.openshift-image-registry.svc:5000/myproject/quarkus-hello-okd   

您的 ImageStream 现在可用。我们所要做的就是创建一个使用 ImageStream quarkus-hello-okd 作为输入的应用程序。这可以使用以下命令完成:

$ oc new-app --image-stream=quarkus-hello-okd:latest

现在,将创建资源。这将由结果输出确认:

--> Found image 5dafe14 (2 minutes old) in image stream "myproject/quarkus-hello-okd" under tag "latest" for "quarkus-hello-okd:latest"

    Red Hat Universal Base Image 8 Minimal 
    -------------------------------------- 
    The Universal Base Image Minimal is a stripped down image that uses microdnf as a package manager. This base image is freely redistributable, but Red Hat only supports Red Hat technologies through subscriptions for Red Hat products. This image is maintained by Red Hat and updated regularly.

    Tags: minimal rhel8

    * This image will be deployed in deployment config "quarkus
    -hello-okd"
    * Port 8080/tcp will be load balanced by service "quarkus
    -hello-okd"
      * Other containers can access this service through the hostname 
        "quarkus-hello-okd"
    * WARNING: Image "myproject/quarkus-hello-okd:latest" runs as
     the 'root' user which may not be permitted by your cluster  
    administrator

--> Creating resources ...
    deploymentconfig.apps.openshift.io "quarkus-hello-okd" created
    service "quarkus-hello-okd" created
--> Success
    Application is not exposed. You can expose services to the
    outside world by executing one or more of the commands below:
     'oc expose svc/quarkus-hello-okd' 
    Run 'oc status' to view your app.

现在,我们的应用程序已准备好使用。为了允许外部客户端访问它,我们需要通过一个路由对象来暴露它,如下:

$ oc expose svc/quarkus-hello-okd 

路由将被暴露,并显示以下日志:

route.route.openshift.io/quarkus-hello-okd exposed

我们可以使用以下命令验证路由地址,该命令使用 JSON 模板显示我们的 quarkus-hello-okd 路由的虚拟主机地址:

$ oc get route quarkus-hello-okd -o jsonpath --template="{.spec.host}"

在我们的例子中,可以通过以下地址访问该路由:

quarkus-hello-okd-myproject.192.168.42.5.nip.io 
请注意,路由的实际 IP 地址由管理程序根据您的网络配置确定,因此如果它与本示例中公开的地址不同,请不要感到惊讶。

您应该能够从 Web 控制台确认此信息,这表明应用程序已启动并正在运行,并且单个 Pod 已启动:

读书笔记《hands-on-cloud-native-applications-with-java-and-quarkus》创建应用程序的容器图像

如果您转到已分配的路由主机/端口(在我们的例子中,http://quarkus-hello-okd-myproject.192.168.42.5.nip.io),您将看到以下欢迎屏幕:

读书笔记《hands-on-cloud-native-applications-with-java-and-quarkus》创建应用程序的容器图像

这是一个简单的静态页面,已包含在 src/main/resources/META-INF/resources/index.html 中,以向您显示您的应用程序可用并包含一些有用的信息放置静态资产和配置。另一方面,您的 REST 服务仍可通过 REST URI 获得:

$ curl quarkus-hello-okd-myproject.192.168.42.5.nip.io/getContainerId

由于应用程序在 quarkus-hello-okd-1-84xwq Pod 上运行,因此预期输出如下:

You are running on quarkus-hello-okd-1-7k2t8

现在,让我们学习如何通过添加我们应用程序的一些副本来扩展我们的 Quarkus 服务。

Scaling our Quarkus service

到目前为止,您已经了解了如何在 Minishift 上部署 Quarkus 应用程序。应用程序运行在 Pod 中,Pod 分配在自己的内部 IP 地址中,相当于运行容器的机器。在我们的例子中,应用程序在 OpenShift 节点中的一个 Pod 上运行。这足以保证我们的应用程序的可用性,因为一些活跃度和就绪性探测会定期执行。如果您的 Pod 停止响应,OpenShift 平台将自动重新启动它们。

另一方面,您的应用程序可能需要满足最低吞吐量。除非请求数量非常少,否则仅使用一个 Pod 通常无法满足此要求。在这种情况下,最简单的策略是水平 Pod 扩展,这将提高可用资源的数量,当对您的应用程序的请求到达路由器时,这些资源将自动平衡。

在扩展我们的应用程序之前,我们需要为其定义一个内存上限,以减少它在系统资源方面对集群的影响。由于我们的 Quarkus 应用程序不需要大量的内存,所以我们将上限设置为 50 MB,这是相当合理的,绝对比一般的 Java 应用程序更薄。

执行以下命令将内存限制设置为 50 MB。这将更新您的应用程序的部署配置:

$ oc set resources dc/quarkus-hello-okd --limits=memory=50M
A deployment configuration (whose alias in the command line is simply dc) describes the state of a particular component of the application as a Pod template. When you update the deployment configuration, a deployment process occurs to scale down the application and scale it up with a new deployment configuration and a new replication controller for the application.

应返回以下输出:

deploymentconfig.apps.openshift.io/quarkus-hello-okd resource requirements updated 

作为概念证明,您可以通过 describe 命令验证部署配置:

$ oc describe dc/quarkus-hello-okd

describe 命令的输出有点冗长;但是,您应该能够在 Limits 部分看到以下设置:

Limits:
memory:        50M

现在,让我们将应用程序扩展到 10 个实例。这将非常快,因为我们对每个 Pod 消耗的资源设置了内存限制:

$ oc scale --replicas=10 dc/quarkus-hello-okd

以下是预期的输出:

deploymentconfig.apps.openshift.io/quarkus-hello-okd scaled

转到 Web 控制台,在 Overview 面板中,我们将看到我们的应用程序已扩展到 10 个 Pod:

读书笔记《hands-on-cloud-native-applications-with-java-and-quarkus》创建应用程序的容器图像

现在我们有大量可用的 Pod,让我们尝试对我们的应用程序运行负载测试:

for i in {1..100}; do curl quarkus-hello-okd-myproject.192.168.42.5.nip.io/getContainerId ; echo ""; done;

现在,您应该能够在控制台中看到 REST 应用程序生成的响应。这将显示执行请求的 Pod 的 ID(为简洁起见,输出已被截断):

 You are running on quarkus-hello-okd-2-jzvp2\n
 You are running on quarkus-hello-okd-2-fc7h9\n
 You are running on quarkus-hello-okd-2-lj67f\n
 You are running on quarkus-hello-okd-2-qwm9j\n
 You are running on quarkus-hello-okd-2-n6kn6\n
 You are running on quarkus-hello-okd-2-bbk84\n
 You are running on quarkus-hello-okd-2-d5bj6\n
 You are running on quarkus-hello-okd-2-skc2h\n
 You are running on quarkus-hello-okd-2-bw5f9\n
 You are running on quarkus-hello-okd-2-p24jl\n
 You are running on quarkus-hello-okd-2-jzvp2\n
 ...  

尽管测量我们的应用程序的性能超出了本书的范围,但您可以继续测量在同一个集群中运行等效的 Java 应用程序所需的时间。您会注意到在时间和内存消耗方面的不同响应!

这是我们本章的最后一个任务。当你完成这个例子,并且你想清理我们在项目中创建的资源时,只需执行以下命令,这将执行资源的批量清理:

oc delete all --all

输出可能会有所不同,具体取决于可用 Pod 的数量。但是,它应该类似于以下内容:

pod "quarkus-hello-okd-1-7k2t8" deleted
pod "quarkus-hello-okd-1-build" deleted
pod "quarkus-hello-okd-1-deploy" deleted
replicationcontroller "quarkus-hello-okd-1" deleted
service "quarkus-hello-okd" deleted
deploymentconfig.apps.openshift.io "quarkus-hello-okd" deleted
buildconfig.build.openshift.io "quarkus-hello-okd" deleted
imagestream.image.openshift.io "quarkus-hello-okd" deleted
route.route.openshift.io "quarkus-hello-okd" deleted

上面的日志确认了所有被删除的资源都被成功逐出。

Summary

在本章中,我们在 Docker 容器中运行了一个简单的 REST 应用程序,然后在 Kubernetes 原生环境即 Minishift 中运行它。我们看到,通过利用 OKD 捆绑分发的特性,使我们的应用程序具有高可用性和良好的吞吐量是多么简单。

现在,是时候为我们的应用程序添加更多功能了。在下一章中,我们将学习如何配置 Undertow 扩展,可以添加它来为我们的应用程序提供 Web 服务器功能。它还包括一些 UI 资产,我们将简要介绍一下。