Camel-云原生集成教程-三-

89 阅读30分钟

Camel 云原生集成教程(三)

原文:Cloud Native Integration with Apache Camel

协议:CC BY-NC-SA 4.0

六、将应用部署到 Kubernetes

欢迎来到云原生集成之旅的最后一部分。到目前为止,您一直关注如何进行集成,学习 Camel 的概念,以及为不同的用例应用组件,但是现在您将关注如何使用 Kubernetes 以云原生方式部署这些应用。

早先做出的决定将对 Kubernetes 的应用开发体验产生巨大影响,其中最大的影响是 Quarkus 的使用。因为您不需要运行 Camel 集成;是选择其他框架还是简单运行 Camel 的主类。Quarkus 是你的目标的完美选择,因为它是云原生的。这不是 Camel 本身最初的设计,因为这个概念在它被创造出来的时候并不存在,尽管由于它坚固的架构,它非常适合这种类型的环境。

我谈了很多 Java 规范,以及它们是如何演变成遵循云原生原则的。这些原则的开发和成型得益于无数人的努力,他们致力于为云或云中提供可靠、有弹性和可扩展的服务,并在开放论坛和开放社区上分享他们的实践。一项强有力地综合了云服务开发最佳实践的举措是 12 因素应用,这是一种开源方法,最初由平台即服务公司 Heroku 的开发人员开发。这 12 条规则定义了 Kubernetes 应用固有的方法以及 Quarkus 的设计方式,所以我强烈建议您(如果您还没有这样做的话)访问 https://12factor.net/ ,从理论上了解您在这里执行的操作。

在这一章中,你将了解 Kubernetes 是什么以及它是如何工作的。您将学习如何使用 minikube,这是一个用于在本地试验 Kubernetes 的社区工具,以及如何使用 Quarkus 扩展来简化工作和部署容器的过程。

不再拖延,让我们开始新的话题吧。

KubernetesKubernetesKubernetesKubernetesKubernetesKubernetesKubernetesKubernetesKubernetesKubernetes

Kubernetes 已经成为大规模管理应用的标准。技术领域最大的参与者已经聚集在这个开源项目中,创造了一系列为这种平台提供支持或提供平台本身的产品和服务。让我们了解一下是什么让这个项目与当今的组织如此相关。

在我们生活的数字时代,可伸缩性已经成为组织采用的任何服务或解决方案的基本特征。我们必须预计到,在任何给定的时间,对我们服务的需求将会增加,要么是因为产品/服务在市场上的总体受欢迎程度、访问的选择,要么是因为我们开始处理比最初计划的更多的数据。规模是不够的;我们还必须保持相同的服务质量(响应时间、成功率等等)。

Kubernetes 非常适合这个高要求的世界,原因很简单。第一个是它与容器一起工作,这是一种与语言/产品无关的打包和分发应用的方式,也是一种利用机器资源托管应用的更有效的方式。第二,它提供了在各种场景下大规模管理容器所需的所有自动化,是一个高度可用的解决方案。

你一直在使用容器与 Keycloak 和 Kafka 等应用进行交互,但你也在第一章中使用了容器来运行 Camel 路线。至此,您已经很好地理解了容器的含义及其提供的好处。让我们从 Kubernetes 的高级架构开始,重点关注它的工作原理以及它提供的自动化类型。看图 6-1 。每个盒子代表一台主机。

img/514395_1_En_6_Fig1_HTML.jpg

图 6-1

库伯内特高层建筑

容器被抽象成称为pod的实体。pod 由一个或多个容器以及运行这些容器的必要配置组成。这种结构是 Kubernetes 真正精心安排的。pod 分布在集群中正在运行的节点之间,这就是为什么您可以实现高可用性。如果一个节点出现故障,有适当的控制器来保证集群中运行所需数量的 pod。控制器将尝试将故障节点中存在的 pod 分配到另一个可用节点中,以保持状态一致。

说到主机,控制面板是集群自动化背后的大脑。它负责协调 pod 和集群成员。在常见的高可用性设置中,您会在一组三个或更多主机中找到它。控制面板实例中有四个主要组件。让我们一个一个来看。

API 服务器,或kube-api-server,是用于内部和外部通信的接口。组件向该 API 发出请求,以获取关于集群状态的信息或更改状态。同样,集群管理员使用这个 API 来管理集群。

Kubernetes 中的状态是通过文档表示的,文档也称为资源,这些文档需要保存在某个地方。ETCD,分布式键值数据库,是 Kubernetes 的内存。

之前我提到过控制器,举了一个例子说明在失败的情况下会发生什么,但是控制器做的不仅仅是这些。控制器是 Kubernetes 内部的一种模式,其中一个组件负责跟踪至少一个 Kubernetes 的资源类型,将文档状态转化为现实。控制器管理器kube-controller-manager是默认控制器,其中有四个控制器:节点、作业、端点和服务帐户&令牌。

最后是调度器,该组件负责根据 pod 需求和可用的节点资源或特征,向节点分配新的 pod。

节点中也有重要的组件。第一个是库伯莱特经纪人。它负责处理和检查PodSpecs中描述的容器的健康状况。如果控制面板是大脑,那么这个组件很容易被认为是手。

第二个组件是 kube-proxy。我还没有讨论的一件事是,这种程度的自动化是如何实现的。动态创建 pod 并移动它们听起来不错,但是这如何与网络一起工作呢?端口和 IP 分配以及可能的扩展服务负载均衡是需要自动化的任务。Kube-proxy 是解释其工作原理的一部分。它是运行在集群中每个节点上的网络代理。它维护允许从集群内部或外部的网络会话与 pod 进行网络通信的网络规则。

Kubernetes 文档中列出的另一个节点组件是容器运行时。Docker 是容器运行时的一个例子。我没有在这里列出它,因为有一些基于 Kubernetes 的产品在容器中运行控制面板中的组件。在这种情况下,节点和控制面板都将拥有容器运行时,从而将运行时变成一种公共需求。因此,我不把它描述为节点组件。

我们只是触及了 Kubernetes 的皮毛。在深入了解它的工作原理以及可以完成哪些配置之前,您需要从一些实际的东西开始。在下一节中,您将看到如何在一台个人计算机上使用 Kubernetes 进行实验。

迷你库比

我不期望您在家中拥有集群,也不期望您能够访问公共云中的集群。有更简单的方法来研究和试验 Kubernetes。让我们试试 minikube。

minikube 是一个工具,使您能够创建一个本地 Kubernetes“集群”。您将能够应用与集群中的应用相同的配置,但是使用单个节点来完成。通过这种方式,您将体验到配置一个应用是什么样的,以及它是如何工作的,而不需要真正拥有一个集群。

请访问 minikube 网站 https://minikube.sigs.k8s.io/ ,了解如何在您使用的操作系统上安装它。我用的是 Minikube 版本 v1.20.0。

你还需要 kubectl 。这个命令行工具将允许您与 Kubernetes API 进行通信。您可以从 Kubernetes 项目网站上的 https://kubernetes.io/docs/tasks/tools/ 获得它,或者您可以使用 minikube CLI 中的kubectl,如下所示:

$ minikube kubectl help

正确安装二进制文件后,使用以下命令启动 minikube:

$ minikube start

您可能希望使用诸如--cpus--memory之类的选项来增加实例容量。只要考虑到你拥有的可用资源的数量。我将在示例中使用默认值。

要测试 minikube 是否有响应,请运行以下命令:

$ kubectl get pods -A

该命令将返回正在运行的 pod,如图 6-2 所示。

img/514395_1_En_6_Fig2_HTML.jpg

图 6-2

获取 POD

你可能已经认出了一些 POD。它们是我前面提到的组件,但是因为您只有一台主机,所以在这台主机中有控制面板和节点的组件。

我之前没有提到 coredns 组件,这是一个默认组件,但被认为是一个附加组件。该组件负责提供一个 DNS 服务器,为 Kubernetes 服务提供 DNS 记录。

storage-provisioner 是 minikube 提供的一个组件,它允许我们在 pods 中使用持久性,但在示例中并不需要。

该图中另一个需要注意的重要事情是名为名称空间的第一列。它是 Kubernetes 用来创建资源之间的分离的抽象。这样,您可以隔离资源,就好像它们在不同的环境中一样,比如生产和开发。

在该命令中,您使用选项-A,这意味着您想要从所有名称空间中检索 pod。在这种情况下,它帮助您识别是否有正在运行的 pod,因为您不知道您拥有哪个名称空间。

在进入下一节之前,删除 minikube 安装。下一节你需要一个不同的。通过运行以下命令停止它:

$ minikube stop

然后使用以下命令删除它:

$ minikube delete

首次应用部署

您了解了如何创建本地 Kubernetes,并了解了更多相关概念。要继续前进,您需要学习如何实际部署应用以及如何与之交互。

在前几章中使用容器时,您依赖 Docker Hub 和本地注册表来检索映像。对于 Kubernetes 来说,情况是一样的。它还需要一个集群可以访问的注册中心,以便能够获取映像和运行容器。在这种情况下,您需要为 minikube 提供一个容器注册表。

首先创建一个新的 minikube 实例,如下所示:

$ minikube start --insecure-registry "10.0.0.0/24"

因为您不会为此安装提供安全的注册表,所以您需要配置实例以允许来自 minikube 内部的不安全注册表。

实例启动后,您可以运行以下命令向其添加注册表:

$ minikube addons enable registry

minikube 将下载并运行注册表映像。完成后,您可以使用以下命令检查注册表是否正在运行:

$ kubectl get pods -n kube-system

您正在使用-n来定义您想要在哪个名称空间中执行查询。您应该会得到类似于图 6-3 的响应。

img/514395_1_En_6_Fig3_HTML.jpg

图 6-3

注册表窗格

Kubernetes 有一个资源叫做服务。该资源创建一个 DNS 记录,以允许 pod 通过名称访问其他 pod。这很重要,因为 pods 可以扩展并移动到集群中的不同节点,改变其地址,但是客户端应用只需要服务名就可以到达应用。该资源除了创建一个可预测的名称供其他 pod 使用之外,还增加了对不同 pod 或具有多个实例的 pod 的负载均衡请求的可能性。安装注册表时,会创建一个服务。使用以下命令检查安装中可用的服务:

$ kubectl get service -n kube-system

这将给出在kube-system名称空间中的服务列表,如图 6-4 。

img/514395_1_En_6_Fig4_HTML.jpg

图 6-4

获取服务库-系统

注册服务是一种ClusterIP类型的服务,这意味着它将在 minikube 的网络中拥有一个可解析的 IP。如果您想获得关于此服务的更多详细信息,您可以像这样进行更精确的查询:

$ kubectl get service registry -o json -n kube-system

该命令将返回类似于清单 6-1 的内容。

{
    "apiVersion": "v1",
    "kind": "Service",
    "metadata": {
        "annotations": {
       "kubectl.kubernetes.io/last-applied-configuration": "..."
        },
        "creationTimestamp": "2021-06-13T11:00:49Z",
        "labels": {
            "addonmanager.kubernetes.io/mode": "Reconcile",
            "kubernetes.io/minikube-addons": "registry"
        },
        "name": "registry",
        "namespace": "kube-system",
        "resourceVersion": "467",
        "uid": "2f31047f-8ae5-4920-bc4d-9a84ad39288f"
    },
    "spec": {
        "clusterIP": "10.103.159.51",
        "clusterIPs": [
            "10.103.159.51"
        ],
        "ports": [
            {
                "name": "http",
                "port": 80,
                "protocol": "TCP",
                "targetPort": 5000
            },
            {
                "name": "https",
                "port": 443,
                "protocol": "TCP",
                "targetPort": 443
            }
        ],
        "selector": {
            "actual-registry": "true",
            "kubernetes.io/minikube-addons": "registry"
        },
        "sessionAffinity": "None",
        "type": "ClusterIP"
    },
    "status": {
        "loadBalancer": {}
    }
}

Listing 6-1Service Resource

如我所说,Kubernetes 中的资源是文档,这些文档可以用 YAML 或 JSON 文件表示。这里我使用 JSON,因为我希望您从它的规范中检索一个特定的值,并且您将使用jsonpath来获取它。

minikube 的虚拟机不使用 Kubernetes DNS 配置,但是您需要 minikube 使用的 Docker 才能解析注册表主机来运行映像。因此,让我们为此创建一个变通解决方案。

首先,让我们检索服务 IP。您将使用jsonpath来自定义查询结果,只带回ClusterIP值。使用以下命令:

$ kubectl get service registry -o=jsonpath='{.spec.clusterIP}' -n kube-system

稍后您将使用该命令。现在您需要部署一个应用。让我们使用camel-hello-minikube项目。

在 IDE 中打开项目。观察清单 6-2 中的项目路线。

public class HelloMinikubeRoute extends RouteBuilder {
    @Override
    public void configure() throws Exception {

      rest("/helloMinikube")
      .get()
          .route()
          .routeId("k8s-hello-minikube")
          .log("Request Received.")
          .setBody(constant("Hello from minikube."))
      .endRest();

    }
}

Listing 6-2HelloMinikubeRoute.java

这是一个简单的“Hello World”类型的应用,只是为了演示如何将 Camel 应用部署到 minikube 中。在这个项目中,你将像在第一章中一样使用quarkus-container-image-jib。您将使用这个扩展来构建容器映像,并将其推送到创建的注册表中。

为了能够将映像推送到注册表,您需要从 minikube 的网络外部访问它。您可以使用名为port-forwardkubectl命令来启用它。

在单独的终端窗口或选项卡中,运行以下命令:

$ kubectl port-forward service/registry -n kube-system 5000:80

这个命令将创建一个从您的本地网络到 Kubernetes 集群的隧道。运行之后,终端将挂起,等待连接。让它一直开着。

现在您可以连接到注册中心了,让我们构建应用并推送映像。在单独的终端窗口中,转到camel-hello-minikube目录。从那里运行以下命令:

camel-hello-minikube $ mvn clean package \
-Dquarkus.container-image.push=true \
-Dquarkus.container-image.build=true \
-Dquarkus.container-image.group=apress \
-Dquarkus.container-image.registry=localhost:5000 \
-Dquarkus.container-image.insecure=true

这里使用了新的参数。首先,通过设置quarkus.container-image.push=true并在quarkus.container-image.registry=localhost:5000中通知注册表地址,通知扩展您想要将映像推送到注册表。这个注册表是不安全的,这就是为什么你需要设置quarkus.container-image.insecure=true

Maven 构建完成后,您可以通过运行以下命令来检查注册表中是否有该映像:

$ curl http://localhost:5000/v2/_catalog

响应应该如图 6-5 所示。

img/514395_1_En_6_Fig5_HTML.jpg

图 6-5

注册表目录

你现在可以停止port-forward了。

回到项目,在k8s文件夹中,打开清单 6-3 中所示的deployment.yml文件。

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: camel-hello-minikube
  name: camel-hello-minikube
  namespace: first-deploy
spec:
  replicas: 1
  selector:
    matchLabels:
      app: camel-hello-minikube
  strategy:
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: camel-hello-minikube
    spec:
      containers:
        - image: {REGISTRY}/apress/camel-hello-minikube:1.0.0
          imagePullPolicy: IfNotPresent
          name: camel-hello-minikube
          ports:
            - containerPort: 8080
              protocol: TCP

Listing 6-3Camel-hello-minikube Deployment File

我说过,Kubernetes 中的资源是文档,部署描述符也是如此。这里有一个最小的deployment配置文件。您可以设置容器映像、pod 副本的数量、部署应该如何展开,以及可以识别此部署资源的组件的标签。

你一定注意到了{REGISTRY}标记。您需要替换这个值,以便能够在 minikube 中创建这个资源。

首先,让我们创建一个命名空间来托管部署。使用此命令:

$ kubectl create namespace first-deploy

使用sed,一个流编辑器,动态替换标记并创建资源。在项目目录中,运行以下命令:

camel-hello-minikube $ sed "s/{REGISTRY}/$(kubectl get svc registry -o=jsonpath='{.spec.clusterIP}' -n kube-system)/" k8s/deployment.yml | kubectl create -f -

您不需要在create命令中传递名称空间,因为名称空间已经在资源文件中声明了。

上面的命令使用资源名称的缩写形式。它用了svc而不是service。大多数 Kubernetes 基础资源都有一个缩写。

您可以通过使用以下命令查看命名空间中生成的事件来检查部署过程:

$ kubectl get events -n first-deploy

您还可以检查名称空间中的部署状态。运行此命令以查看 pod 是否准备就绪:

$ kubectl get deployment -n first-deploy

如果一切顺利,你的结果应该如图 6-6 所示。

img/514395_1_En_6_Fig6_HTML.jpg

图 6-6

获得部署

有一个1/1 ready意味着所需的一个 pod 正在运行,没有可察觉的错误。我说“可察觉的错误”是因为应用可能正在运行,但不能正常工作。如果您想对这个状态行更加自信,您应该在应用中实现更好的健康检查。我们将在接下来的章节中详细讨论这一点。

我希望您知道部署资源是什么样子的,这就是为什么您使用文件来创建部署,但是还有另一种方法来使用kubectl创建默认部署。您可以使用以下命令:

$ kubectl create deployment camel-hello-minikube--image=$(kubectl get svc registry -n kube-system-o=jsonpath='{.spec.clusterIP}')/apress/camel-hello-minikube:1.0.0 --port=8080 -n first-deploy

要访问和测试该部署,您需要再次运行port-forward。首先,让我们为这个deployment创建一个service。在终端窗口中,运行

$ kubectl expose deployment camel-hello-minikube -n first-deploy

这将为您创建一个ClusterIP类型的服务,目标是唯一声明的端口。您可以通过运行以下命令来检查结果

$ kubectl get services -n first-deploy

现在您可以使用这个服务来创建带有port-forward的隧道:

$ kubectl port-forward service/camel-hello-minikube \

-n first-deploy 8080:8080

您不需要服务来port-forward到 pod。我在这里只使用这种方法,所以您不需要寻找 pod 的名称,也不需要演示如何公开部署。

在另一个终端中,您可以使用 cURL 测试应用:

$ curl http://localhost:8080/helloMinikube

您应该会得到如图 6-7 所示的响应。

img/514395_1_En_6_Fig7_HTML.jpg

图 6-7

卷曲响应

另外,检查容器日志。它将帮助您理解应用在 minikube 中的行为。

$ kubectl logs deployment/camel-hello-minikube -c camel-hello-minikube -n first-deploy

一旦您完成了对示例的测试,您可能想要删除所创建的内容,以便为其他测试节省资源。您可以通过运行如下命令来删除命名空间和其中的所有资源:

$ kubectl delete namespace first-deploy

夸克-迷你立方

在上一节中,我想向您介绍一些使用 minikube 和 Quarkus 扩展时的概念和可能性。您了解了如何将映像推送到外部注册表,以及如何使用port-forward来访问 minikube 内部的应用,但是根据您想要实现的目标,还有更简单的方法来实现这一点。在这一节中,您将看到 quarkus-minikube 扩展如何在使用 minikube 进行测试时帮助您。

在开始这个新例子之前,让我们获得一个全新的 minikube 实例。按顺序运行以下命令:

$ minikube stop
$ minikube delete
$ minikube start

首先要注意的是,您没有设置--insecure,因为您不打算使用容器注册表。相反,你要保存映像直接在 minikube 的 Docker 注册表。您将使用同一个项目camel-hello-minikube,但是您需要向它添加一个新的扩展。

camel-hello-minikube目录下,运行以下命令:

camel-hello-minikube $ mvn quarkus:add-extension \
 -Dextensions="quarkus-minikube"

该扩展生成 Kubernetes 清单(minikube.yamlminikube.json),将用于部署应用。当与quarkus-container-image-jib一起使用时,jib会将映像推送到 minikube 的 Docker,而quarkus-minikube会将清单中的deploymentservice资源应用到 Minikube。

在构建和运行这些扩展之前,您需要 minikube 的 Docker 配置。运行以下命令公开配置:

$ minikube -p minikube docker-env

你会得到类似图 6-8 的回报。

img/514395_1_En_6_Fig8_HTML.jpg

图 6-8

minikube 坞站配置

变量DOCKER_HOST的值是 minikube 虚拟机 IP 地址加上端口号。您可以通过运行以下命令来检查 IP

$ minikube ip

现在您已经知道如何检索配置,您需要在终端会话中加载它。这样当你运行 Quarkus 插件时,jib将知道如何连接到所需的 Docker。

运行这个:

$ eval $(minikube -p minikube docker-env)

让我们为这个应用创建一个新的名称空间:

$ kubectl create namespace second-deploy

更改kubectl上下文以指向这个新的名称空间。扩展kubernetes-client将使用这个信息来确定将创建的资源发送到哪个集群和名称空间。运行以下命令进行更改:

$ kubectl config set-context --current --namespace=second-deploy

然后像这样构建应用:

camel-hello-minikube $ mvn clean package  \
-Dquarkus.container-image.group=apress \
-Dquarkus.kubernetes.deploy=true \
-Dquarkus.kubernetes.deployment-target=minikube

通过设置quarkus.kubernetes.deploy=true,扩展将构建映像并将生成的清单推送到 Kubernetes。在这种情况下,您使用quarkus.kubernetes.deployment-target=minikube瞄准了 Minikube。

转到target/kubernetes并查看生成的文件,如清单 6-4 所示。

---

apiVersion: v1
kind: Service
metadata:
  annotations:
  labels:
    app.kubernetes.io/name: camel-hello-minikube
    app.kubernetes.io/version: 1.0.0
  name: camel-hello-minikube
spec:
  ports:
  - name: http
    nodePort: 30254
    port: 8080
    targetPort: 8080
  selector:
    app.kubernetes.io/name: camel-hello-minikube
    app.kubernetes.io/version: 1.0.0
  type: NodePort
---

apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
  labels:
    app.kubernetes.io/name: camel-hello-minikube
    app.kubernetes.io/version: 1.0.0
  name: camel-hello-minikube
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: camel-hello-minikube
      app.kubernetes.io/version: 1.0.0
  template:
    metadata:
      annotations:
      labels:
        app.kubernetes.io/name: camel-hello-minikube
        app.kubernetes.io/version: 1.0.0
    spec:
      containers:
      - env:
        - name: KUBERNETES_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        image: apress/camel-hello-minikube:1.0.0
        imagePullPolicy: IfNotPresent
        name: camel-hello-minikube
        ports:
        - containerPort: 8080
          name: http
          protocol: TCP

Listing 6-4minikube.yaml

如您所见,服务和部署已经生成。该服务被配置为NodePort类型,这意味着它将在 minikube VM 中公开一个端口,这样您就可以从安装外部访问应用。部署是标准的。

在下一节中,您将看到如何向这个文档添加不同的参数。

您可以使用这个插件通过不设置quarkus.kubernetes.deploy来创建清单。这样,您可以自己推送清单或将其用作模板。需要记住的一件重要事情是quarkus-minikube有一些先决条件:

  • 该映像将由 minikube 的 Docker 构建。

  • 应用将使用节点端口公开。

如果您不希望创建这些特征,那么您可以使用quarkus-kubernetes扩展来为您生成清单,这样会更加灵活,但是需要更多的参数。

使用以下命令检查是否成功创建了部署:

$ kubectl get deployment -n second-deploy

在测试应用之前,我们先来看看 minikube 的 Docker。要访问虚拟机,请使用以下命令:

$ minikube ssh

一旦进入,您可以使用 Docker 命令来检查 Docker 是如何配置的,例如,检查您期望生成的映像是否在那里,如图 6-9 所示。

img/514395_1_En_6_Fig9_HTML.jpg

图 6-9

ssh 小型库会话

您可以通过键入exit并按 Enter 键来关闭会话。

您知道扩展根据清单文件中的内容为您提供了一个NodePort服务,但是有另一种方法可以更容易地获得这些信息。运行以下命令:

$ minikube service list

观察图 6-10 查看输出。请记住,您可能会有不同的虚拟机 IP 和端口号。

img/514395_1_En_6_Fig10_HTML.jpg

图 6-10

服务列表

要测试该应用,只需发送一个指向所示 URL 的 cURL 请求(注意,您的 IP 可能略有不同),如下所示:

$ curl http://192.168.64.10:30254/helloMinikube

完成测试后,清理环境以节省资源。像这样删除名称空间:

$ kubectl delete namespace second-deploy

应用配置

在前面的章节中,您看到了如何使用 minikube 以及如何使用 Quarkus 扩展来部署应用,但是使用了非常简单的方法。有一些非常重要的配置,您的应用必须至少被认为是生产就绪的。我们会讨论这些。

环境变量

容器化的应用通常在启动时或启动前使用环境变量来配置自己。一些映像可能在启动脚本中使用这些值;有些人可能会直接在应用中使用它们(正如您将要做的那样),但重要的是,根据传递给它的配置,单个映像可以用于不同的目的或不同的环境。让我们看看如何将扩展和 Camel 放在一起实现这个重要的特性。

正如我在第一章谈论 Quarkus 时提到的,它实现了MicroProfile Config规范。其实,并不是 Quarkus 实现了它,而是打包在其中的 SmallRye 配置项目。该项目允许您从五个不同的来源检索配置:

  • 系统属性

  • 环境变量

  • 放置在工作目录中的.env文件

  • 一个放在名为config的目录中的application.properties文件,位于应用运行的同一层

  • 一个打包了应用代码的application.properties文件

当设置clientIdsgroupId名称时,您已经在 Kafka 示例中使用了系统属性,并且您已经在大多数示例中使用了与代码打包在一起的application.properties文件,但是现在让我们集中在使用环境变量上。当使用容器和 Kubernetes 时,这种方法特别有趣,因为在运行映像时可以很容易地改变它的值。

在这个例子中,您将使用camel-env-msg项目。在 IDE 中打开它。检查清单 6-5 中的项目路线。

public class EnvMSGRoute extends RouteBuilder {

    @Override
    public void configure() throws Exception {

      rest("/helloEnv")
      .get()
          .route()
          .routeId("env-msg")
          .log("Request Received")
          .setBody(constant("{{app.msg}}"))
      .endRest();

    }
}

Listing 6-5EnvMSGRoute.java File

这是另一个简单的路线示例,重点向您展示 Camel 和 Quarkus 中的特定配置。这里有一个 REST 路由,它返回一个在常量中定义的消息。该常量引用一个名为app.msg的属性键。查看清单 6-6 中的application.properties文件。

# Route properties
application.message=Default Message
app.msg=${application.message}

# Kubernetes configuration
quarkus.container-image.group=apress
quarkus.kubernetes.env.vars.application-message=Message from ENV var

Listing 6-6Application.properties File

从应用与路线相关的属性开始,您有两个值。路由使用app.msg条目来检索一个值,但是这个条目值是从另一个名为application.message的属性中检索的。后者由 SmallRye 实现解决,因此您可以用它来定义默认值,也可以用它来替换环境变量。该库将寻找不同的模式来找到匹配的环境变量,但最常用的模式是由下划线(_)分隔的大写单词。

最后一部分是与您将如何为这个项目生成部署资源相关的属性。该属性可以这样理解:

quarkus.kubernetes.env.vars.[KEY]=[VALUE]

需要注意的一件重要事情是密钥声明中使用的模式。它将按照以下约定生成一个环境变量:

  • 它用下划线替换每个既不是字母数字也不是下划线的字符。

  • 它将名称转换为大写。

application-message这样的键会变成一个名为APPLICATION_MESSAGE的变量。

让我们在将应用发送到 Minikube 之前在本地测试它。在终端窗口中,导航到camel-env-msg目录并启动应用,如下所示:

camel-env-msg $ mvn quarkus:dev

在另一个终端窗口中,发送如下请求来测试应用:

$ curl -w "\n" http://localhost:8080/helloEnv

您将得到如图 6-11 所示的响应。

img/514395_1_En_6_Fig11_HTML.jpg

图 6-11

HelloEnv 响应

停止应用。在同一个终端中,声明一个环境变量,然后启动应用:

camel-env-msg $ export APPLICATION_MESSAGE='new message'
camel-env-msg $ mvn quarkus:dev

再次测试应用。这一次你的反应应该如图 6-12 所示。

img/514395_1_En_6_Fig12_HTML.jpg

图 6-12

新 helloEnv 响应

您能够通过使用环境变量来更改应用配置,但是它在部署资源中会是什么样子呢?

像这样打包应用:

camel-env-msg $ mvn clean package \
-Dquarkus.kubernetes.deployment-target=minikube

查看target/kubernetes目录,打开minikube.yaml文件,如清单 6-7 所示。

---

apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
  labels:
    app.kubernetes.io/name: camel-env-msg
    app.kubernetes.io/version: 1.0.0
  name: camel-env-msg
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: camel-env-msg
      app.kubernetes.io/version: 1.0.0
  template:
    metadata:
      annotations:
      labels:
        app.kubernetes.io/name: camel-env-msg
        app.kubernetes.io/version: 1.0.0
    spec:
      containers:
      - env:
        - name: KUBERNETES_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: APPLICATION_MESSAGE
          value: Message from ENV var
        image: apress/camel-env-msg:1.0.0
        imagePullPolicy: IfNotPresent
        name: camel-env-msg
        ports:
        - containerPort: 8080
          name: http
          protocol: TCP

Listing 6-7Minikube.yaml Deployment

现在,资源的定义中有了所需的变量。让我们在 minikube 上测试一下。

在同一个终端中,像这样配置 Docker 变量:

camel-env-msg $ eval $(minikube -p minikube docker-env)

为此测试创建新的命名空间:

$ kubectl create namespace third-deploy

将您的客户端上下文设置为新的名称空间:

$ kubectl config set-context --current --namespace=third-deploy

然后您可以像这样部署应用:

camel-env-msg $ mvn clean package \
-Dquarkus.kubernetes.deployment-target=minikube \
-Dquarkus.kubernetes.deploy=true

部署完成后,使用以下命令获取 URL 信息

$ minikube service list

在我的例子中,结果是图 6-13 。

img/514395_1_En_6_Fig13_HTML.jpg

图 6-13

服务列表结果

在我的环境中,测试调用是这样的:

$ curl http://192.168.64.10:30410/helloEnv

预期的响应是资源定义中设置的“Message from ENV var”,但是一旦构建了映像,您就可以对部署定义进行更改,并更改应用的行为方式。

您可以用多种方式更改资源。例如,您可以使用kubectl edit deployment ${name}并使用命令行编辑器手动编辑资源,但是对于这个例子,您可以使用更直接的方法。

运行以下命令来更改部署:

$ kubectl patch deploy camel-env-msg --type='json' -p='[{"op": "replace","path":"/spec/template/spec/containers/0/env/1/value", "value":"new patched message"}]'

我没有在调用中传递名称空间,因为上下文已经设置为正确的名称空间。如果您更改了客户端配置中的上下文,只需在命令中添加-n third-deploy

修补部署将导致新的部署发生。通过运行以下命令检查事件,您可能会看到这一点:

$ kubectl get events

尝试另一个请求。您应该会得到如图 6-14 所示的响应。

img/514395_1_En_6_Fig14_HTML.jpg

图 6-14

修补的消息

完成测试后,您可以删除名称空间:

$ kubectl delete namespace third-deploy

这个例子的目的是向您展示使用环境变量来配置您的 Quarkus 应用是可能的,以及使用扩展来创建具有这些定义的部署资源是多么容易。

这里您在资源定义中设置变量,但是 Kubernetes 比这更灵活。您可以使用configMapssecrets作为变量的值,尽管这不会改变应用访问变量的方式。

配置映射和机密

配置映射和机密是 Kubernetes 资源,允许用户从配置中分离映像。它们是一个键值文件,可以承载简单的字符串值来完成文件。两者的区别在于,机密通常用于保存更敏感的数据,因为默认情况下,机密存储为未加密的 base64 编码字符串。让我们看看如何使用 Quarkus 和 Camel 的这些资源。

对于这个例子,您将使用camel-cm-secret项目。在您的 IDE 中打开它,让我们分析这个项目路径,如清单 6-8 所示。

public class ConfigMapSecretRoute extends RouteBuilder {

@Override
public void configure() throws Exception {

rest("/ConfigMapSecret")
.get()
.route()
.routeId("cm-secret")
.log("Request Received")
.choice()
.when(header("password").isEqualTo(constant("{{password}}" )))
   .setBody(constant("{{application.message}}"))
   .log("Authorized")
.otherwise()
   .setHeader(Exchange.HTTP_RESPONSE_CODE, constant(403))
   .log("Not Authorized")
.endRest();

    }
}

Listing 6-8ConfigMapSecretRoute.java File

您将使用这个 REST 路由来模拟一个身份验证过程。它将检查“password”报头是否等于给定值,如果是,将向客户端发送一条消息。如果报头不存在或者不等于预期值,将向客户端返回一个403响应代码。

使用谓词绝对不是什么新鲜事,但是新的技巧就在这个项目application.properties文件中,如清单 6-9 所示。

# Route properties
%dev.application.message=Authorized
%dev.password=test

# Kubernetes configuration
quarkus.container-image.group=apress

## Secret mount
quarkus.kubernetes.mounts.my-volume.path=/work/config/application.properties
quarkus.kubernetes.mounts.my-volume.sub-path=application.properties
quarkus.kubernetes.secret-volumes.my-volume.secret-name=app-secret

## ConfigMap Environment Variable
quarkus.kubernetes.env.mapping.application-message.from-configmap=app-config
quarkus.kubernetes.env.mapping.application-message.with-key=app.msg

Listing 6-9Camel-cm-secret application.properties File

从路径属性开始,这里有一些新的东西。Quarkus 允许您使用相同的application.properties文件来声明不同概要文件的属性。每个概要文件都用于特定的环境,比如开发、测试和生产。在这个例子中,您使用的是一个dev概要文件,它将在您使用插件quarkus:dev运行代码时使用,但是在运行 jar 时不使用。

在秘密挂载部分,您将在/work/config目录下挂载一个秘密作为属性文件,并将其作为application.properties进行加载。为了便于理解,您可以像这样阅读属性键:

quarkus.kubernetes.mounts.[volume name].path
quarkus.kubernetes.mounts.[volume name].sub-path
quarkus.kubernetes.secret-volumes.[volume name].secret-name

您已经看到了如何在 Quarkus 中使用环境变量,但是这里您以不同的方式使用它们。您将使用属性键从configMap中获取值,而不是直接设置值。你可以这样读密钥:

quarkus.kubernetes.env.mapping.[ENV VAR].from-configmap
quarkus.kubernetes.env.mapping.[ENV VAR].with-key

让我们看看部署定义是什么样子的。像这样打包应用:

camel-cm-secret $ mvn clean package \
-Dquarkus.kubernetes.deployment-target=minikube

查看清单 6-10 中的target/kubenetes/minikube.yaml文件。

apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
  labels:
    app.kubernetes.io/name: camel-cm-secret
    app.kubernetes.io/version: 1.0.0
  name: camel-cm-secret
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: camel-cm-secret
      app.kubernetes.io/version: 1.0.0
  template:
    metadata:
      annotations:
      labels:
        app.kubernetes.io/name: camel-cm-secret
        app.kubernetes.io/version: 1.0.0
    spec:
      containers:
      - env:
        - name: KUBERNETES_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: APPLICATION_MESSAGE
          valueFrom:
            configMapKeyRef:
              key: app.msg
              name: app-config
        image: apress/camel-cm-secret:1.0.0
        imagePullPolicy: IfNotPresent
        name: camel-cm-secret
        ports:
        - containerPort: 8080
          name: http
          protocol: TCP
        volumeMounts:
        - mountPath: /work/config/application.properties
          name: my-volume
          readOnly: false
          subPath: application.properties
      volumes:
      - name: my-volume
        secret:
          defaultMode: 384
          optional: false
          secretName: app-secret

Listing 6-10Camel-cm-secret minikube.yaml Deployment

您有一个环境变量APPLICATION_MESSAGE,使用一个configMap引用作为它的值,并有一个卷来挂载这个秘密。现在您可以开始设置测试环境了。

首先,您需要创建一个新的名称空间来测试这个应用。在终端窗口中,运行以下命令:

$ kubectl create namespace forth-deploy

将客户端上下文设置为新创建的名称空间:

$ kubectl config set-context --current --namespace=forth-deploy

应用需要在名称空间中配置一个configMap和一个密码。让我们提供它们,从秘密开始:

$ echo "password=admin" >> application.properties


$ kubectl create secret generic app-secret \

--from-file=application.properties


$ rm application.properties

然后像这样创建configMap:

$ kubectl create cm app-config \
--from-literal=app.msg="Message from CM"

要部署应用,请导航到camel-cm-secret目录。这次您将通过设置quarkus.kubernetes.node-port属性来设置您将使用哪个node-port:

camel-cm-secret $ eval $(minikube -p minikube docker-env)
camel-cm-secret $ mvn clean package \
-Dquarkus.kubernetes.deployment-target=minikube \
-Dquarkus.kubernetes.deploy=true \
-Dquarkus.kubernetes.node-port=30241

通过设置node-port,您现在有了一个可预测的地址来发送请求。您可以使用以下命令在您的计算机中进行测试:

$ curl http://$(minikube ip):30241/ConfigMapSecret \
-H "password: admin"

期望收到与图 6-15 相同的信息。

img/514395_1_En_6_Fig15_HTML.jpg

图 6-15

ConfigMapSecret 响应

您可以通过发送错误的密码或更改密码或configMap来继续测试,但请记住,对这些资源的更改不会触发新的部署。如果您已经更改了这些资源,您可以像这样“弹跳”应用窗格:

$ kubectl delete pod ${pod-name}

用 pod 名称替换变量${pod-name}。您可以使用以下命令获取名称:

$ kubectl get pods

当您删除一个 pod 时,控制器将创建一个新的 pod 来替换已删除的 pod,从而保持部署的定义的所需状态。部署 pod 时会评估卷和环境变量值。

完成测试后,删除名称空间:

$ kubectl delete namespace forth-deploy

运行状况检查和探测

运行状况检查对于监控生产中的应用非常重要。您需要知道应用是否启动并运行,如果没有,则采取正确的措施。当您处理在 Kubernetes 中运行的容器化应用时,这个功能就变得必不可少了。我们来看看为什么。

使用 Kubernetes 的美妙之处在于,它为我们实现了自动化,使我们的应用保持运行,同时还具有可伸缩性。要做到这一点,Kubernetes 需要知道如何评估应用是否有响应并准备好接收请求。检查图 6-16 。

img/514395_1_En_6_Fig16_HTML.jpg

图 6-16

服务负载均衡

集群中的客户端应用通常会使用services通过 name 访问其他 pods。虽然services只是对 Kubernetes 如何处理服务发现和负载均衡的一个抽象,但您需要知道的重要一点是,它们有能力识别一个端点(pod)是否准备好接收请求。在图 6-16 中,第二个 pod 可能是一个新的 pod,因为部署被扩展,或者它可能是第二个副本,因为它进入失败状态而被重新启动。在这两种情况下,应用都没有准备好接收请求,因此服务层会将所有请求重定向到第一个 pod。

为了识别应用的状态,Kubernetes 定义了称为 probes 的测试例程。有三个:就绪、活跃度和启动。

ReadinessStartup是用于识别应用是否准备好接收连接的探针。对于在首次初始化时可能需要额外启动时间的遗留应用,启动探测器更为理想。在本书中,您将不会涉及启动探针,因为您正在创建启动速度非常快的现代应用。

Liveness是检查应用是否响应的测试。通过这种方式,您可以确定正在运行的应用是否没有陷入死锁,如果是,就重新启动它。

您可以使用 HTTP 调用、TPC 端口检查或在容器中执行命令来执行测试。关于容错、尝试之间的延迟、超时等有不同的参数,使得这些诊断足够灵活,可以处理不同类型的应用。当研究这个例子时,你会看到一些可能性。

在应用方面,您需要提供测试实现。这正是你将在camel-health-check项目中看到的。在 IDE 中打开它。

让我们从分析这个项目中使用的依赖项开始。打开清单 6-11 所示的pom.xml文件。

...
<dependencies>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-rest</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-container-image-jib</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-minikube</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-health</artifactId>
</dependency>
</dependencies>
...

Listing 6-11camel-health-check Dependencies

Camel 提供了创建健康检查的机制,但是您将要使用的是另一个 SmallRye 项目,它实现了微概要健康规范 SmallRye Health。您将使用它,因为它非常灵活,而且还集成了 Kubernetes 插件,所以 Quarkus 配置将反映在生成的Deployment文档中,而不必添加更多配置。

看一下清单 6-12 所示的测试路线。

public class HealthCheckRoute extends RouteBuilder {

    @Override
    public void configure() throws Exception {

      rest("/healthApp")
      .get()
          .route()
          .routeId("health-route")
          .log("Request Received")
          .setBody(constant("{{application.message}}"))
      .endRest();

    }
}

Listing 6-12HealthCheckRoute.java File

这是另一种“Hello World”类型的路线,因为你真正需要学习的不是路线,而是你如何处理健康检查。

首先打开清单 6-13 中所示的AppReadiness类。

@Readiness
@Singleton
public class AppReadiness implements HealthCheck {

private static final Logger LOG = Logger.getLogger(AppReadiness.class);

@Inject
CamelContext context;

@Override
public HealthCheckResponse call() {

LOG.trace("Testing Readiness");

HealthCheckResponseBuilder builder;

if(context.isStarted()){
  builder = HealthCheckResponse.named("Context UP").up();
}else{
  builder = HealthCheckResponse.named("Context Down").down();
}

 return builder.build();

}
}

Listing 6-13AppReadiness.java File

首先实现HealthCheck接口,它定义了一个方法call()。此方法用于确定应用是否准备好接收连接。因为这是一个简单的 REST 应用,所以就绪条件是 Camel 上下文被启动。为了检查上下文,您在 Singleton bean 中注入了上下文,并使用上下文方法isStarted()来进行验证。根据返回的方法,给出肯定(up())或否定(down() ) HealCheckResponse。为了使这个HealthCheck有效,您只需要添加注释@Readiness

现在让我们看看活性测试是如何完成的。参见清单 6-14 。

@Liveness
@Singleton
public class AppLiveness implements HealthCheck {

private static final Logger LOG = Logger.getLogger(AppLiveness.class);

@Inject
CamelContext context;

@Override
public HealthCheckResponse call() {

 LOG.trace("Testing Liveness");

  HealthCheckResponseBuilder builder;

  if(!context.isStopped()){
     builder = HealthCheckResponse.named("Camel UP").up();
  }else{
     builder = HealthCheckResponse.named("Camel DOWN").down();
  }

  return builder.build();
}
}

Listing 6-14AppLiveness.java File

这个健康检查与AppReadiness非常相似,但是不是检查上下文是否启动,而是检查上下文是否没有停止。如果上下文停止,这意味着您的路由没有运行,因此需要重新启动应用。

要将这种健康检查作为您的活性测试,您只需要向类添加@Liveness注释。

Camel 上下文只有在您编程时才会停止,或者当应用收到 SIGTERM 以正常关闭时才会停止,但它仍然是如何实现活跃度的一个示例。

您可能已经注意到,在两次运行状况检查中都有跟踪日志。这些日志帮助您了解 Kubelet 是如何调用这些测试的。看一下application.properties文件。您只为运行状况检查启用跟踪日志。参见清单 6-15 。

# Quarkus properties
quarkus.log.category."com.apress.integration.health".level
=TRACE
quarkus.log.min-level=TRACE

# Route properties
application.message=Hello from HealthApp

# Kubernetes configuration
quarkus.container-image.group=apress

Listing 6-15Camel-health-check application.properties File

让我们生成部署定义。在终端中,导航到camel-health-check目录并运行以下命令:

camel-health-check $ mvn clean package \
-Dquarkus.kubernetes.deployment-target=minikube

打开生成的文件,target/kubernetes/minikube.yaml。让我们检查一下Deployment中的 pod 定义。见清单 6-16 。

...
  template:
    metadata:
      annotations:
      labels:
        app.kubernetes.io/name: camel-health-check
        app.kubernetes.io/version: 1.0.0
    spec:
      containers:
      - env:
        - name: KUBERNETES_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        image: apress/camel-health-check:1.0.0
        imagePullPolicy: IfNotPresent
        livenessProbe:
          failureThreshold: 3
          httpGet:
            path: /q/health/live
            port: 8080
            scheme: HTTP
          initialDelaySeconds: 0
          periodSeconds: 30
          successThreshold: 1
          timeoutSeconds: 10
        name: camel-health-check
        ports:
        - containerPort: 8080
          name: http
          protocol: TCP
        readinessProbe:
          failureThreshold: 3
          httpGet:
            path: /q/health/ready
            port: 8080
            scheme: HTTP
          initialDelaySeconds: 0
          periodSeconds: 30
          successThreshold: 1
          timeoutSeconds: 10

Listing 6-16Deployment Pod Spec

正如您所看到的,就绪性和活性探测是用所有必要的信息声明的,使用测试参数的默认值。这些默认值足以让您在 minikube 中测试这个特定的应用。这个应用又轻又快,所以在容器启动后第一次尝试有零秒的延迟不是问题,因为应用在几毫秒内启动。测试将在 30 秒的时间间隔内进行,只有在连续三次失败后,探头才会被视为失败。要回到成功状态,只需要一次成功的尝试。

这些参数很重要,因为您不希望在考虑应用失败时过于激进。您的访问选择可能会降低应用的性能,但它可能仍在正常运行,重新启动应用可能只会加剧问题。

您尝试使用此配置来生成最佳配置,以允许 pod 在准备就绪时尽快接收连接,并在 pod 无法避免将错误传播到客户端应用时尽快进行识别。达到这种最佳配置的唯一方法是在不同的场景下测试应用。

要测试这个应用,打开一个终端窗口并导航到camel-health-check/目录。首先为该部署创建一个新的名称空间:

camel-health-check $ kubectl create namespace fifth-deploy

将客户端上下文设置为新的名称空间:

camel-health-check $ kubectl config set-context--current     --namespace=fifth-deploy

在运行部署之前,设置 Docker 变量:

camel-health-check $ eval $(minikube -p minikube docker-env)

部署应用:

camel-health-check $ mvn clean package \
-Dquarkus.kubernetes.deployment-target=minikube \
-Dquarkus.kubernetes.deploy=true \
-Dquarkus.kubernetes.node-port=30241

您可以使用此命令来测试应用:

$ curl http://$(minikube ip):30241/healthApp

测试有助于检查部署是否顺利,但是您在这里看到的是如何调用健康检查。为此,您需要检查日志。您可以通过运行以下命令来检查它们:

$ kubectl logs -f deploy/camel-health-check

使用-f将允许您持续跟踪出现的日志。一旦你得到一些条目,比如清单 6-17 ,你可以使用 Control+ C 来停止它

2021-06-16 23:45:20,975 INFO  [io.quarkus] (main) camel-health-check 1.0.0 on JVM (powered by Quarkus 1.13.0.Final) started in 3.051s. Listening on: http://0.0.0.0:8080
2021-06-16 23:45:20,976 INFO  [io.quarkus] (main) Profile prod activated.
2021-06-16 23:45:20,977 INFO  [io.quarkus] (main) Installed features: [camel-attachments, camel-core, camel-platform-http, camel-rest, camel-support-common, cdi, kubernetes, mutiny, smallrye-context-propagation, smallrye-health, vertx, vertx-web]
2021-06-16 23:45:43,078 TRACE [com.apr.int.hea.AppReadiness] (vert.x-worker-thread-0) Testing Readiness
2021-06-16 23:45:46,522 TRACE [com.apr.int.hea.AppLiveness] (vert.x-worker-thread-1) Testing Liveness
2021-06-16 23:46:12,661 TRACE [com.apr.int.hea.AppReadiness] (vert.x-worker-thread-2) Testing Readiness
2021-06-16 23:46:16,520 TRACE [com.apr.int.hea.AppLiveness] (vert.x-worker-thread-3) Testing Liveness

Listing 6-17Camel-health-check Logs Output

您可以看到,在应用正常启动后,每隔 30 秒就会对应用进行一次新的就绪性和活性测试。

另一个可以进行的测试是并排打开两个终端窗口。在一个终端中,运行以下命令来查看可用的窗格:

$ kubectl get pods -w

在第二个终端中,运行以下命令,通过添加一个副本来扩展部署:

$ kubectl scale deploy/camel-health-check --replicas=2

如图 6-17 所示,您将看到新的 pod 从Ready 0/1移动到1/1,需要一些时间。这是容器通过准备测试所需的时间。

img/514395_1_En_6_Fig17_HTML.jpg

图 6-17

POD 结垢

要求和限制

我谈到了调度器组件,它负责将 pod 分配给适当的节点。但是“适当”在这个上下文中是什么意思呢?除了诸如节点标签和节点特性之类的特定参数之外,它还意味着拥有容纳 pod 所需的资源量。由于调度程序无法猜测应用需要什么,所以您必须为您拥有的名称空间设置默认值,或者您可以在部署定义中设置这些值。请求和限制是告诉 Kubernetes 您认为应用应该消耗多少资源的方法。

这里您将关注两种不同的资源,CPU 和 RAM。还有其他资源可以限制使用,但是 CPU 和 RAM 是最常用于确定 pod 位置的资源。分析图 6-18 以查看 pod 放置是如何工作的。

img/514395_1_En_6_Fig18_HTML.jpg

图 6-18

pod 放置过程

在本例中,您有一个三节点集群,每个集群有 3GB 的 RAM 用于 pod 分配。您为该群集生成了一个新的 pod,只有Node 0有足够的资源来托管这个 2GB 的 pod。

虽然图中没有展示,但 CPU 也是如此。每个节点都有其可用于 pod 分配的 CPU 容量。如果一个新的 pod 指定了它的要求,调度程序将尝试找到一个与 pod 请求的 CPU 和 RAM 数量相匹配的节点。

请求是一个 pod 预计从一个节点消耗的资源量,也是节点容量的基本衡量标准。

当 pod 定义没有声明请求时,调度程序会将它放在任何仍有资源可用的节点中,但可用资源可能不足以容纳这个新的 pod,从而导致内存不足(OOM)错误,或者在节点没有足够的 CPU 可用时影响应用性能。

您可以定义每个容器和每个箱的请求和限额。这里您将重点放在 pod 声明上,因为您将在示例中使用它。

应用资源消耗通常不是线性的。这将取决于正在处理的内容或在给定时间内的访问量,因此请求的资源量很少是实际使用的量。他们可以使用更多;他们可以少用。使用更少的资源对应用来说没有风险,因为它们将拥有正常工作所需的资源量,但这取决于使用了多少更少的资源,并且如果这种行为传播到许多 pod,您可能会有节点分配不足,其他节点缺乏资源,或者最终无法部署新的 pod,即使您在集群中有足够的物理资源。

限制用于为应用提供在最终需要时使用更多资源的灵活性,但也对可以消耗的资源量设置了严格的上限。如果一个应用试图使用超过其限制的内存,Kubelet将终止该应用,使其重启以解决问题。

CPU 使用率不同。由于 CPU 的度量是时间分配,应用将不能使用超过其限制的 CPU。

为了实现这些概念,让我们在您的跑步迷你库上启用一些功能。首先,您需要添加一个组件来监控 pod 的资源使用情况。运行以下命令在 minikube 中安装度量服务器:

$ minikube addons enable metrics-server

一旦安装了附加组件,您就可以使用 minikube 的仪表板来可视化指标。运行以下命令在浏览器上打开它:

$ minikube dashboard

该命令将引导您进入仪表板主页,如图 6-19 所示。

img/514395_1_En_6_Fig19_HTML.jpg

图 6-19

minikube 仪表板

这个仪表板允许您在 minikube 中可视化、创建、编辑和删除 Kubernetes 资源。您一直在使用命令行,因为它使示例步骤更具可重复性,但是如果您是 GUI 爱好者,仪表板可能是一个不错的选择。

您希望在这个仪表板中看到的是您在最近一节中使用的应用消耗了多少资源。为此,请按照下列步骤操作:

  • 在屏幕顶部的下拉框中。选择名称空间fifth-deploy

  • 在左侧面板中,单击窗格。

这就是了。您应该会看到类似于图 6-20 的内容。

img/514395_1_En_6_Fig20_HTML.jpg

图 6-20

minikube 指标仪表板

正如你所看到的,其中一个 POD 消耗了 2 毫核心的 CPU 和大约 73 兆字节的内存。

毫核心是声明 CPU 时间分配的度量。机器中的每个核心或虚拟核心都被给予总共 1000 毫核心点,与 CPU 的类型无关。这些点用于确定处理优先级,因此容器拥有的点越多,它消耗 CPU 的时间就越多。在我的例子中,我的 minikube 实例使用 MacOS 的默认值,2 个 CPU,这表示 2000 个毫核或 2 个核。

一兆字节只是表示内存分配的一种不同方式,其中 1 兆字节= 2-20 字节或 1,048,576 字节。同时 1MB 是 106 或 1,000,000 字节。如果你想知道 73Mi 在 MB 中意味着多少,只需将字节乘以 1.04858,即 76.546MB。

既然您从一开始就知道这个应用消耗了多少,那么让我们在部署配置中对它进行一些调整。

在您的 IDE 中打开camel-health-check项目。打开application.properties,添加以下几行:

#Requests
quarkus.kubernetes.resources.requests.memory=44Mi
quarkus.kubernetes.resources.requests.cpu=10m
#Limits
quarkus.kubernetes.resources.limits.memory=50Mi
quarkus.kubernetes.resources.limits.cpu=100m

让我们生成部署资源:

camel-health-check $ mvn clean package \
-Dquarkus.kubernetes.deployment-target=minikube

查看部署定义中的模板,如清单 6-18 所示。

...
  template:
    metadata:
      annotations:
      labels:
        app.kubernetes.io/name: camel-health-check
        app.kubernetes.io/version: 1.0.0
    spec:
      containers:
      - env:
        - name: KUBERNETES_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        image: apress/camel-health-check:1.0.0
        imagePullPolicy: IfNotPresent
        livenessProbe:
          failureThreshold: 3
          httpGet:
            path: /q/health/live
            port: 8080
            scheme: HTTP
          initialDelaySeconds: 0
          periodSeconds: 30
          successThreshold: 1
          timeoutSeconds: 10
        name: camel-health-check
        ports:
        - containerPort: 8080
          name: http
          protocol: TCP
        readinessProbe:
          failureThreshold: 3
          httpGet:
            path: /q/health/ready
            port: 8080
            scheme: HTTP
          initialDelaySeconds: 0
          periodSeconds: 30
          successThreshold: 1
          timeoutSeconds: 10
        resources:
          limits:
            cpu: 100m
            memory: 50Mi
          requests:
            cpu: 10m
            memory: 44Mi

Listing 6-18Template Definition with Request and Limits

定义被添加到 pod 中,因为使用这个插件,每个部署只有一个容器。

您知道 44Mi 比应用使用的要少,但是在这种情况下部署会发生什么情况呢?让我们通过测试来检查。

首先,删除旧的名称空间:

$ kubectl delete namespace fifth-deploy

现在让我们为这个测试创建一个新的名称空间,为客户机设置上下文,设置 Docker 变量,并部署应用。

导航到camel-health-check目录并运行以下命令:

camel-health-check $ kubectl create namespace sixth-deploy
camel-health-check $ kubectl config set-context--current     --namespace=sixth-deploy

camel-health-check $ eval $(minikube -p minikube docker-env)

camel-health-check $ mvn clean package \
-Dquarkus.kubernetes.deployment-target=minikube \
-Dquarkus.kubernetes.deploy=true \
-Dquarkus.kubernetes.node-port=30241

部署完成后,使用以下命令监视命名空间窗格:

$ kubectl get pod -w

你会看到部署永远不会正确完成,因为你没有给应用启动所需的内存量,当应用试图消耗超过其限制的内存时,Kubelet将对其进行操作并杀死它,如图 6-21 所示。

img/514395_1_En_6_Fig21_HTML.jpg

图 6-21

OOMKilled

探测、请求和限制也需要彻底的测试,以便在预期资源消耗和访问选择下的资源消耗之间配置最佳平衡。请记住,在使用容器时,您最好水平扩展,这意味着创建更多的 pod,而不仅仅是向单个 pod 添加更多的资源。我说“理想”是因为你会发现容器不能伸缩的情况,在这些情况下只有垂直伸缩是可能的。

您已经完成了使用 minikube 的测试。此时,如果愿意,您可以停止它并删除您的实例:

$ minikube stop
$ minikube delete

摘要

这是本书的最后一章,也是大胆的一章。Kubernetes 涉及的范围太广了,但是我试图让对话更多地与应用相关,而不是管理 Kubernetes 集群的挑战。在本章中,您学习了以下内容:

  • Kubernetes 是什么以及使用它的好处

  • 如何使用 minikube 从开发角度试验 Kubernetes

  • 如何使用 Quarkus 扩展将 Quarkus 应用部署到 Kubernetes 中

  • 将应用部署到 Kubernetes 中需要了解的应用配置

这本书的想法是为有集成需求并且必须使用容器和 Kubernetes 解决这些需求的开发人员和架构师创建一个坚实的学习路径。从如何在您的机器上运行 Camel 应用,到生成映像并将其部署到 Kubernetes,您已经看到了非常基础的内容,并提供了将来可以参考的实例。

我希望您能够使用现代技术和架构设计来创建自己的集成。我们将在未来的技术讨论中再见。