构建 Kubernetes:如何选择集群部署策略

一、介绍

本指南将引导您完成在为 Vultr Kubernetes 引擎 (VKE) 选择集群部署策略时应考虑的一些架构决策,以及每种方法的优缺点。

VKE 群集是一组服务器,它们为以高度可用、分布式和可扩展的方式运行应用程序和服务提供基础架构。集群由一个控制平面(负责管理集群)和一个或多个工作器节点(负责运行实际应用程序和服务)组成。控制平面提供核心调度、网络和存储服务。它管理基础结构,例如虚拟机和容器。它监督工作节点之间的通信,以确保它们以最佳方式运行。工作器节点负责运行托管群集各种工作负载的容器(称为 Pod)。

有多种方法可以配置群集体系结构以托管应用程序,这些方法具有灵活扩展、成本效率、应用程序和环境隔离、易于管理等优势。幸运的是,VKE 提供了一个免费的控制平面,并且只对工作节点的总数收费,这消除了为您的应用程序选择最佳架构时的任何成本考虑因素。但是,在选择部署策略时,除了成本之外,还应考虑其他重要事项。本指南探讨了每种方法最重要的优缺点,以帮助您做出明智的选择。

二、基于大小的架构模式

组织和分组群集的一种方法是按大小。Kubernetes 集群可以通过添加和删除工作器节点或部署具有不同计算资源量(如 CPU、内存和存储)的工作器节点来扩展或缩减。这些工作器节点是运行应用程序的物理或虚拟服务器。使用 VKE,您可以轻松添加或删除节点以调整集群大小。Kubernetes 集群非常强大和高效,允许用户根据自己的需求快速扩展或缩减其计算资源。

集群的大小是工作器节点总数及其每个计算资源的组合,它们用于部署更多 Pod(水平扩展)或为每个 Pod 分配不同数量的计算资源(垂直扩展)。

2.1、几个大型集群

在此架构配置中预置一些集群来托管工作负载。您甚至可以使用单个集群。每个都相对较大,有许多工作节点和计算资源可用于在许多 Pod 中运行工作负载。

使用几个大型集群有好处:

  • 您可以优化资源利用率。在几个大型集群上托管应用程序时,工作节点会有效地与 Pod 共享计算资源,从而最大限度地减少计算能力的浪费。
  • 您可以有效地管理基础架构,因为您无需与多个集互即可执行管理或日常任务。
  • 您可以重复使用集群范围的资源,例如负载均衡器、入口控制器等,从而使其管理更简单、更高效。一个

但是,拥有几个大型集群有一些缺点:

  • 如果需要分离不同的应用程序,单个控制平面只能具有软多租户。Kubernetes 命名空间和基于角色的访问控制提供租户之间的分离,但它们共享相同的控制平面组件,例如用于服务发现的 DNS 服务器。
  • 集群越少,容错能力就越低,因为每个集群上集中的服务就越多。任何服务中断或故障都可能导致大量容量丢失。
  • 如果大型群集发生故障,则重建该群集更具挑战性。大型群集可能具有各种不同的应用程序,这些应用程序需要复杂的配置过程。
  • 在单个群集上具有太多不同的租户应用程序可能会给控制平面组件带来压力,从而导致意外错误。如果计划托管许多应用程序,最好将它们分布在多个群集中。

2.2、许多小集群

在此体系结构模式中,您将工作负载分布在具有较小工作器节点的较大集群组中。这可实现灵活的扩展、成本效益、应用程序和环境隔离以及易于管理。如果集群过载,可以将某些 Pod 移动到另一个集群,使其成为更动态、更高效的解决方案。

如果需要,使用许多小型集群是很好的:

  • 硬多租户:您可以通过将应用程序分布在不同的集群中来完全隔离应用程序,以防止共享控制平面组件。
  • 容错:此体系结构具有更强的容错能力。在发生单个集群故障时,您可以保留大量容量。
  • 降低复杂性:重建损坏的小型群集不太复杂,因为它托管的应用程序较少。

此模式有一些缺点:

  • 您必须反复与许多不同的集互,以执行管理或例行任务,例如监视、更新等。
  • 将应用程序分散到许多不同的集群可能会浪费负载均衡器和入口控制器等资源,而这些资源可以同时处理许多应用程序。

三、基于效用的架构模式

应用程序可能包含前端、数据库、业务逻辑等的多个组件。您可能会发现自己需要部署应用程序的多个实例来创建不同的环境,例如生产、开发、测试、暂存等。这些环境中的每一个都可能具有非常不同的要求来满足应用程序的需求,这可能导致资源使用效率低下。

幸运的是,可以使用其他几种体系结构模式来解决此问题。这些关注的是 Kubernetes 集群的效用,而不是它的大小。例如,您可能会发现一个应用程序占用更多 CPU,而另一个应用程序需要大量内存。此外,生产环境可能需要入口控制器,而开发不需要,依此类推。通过选择正确的配置,您可以确保应用程序尽可能高效地运行,同时满足每个环境的需求。

3.1、每个应用程序的群集数

每个应用程序群集的方法在单个群集中运行每个应用程序及其所有环境(例如开发、测试和生产)。此体系结构配置提供了许多优点,例如应用程序隔离和易于管理,因为应用程序的所有相关组件都可以一起找到。此外,它还允许更大的可扩展性,因为可以配置集群以满足应用程序的确切要求,包括不同的计算资源和 Kubernetes 版本。

但是,由于所有环境都位于同一群集上,因此这可能会对性能和可靠性产生不利影响。假设在一个环境中出现问题,例如在测试环境中执行错误的代码。在这种情况下,它可能会导致其他环境(尤其是生产环境)的服务中断。因此,在同一群集上托管多个环境时,请务必采取额外的预防措施。

3.2、每个环境的群集数

每个环境群集的方法允许您在单个群集上托管共享环境(例如测试或生产)的多个应用程序。这种方法的好处是:

  • 高效使用资源:您可以根据环境要求预置每个集群的计算资源。您的开发和测试环境可能小于生产环境。
  • 访问隔离:更容易限制对生产群集或托管敏感环境的其他内容的访问。

但是,与每个应用程序群集的方法相比,管理单个应用程序更为复杂,因为您将环境分散在不同的群集上。可能有更好的方法来对具有高度可变要求的应用程序进行分组。例如,需要不同 Kubernetes 版本的应用程序不能使用这种方法组合在一起。

此外,使用此方法时,您必须管理多个群集及其资源,这可能会成为一项艰巨的任务。这是因为必须单独预配和维护每个群集,并且必须跨所有群集复制对应用程序或环境所做的任何更改。此外,如果一个集群关闭,可能会影响其他集群及其应用程序,从而导致潜在的停机。

幸运的是,有一些方法可以最大程度地降低此方法的复杂性。例如,您可以使用自动化工具来管理集群和资源,从而简化管理多个集群的过程。此外,您可以使用容器化技术来确保应用程序和环境可以在多个集群上运行,从而提供一定程度的冗余,以帮助最大限度地降低停机风险。

四、选择正确的架构

在托管基础结构时,没有无效的方法。上面讨论的所有体系结构都是有效的。但是,两种体系结构配置之间的选择取决于您如何确定以下因素的优先级。

  • 缩放
  • 成本效益
  • 租户隔离
  • 高可用性
  • 易于管理

随着资源需求的增加,扩展一组大型集群的成本非常高,因为与启动许多小型集群相比,每个集群的成本要高得多。您可以通过向小型集群组添加更多集群来轻松扩展基础架构,从而无限增加容量。

成本效益取决于您如何利用资源。通过在几个大型群集上托管基础结构,通过重用群集范围的资源(如入口控制器、负载均衡器等),可以更高效。拥有许多集群可能会引入资源缺口,并且成本高于您的利用率。

租户隔离有两种类型:软隔离和硬隔离。通过使用 Kubernetes 命名空间、基于角色的访问控制等在大型群集上实现软租户隔离,但由于共享控制平面组件,应用程序仍然可以发现群集上运行的其他应用程序。您可以通过在许多小型集群上托管基础架构块来实现硬租户隔离,从而消除共享控制平面组件。

Kubernetes 具有安全和资源限制功能,例如用于控制流量的 NetworkPolicy 资源、用于命名空间级别隔离的 Pod 安全准入、ResourceQuota 对象或 LimitRange 资源的资源限制等等。但是,这些方法需要额外的配置而不是硬隔离,并且无法保护您的基础结构免受每个安全漏洞的影响。

高可用性很难通过少量大型集群实现,因为它的容错能力低于拥有许多小型集群。由于大型集群中的服务中断,您可能会损失大量容量。与重建大型集群相比,您可以轻松地在一组小型集群中重建损坏的集群。

易于管理是拥有几个大型集群的一个吸引人的功能。与大型集群相比,管理由许多小型集群组成的组非常复杂,因为您需要单独与每个集群进行交互。如果您管理大量集群,则必须多次执行日常任务,例如升级 Kubernetes 版本和监控集群运行状况。

五、结论

本指南引导您完成用于托管应用程序的不同体系结构配置。它还比较了不同的配置,包括扩展、成本效率、应用程序/环境隔离、易管理性等。您可以通过根据所讨论的指针分析和评估需求,为应用程序选择正确的集群体系结构。请参阅 Kubernetes 组件概述,了解有关 Kubernetes 集群中各个组件的更多信息。

如何配置 Kubernetes 就绪性和活动性探测

一、什么是 Kubernetes 中的探测器

由于多种原因,应用程序可能变得不可靠。您需要确保应用程序处于所需状态。Kubernetes 提供了一种检查容器运行状况的方法。这种健康诊断称为探针。

二、探测容器的方法

kubelet 使用以下方法之一在其容器上完成探测:

2.1、执行

Kubelet 在容器内执行命令。结果取决于命令的退出代码。退出代码 0 表示诊断成功。

2.2、 httpget

Kubelet 向容器 IP 地址的指定端口发送请求。介于 200 和 400(含)之间的响应代码表示诊断成功。GET

2.3、tcp 套接字

Kubelet 尝试在容器 IP 地址的指定端口上建立 TCP 连接。已建立的连接表示诊断成功。

三、探头类型

Kubernetes 中有三种类型的探测器:

  1. 活体探头
  2. 就绪情况探测
  3. 启动探针

启动探测器是此列表中最近添加的,本文不对其进行讨论。

四、活体探头

活动探测器检测容器是否处于活动状态。这用于确保 Pod 的可用性。如果存活探测成功,kubelet 不会执行任何操作,因为容器已处于所需状态。如果失败,kubelet 会杀死容器,进一步的操作过程取决于容器的指定重启策略

4.1、为什么要使用活体探针?

Kubelet 通过查看容器的状态来检查容器的运行状况。问题是 的状态不反映容器子进程的运行状况。如果您有单个进程容器,则无需指定活动探测器。否则,应使用活动探测器。PID 1PID 1

五、就绪情况探测

就绪情况探测检测容器是否已准备好为传入的网络请求提供服务。您不希望将流量发送到尚未准备好运行的 Pod。如果就绪探测失败,控制窗格将停止发往容器的网络流量。在容器上运行第一个就绪情况探测之前,默认就绪状态设置为就绪情况探测在其整个生命周期内定期在容器上运行。Failure

5.1、为什么使用就绪情况探测?

所有子进程启动后,应将网络流量发送到容器。如果没有就绪探测,Kubernetes 会在启动后立即将网络流量发送到容器。这并不意味着子进程已启动。在此状态下向容器发送网络流量可能会导致错误。每次容器重新启动或创建应用程序的新实例时,应用程序都会遇到错误。PID 1

六、如何在 Kubernetes 中配置探测

在场内定义探测器。每个探头都有以下五个参数:pods.spec.containers

  1. initialDelaySeconds:这指定容器启动后等待探测器运行的时间(以秒为单位)。默认值为 0。
  2. 周期秒:指定每个周期探测器之间的时间间隔。默认值为 10。
  3. timeoutSeconds:指定每个探测器必须等待才能获得响应的时间。默认值为 0。
  4. 成功阈值:指定指示诊断成功的成功探测计数。默认值为 1。
  5. 失败阈值:指定指示诊断失败的失败探测器计数。默认值为 3。

所有这些参数都用于配置探测器。要遵循本指南,您需要配置一个 Kubernetes 集群和 kubectl 客户端。您可以使用Vultr Kubernetes Engine在云中部署 Kubernetes 集群并测试探针。在您的计算机上进行配置并按照操作。kubectl

6.1、创建活动探测器

查看以下配置文件:

apiVersion: v1

kind: Pod

metadata:

  labels:

    test: liveness

  name: liveness-demo

spec:

  containers:

  - name: basic-container

    image: registry.k8s.io/busybox

    args:

    - /bin/sh

    - -c

    - touch /tmp/healthy; sleep 20; rm -f /tmp/healthy; sleep 1000

    livenessProbe:

      exec:

        command:

        - cat

        - /tmp/healthy

      initialDelaySeconds: 5

      periodSeconds: 10

此配置创建单个容器 Pod 并定义活动探测。该探头用于执行诊断。创建容器后,执行命令。此命令将:exectouch /tmp/healthy; sleep 20; rm -f /tmp/healthy; sleep 100

  • 创建文件/tmp/healthy
  • 等待 20 秒。
  • 删除文件。/tmp/healthy
  • 使容器再保持活动状态 1000 秒。

请注意字段下的字段。这是定义活动探测器的位置。在这里,活体探测器将在创建容器后等待 5 秒,以 10 秒的时间间隔开始定期诊断。它使用 exec 方式执行探测。如果命令返回 0,则探测成功。livenessProbecontainerscat /tmp/healthy

创建容器:

kubectl apply -f config.yaml

检查容器的事件:

kubectl describe liveness-demo

在输出的末尾,您可以看到类似以下内容:

Events:

    Type    Reason     Age   From               Message

      ----    ------     ----  ----               -------

    Normal  Scheduled  25s   default-scheduler  Successfully assigned default/liveness-demo to probescluster-3a397dd3c5ec

    Normal  Pulling    25s   kubelet            Pulling image "registry.k8s.io/busybox"

    Normal  Pulled     22s   kubelet            Successfully pulled image "registry.k8s.io/busybox" in 3.292188263s

    Normal  Created    22s   kubelet            Created container basic-container

    Normal  Started    22s   kubelet            Started container basic-container

到目前为止,豆荚是健康且正在运行的。25 秒后,再次检查容器的事件:

kubectl apply -f config.yaml

您可以期望看到类似以下内容:

Events:

    Type     Reason     Age               From               Message

      ----     ------     ----              ----               -------

    Normal   Scheduled  56s               default-scheduler  Successfully assigned default/liveness-demo to probescluster-3a397dd3c5ec

    Normal   Pulling    55s               kubelet            Pulling image "registry.k8s.io/busybox"

    Normal   Pulled     52s               kubelet            Successfully pulled image "registry.k8s.io/busybox" in 3.292188263s

    Normal   Created    52s               kubelet            Created container basic-container

    Normal   Started    52s               kubelet            Started container basic-container

    Warning  Unhealthy  5s (x3 over 25s)  kubelet            Liveness probe failed: cat: can't open '/tmp/healthy': No such file or directory

    Normal   Killing    5s                kubelet            Container basic-container failed liveness probe, will be restarted

从输出中可以看到,5 秒前,存活探测失败,提示 kubectl 重启容器。现在,检查容器的状态:

kubectl get pod liveness-demo

预期产出:

NAME            READY   STATUS    RESTARTS     AGE

liveness-demo   1/1     Running   1 (4s ago)   85s

请注意,“重新启动”字段显示确认容器已重新启动。1

6.2、配置就绪探测

配置就绪情况探测类似于配置活动探测。可以通过在配置文件中的字段下添加以下内容来创建类似的准备情况检查探测器。pods.spec.containers

    readinessProbe:

      exec:

        command:

        - cat

        - /tmp/healthy

      initialDelaySeconds: 5

      periodSeconds: 10

 七、结论

本文介绍了 Kubernetes 的活跃性和就绪性探测、它们的使用以及如何配置它们。若要了解详细信息,请参阅在 kubernetes.io 配置活动、就绪和启动探测。

Kubernetes 服务类型概述

一、什么是 Kubernetes 中的服务?

在 Kubernetes 环境中,你可以有数百个(如果不是数千个)短暂的 pod。无论是因为节点被缩减、Pod 副本被缩减,还是 Pod 被重新调度到新节点,Pod 的 IP 地址都无法保证。Pod IP 地址是在调度到特定节点之后和引导之前分配的。

鉴于我们处于云原生环境中,我们希望能够水平扩展 Pod。因此,跟踪我们所有应用程序的所有IP地址将非常困难。Kubernetes 有一种资源类型,可以解决不断变化的 pod IP 问题,称为服务。Kubernetes 服务允许您创建单个常量 IP 地址,其中包含一组包含相同服务的 Pod 的 IP 地址。这非常有用,因为服务 IP 保持静态,而 Pod IP 可以不断更改。您永远不必担心没有正确的IP地址。

二、服务如何运作?

服务如何工作?如何配置?

让我们首先创建一个包含三个 Nginx pod 实例的部署。

apiVersion: apps/v1

  kind: Deployment

metadata:

  labels:

    app: nginx

  name: nginx

spec:

  replicas: 3

  selector:

    matchLabels:

      app: nginx

    spec:

      containers:

      - image: nginx

        name: nginx

应用部署清单后,我们可以看到有三个 nginx 实例与 IP 一起运行。另外,请注意我们为这些 Pod 分配的标签。这对于服务如何监视这些 Pod 至关重要。10.244.242.66, 10.244.242.67, 10.244.230,199app=nginx

$ kubectl get pods -owide --show-labels

NAME                    READY   STATUS    RESTARTS   AGE     IP               NODE                      LABELS

nginx-8f458dc5b-6vcxt   1/1     Running   0          2m27s   10.244.242.66    nodepool-a-95e9c8e86208   app=nginx,pod-template-hash=8f458dc5b

nginx-8f458dc5b-ktvtf   1/1     Running   0          2m27s   10.244.242.67    nodepool-a-95e9c8e86208   app=nginx,pod-template-hash=8f458dc5b

nginx-8f458dc5b-mlkwd   1/1     Running   0          2m27s   10.244.230.199   nodepool-a-11aa1dc199fa   app=nginx,pod-template-hash=8f458dc5b

现在,让我们定义我们的服务清单,下面定义服务清单,其中包含所定义内容的细分。

apiVersion: v1

kind: Service

metadata:

  name: nginx

  labels:

    app: nginx

spec:

  selector:

    app: nginx

  type: ClusterIP

  ports:

  - name: 80-80

    port: 80

    protocol: TCP

    targetPort: 80

当我们开始分解这个清单时,我们注意到这种清单是。接下来,元数据部分应该很熟悉,因为它与其他 Kubernetes 资源上的元数据字段相同。规范部分定义了我们的服务将如何以及与什么交互。Service

菲尔德。在此字段中,您可以定义希望此服务监视的容器。这是通过标签匹配完成的。您会注意到这有一个定义。如果您还记得,这些是我们在部署清单中为 Pod 定义的标签。通过此选择器定义,我们声明任何带有标签的 Pod 都将成为此服务的一部分。selectorselectorapp: nginxapp=nginx

接下来,我们有字段。这定义了我们希望这是什么样的服务。稍后我们将更深入地介绍服务类型,但我们现在刚刚将其定义为 a。typeClusterIP

最后,我们有领域。在这里,我们定义流量流经服务的方式和位置。请注意,此字段包含一个数组,以便您可以定义多个条目。ports

让我们分解端口中的每个字段:

  • 名称:可以为给定端口条目分配特定名称。
  • 端口:这是服务将侦听的端口。
  • 目标端口:这是服务将请求转发到的端口。这应该与 Pod 正在侦听的端口匹配。
  • 协议:您希望服务侦听并与之交互的特定协议。

现在,我们已经基本了解了服务清单的功能以及它如何与 Pod 交互,接下来我们将部署清单并检查服务。

$ k get svc

NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE

kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP   90m

nginx        ClusterIP   10.108.176.62   <none>        80/TCP    18m



$ kubectl describe svc nginx

Name:              nginx

Namespace:         default

Labels:            app=nginx

Annotations:       <none>

Selector:          app=nginx

Type:              ClusterIP

IP Family Policy:  SingleStack

IP Families:       IPv4

IP:                10.108.176.62

IPs:               10.108.176.62

Port:              80-80  80/TCP

TargetPort:        80/TCP

Endpoints:         10.244.230.199:80,10.244.242.66:80,10.244.242.67:80

Session Affinity:  None

Events:            <none>

描述命令中的所有内容都应与清单定义保持一致,但两个字段和除外。IPEndpoints

该字段定义服务的 IP 地址。这是可用于访问服务后面的 Pod 的静态 IP。IP

该字段定义当前分配给此服务的容器 IP。您注意到这些 IP 来自我们的 Nginx 部署。Endpoints

在我们继续讨论其他服务类型之前,让我们展示如何缩减 Nginx 部署,并查看服务将如何处理此更改。

$ kubectl scale deployment/nginx --replicas 1

deployment.apps/nginx scaled



$ kubectl get pods -owide

NAME                    READY   STATUS    RESTARTS   AGE   IP               NODE                      NOMINATED NODE   READINESS GATES

nginx-8f458dc5b-mlkwd   1/1     Running   0          43m   10.244.230.199   nodepool-a-11aa1dc199fa   <none>           <none>



$ kubectl describe service nginx | grep Endpoints  

Endpoints:         10.244.230.199:80



$ kubectl scale deployment/nginx --replicas 5

deployment.apps/nginx scaled



$ kubectl get pods -owide

NAME                    READY   STATUS    RESTARTS   AGE    IP               NODE                      NOMINATED NODE   READINESS GATES

nginx-8f458dc5b-9pmhb   1/1     Running   0          108s   10.244.242.69    nodepool-a-95e9c8e86208   <none>           <none>

nginx-8f458dc5b-mlkwd   1/1     Running   0          47m    10.244.230.199   nodepool-a-11aa1dc199fa   <none>           <none>

nginx-8f458dc5b-r2pcp   1/1     Running   0          108s   10.244.242.70    nodepool-a-95e9c8e86208   <none>           <none>

nginx-8f458dc5b-vgwv6   1/1     Running   0          108s   10.244.242.68    nodepool-a-95e9c8e86208   <none>           <none>

nginx-8f458dc5b-x6thl   1/1     Running   0          108s   10.244.230.200   nodepool-a-11aa1dc199fa   <none>           <none>



$ kubectl describe service nginx | grep Endpoints

Endpoints:         10.244.230.199:80,10.244.230.200:80,10.244.242.68:80 + 2 more...

您可以看到,向上和向下扩展 Pod 会立即反映在服务中,允许单个静态 IP 访问 Pod。

三、服务类型

在服务资源中,有三种不同的类型。它们如下:

  1. 集群IP
  2. 节点端口
  3. 负载均衡器这些都有自己的行为和特定的用例,因此知道何时使用每个非常重要。在以下部分中,我们将对其中的每一个进行更深入的探讨。

3.1、集群 IP

集群 IP 是最基本的“服务类型。它将创建一个静态 IP 地址,该地址位于与 Pod IP 范围不同的子网上,并通过标签选择器监控一组 Pod。

集群-IP

ClusterIP 允许 Pod 之间轻松通信,因为 Pod 可以向静态 IP 发送请求,也可以使用 Kubernetes DNS 发送请求。在我们部署的前面的示例中,DNS 应该是。使用 ClusterIP 存在一些限制。最大的问题是没有办法向外界公开这项服务。这是服务类型应该使用的地方。{service-name}.{namespace}nginx.defaultnodePortloadBalancer

3.2、节点端口

该服务建立在类型之上。它通过在工作节点上打开端口来转发流量,从而向外界公开。这意味着,如果您有 50 个工作器节点,则每个工作器节点都将侦听分配的端口,即使 Pod 不存在于该工作节点上也是如此。NodePortClusterIPClusterIP

节点端口

要创建服务,唯一的区别是定义NodePortTypeNodePort

kind: Service

metadata:

  labels:

    app: nginx

  name: nginx

spec:

  ports:

  - name: 80-80

    port: 80

    protocol: TCP

    targetPort: 80

    # nodePort: 30001

  selector:

    app: nginx

  type: NodePort

您可能会注意到它被注释掉了。这是因为如果您不定义该部分,Kubernetes 将自动从该范围内分配一个随机端口。如果要定义特定端口,则必须从此范围完成。nodePortnodePortports30000-32767

部署 NodePort 服务清单并让我们对其进行审核。

$ kubectl get svc

NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE

kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP        135m

nginx        NodePort    10.98.246.253   <none>        80:30828/TCP   5s



$ kubectl describe svc nginx

Name:                     nginx

Namespace:                default

Labels:                   app=nginx

Annotations:              <none>

Selector:                 app=nginx

Type:                     NodePort

IP Family Policy:         SingleStack

IP Families:              IPv4

IP:                       10.98.246.253

IPs:                      10.98.246.253

Port:                     80-80  80/TCP

TargetPort:               80/TCP

NodePort:                 80-80  30828/TCP

Endpoints:                10.244.230.199:80,10.244.230.200:80,10.244.242.68:80 + 2 more...

Session Affinity:         None

External Traffic Policy:  Cluster

Events:                   <none>

在这里,我们看到一个名为值的新字段。这是分配给此服务的端口和协议。现在,如果我们要使用该端口向任何工作节点 IP 地址发送请求,则请求将发送到服务,然后服务将发送到我们的 Pod。NodePort30828/TCP

$ kubectl get nodes -owide

NAME                      STATUS   ROLES    AGE    VERSION   INTERNAL-IP   EXTERNAL-IP     OS-IMAGE                       KERNEL-VERSION    CONTAINER-RUNTIME

nodepool-a-11aa1dc199fa   Ready    <none>   140m   v1.24.4   10.1.96.5     45.77.152.173   Debian GNU/Linux 10 (buster)   4.19.0-22-amd64   containerd://1.6.8

nodepool-a-95e9c8e86208   Ready    <none>   140m   v1.24.4   10.1.96.4     140.82.44.105   Debian GNU/Linux 10 (buster)   4.19.0-22-amd64   containerd://1.6.8

$ curl 140.82.44.105:30828

<!DOCTYPE html>

<html>

<head>

<title>Welcome to nginx!</title>



$ curl 45.77.152.173:30828

<!DOCTYPE html>

<html>

<head>

<title>Welcome to nginx!</title>

虽然这是向外界公开我们的服务的有用方法,但它不能很好地扩展。在高度自动扩展的 Kubernetes 集群中,工作节点可能是短暂的,因为它们可能不断上下旋转。这使我们陷入了与pod类似的问题,我们可以在其中找出IP是什么,但这并不意味着它们将永远存在。如果您的服务需要静态外部 IP 地址,则服务类型将满足该需求。LoadBalancer

3.3、负载均衡器

与 how’s 构建在 ‘, the’s 服务构建在 –使用服务时,每个工作节点继续具有分配给特定服务的唯一端口。但是,现在部署并配置了 L4 以路由到特定的工作器节点和端口。高级请求流如下所示:将请求发送到负载均衡器,负载均衡器转发到特定节点端口上的工作节点,然后传递到特定服务,最后发送到 Pod。NodePortClusterIPLoadBalancerNodePortLoadBalancer

负载均衡器

如果需要,配置服务会更加复杂。一个非常简单的配置可以如下所示,只是将字段更改为。LoadBalancertypeLoadBalancer

apiVersion: v1

kind: Service

metadata:

  labels:

    app: nginx

  name: nginx

spec:

  ports:

  - name: 80-80

    port: 80

    protocol: TCP

    targetPort: 80

  selector:

    app: nginx

  type: LoadBalancer

例如,如果您希望更改负载均衡器以强制 SSL 重定向、配置防火墙规则、使用 HTTPS 而不是 HTTP 或使用不同的平衡算法,则需要在您的服务上定义。Annotations

下面是一个这样的例子。LoadBalancer

apiVersion: v1

kind: Service

metadata:

  annotations:

    service.beta.kubernetes.io/vultr-loadbalancer-protocol: "http"

    service.beta.kubernetes.io/vultr-loadbalancer-https-ports: "443"

    # You will need to have created a TLS Secret and pass in the name as the value

    service.beta.kubernetes.io/vultr-loadbalancer-ssl: "ssl-secret"

    service.beta.kubernetes.io/vultr-loadbalancer-algorithm: "least_connections"

    service.beta.kubernetes.io/vultr-loadbalancer-ssl-redirect: "true"

    service.beta.kubernetes.io/vultr-loadbalancer-firewall-rules: "0.0.0.0/80;0.0.0.0/443"

  labels:

    app: nginx

  name: nginx

spec:

  ports:

    - port: 80

      name: "http"

    - port: 443

      name: "https"

  selector:

    app: nginx

  type: LoadBalancer

有关更多详细信息,请参阅可与 Vultr 负载均衡器一起使用的所有可用注释列表。

当您第一次部署您的服务时,您将看到该部分是LoadBalancerEXTERNAL-IPpending

$ portfolio [talk-postman] âš¡  k get svc

NAME         TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE

kubernetes   ClusterIP      10.96.0.1        <none>        443/TCP        150m

nginx        LoadBalancer   10.103.252.120   <pending>     80:30092/TCP   4s



$ portfolio [talk-postman] âš¡  kubectl describe service nginx

Name:                     nginx

Namespace:                default

Labels:                   app=nginx

Annotations:              <none>

Selector:                 app=nginx

Type:                     LoadBalancer

IP Family Policy:         SingleStack

IP Families:              IPv4

IP:                       10.103.252.120

IPs:                      10.103.252.120

Port:                     80-80  80/TCP

TargetPort:               80/TCP

NodePort:                 80-80  30092/TCP

Endpoints:                10.244.230.199:80,10.244.230.200:80,10.244.242.68:80 + 2 more...

Session Affinity:         None

External Traffic Policy:  Cluster

Events:

  Type     Reason                  Age               From                Message

  ----     ------                  ----              ----                -------

  Normal   EnsuringLoadBalancer    8s (x4 over 44s)  service-controller  Ensuring load balancer

  Warning  SyncLoadBalancerFailed  8s (x4 over 44s)  service-controller  Error syncing load balancer: failed to ensure load balancer: load-balancer is not yet active - current status: pending

您注意到,这里 Kubernetes 已经部署了一个 Vultr 负载均衡器,并且正在进行持续通信,以检查负载均衡器是否已被配置并分配了 IP。

一旦 Vultr 负载均衡器已配置并准备好用于流量,您将在字段中看到分配的 IP 地址,并且 LB 已得到保证”。这意味着 Kubernetes 现在可以控制相应地监控和更新负载均衡器。另外,请注意,分配了一个唯一的节点端口。EXTERNAL-IP

$ portfolio [talk-postman] âš¡  k get svc

NAME         TYPE           CLUSTER-IP       EXTERNAL-IP    PORT(S)        AGE

kubernetes   ClusterIP      10.96.0.1        <none>         443/TCP        154m

nginx        LoadBalancer   10.103.252.120   45.63.15.140   80:30092/TCP   4m9s



$ kubectl describe service nginx

Name:                     nginx

Namespace:                default

Labels:                   app=nginx

Annotations:              <none>

Selector:                 app=nginx

Type:                     LoadBalancer

IP Family Policy:         SingleStack

IP Families:              IPv4

IP:                       10.103.252.120

IPs:                      10.103.252.120

LoadBalancer Ingress:     45.63.15.140

Port:                     80-80  80/TCP

TargetPort:               80/TCP

NodePort:                 80-80  30092/TCP

Endpoints:                10.244.230.199:80,10.244.230.200:80,10.244.242.68:80 + 2 more...

Session Affinity:         None

External Traffic Policy:  Cluster

Events:

  Type     Reason                  Age                    From                Message

  ----     ------                  ----                   ----                -------

  Warning  SyncLoadBalancerFailed  2m21s (x5 over 3m37s)  service-controller  Error syncing load balancer: failed to ensure load balancer: load-balancer is not yet active - current status: pending

  Normal   EnsuringLoadBalancer    61s (x6 over 3m37s)    service-controller  Ensuring load balancer

  Normal   EnsuredLoadBalancer     60s                    service-controller  Ensured load balancer

要验证我们的 Vultr 负载均衡器是否正常工作,我们可以向负载均衡器公共 IP 地址发送请求。EXTERNAL-IP

$ curl 45.63.15.140

<!DOCTYPE html>

<html>

<head>

<title>Welcome to nginx!</title>

您可以将服务类型视为一个绝佳的解决方案。但是,它们有一个缺点;不能定义任何类型的 URL 路径,这不允许灵活性或将其路由到多个服务。该服务绑定到单个服务,因此如果要从群集中公开另一个应用程序,则需要部署另一个 Vultr 负载均衡器,如果您有多个应用程序,这可能会迅速增加成本。LoadBalancerLoadBalancer

这个问题的解决方案不是另一种服务类型,而是调用不同的 Kubernetes 资源。ingress

3.4、入口

Anresource是一种更灵活的方法来定义外部世界要访问的服务。这里最大的卖点之一是您可以定义单个入口资源,并将此路由路由到您定义的子域、端口或 URL 路径下的所有服务。它们还提供了大量其他功能,例如速率限制、身份验证、自动 TLS、加权路由等等。所有这些都可以使用单个负载均衡器完成,因为 theresource 与服务类型一起工作,唯一的区别是部署负载均衡器充当代理将所有流量发送到 theresource,它充当集群中的 L7 负载均衡器。ingressIngressLoadBalancerIngress

 

这里唯一需要注意的是,Kubernetes没有内部入口控制器。那里有各种入口控制器,都具有不同类型的功能。最终,这取决于您的需求和愿望,然后再决定安装哪个入口控制器。有关配置指南,请参阅入口控制器的文档。

一些更流行的入口控制器是:

  • 使者入口
  • 大使边缘堆栈
  • 入口 nginx

四、总结

虽然这篇文章可能会让你很好地理解不同类型的 Kubernetes 服务和入口控制器,但这只是划伤了服务。这些服务还有更多配置选项和一些免费功能,例如网络策略。请查看下面列出的一些链接以获取更多信息。

使用 client-go API 在 Go 中创建和管理 Kubernetes 作业 教程

一、介绍

Kubernetes API 服务器提供了一个 REST API 接口,供客户端与之交互并执行创建部署等操作。但是客户端应用程序不必理解和使用较低级别的 HTTP 操作,如 GET、PUT、POST 或 PATCH,因为不同语言有多个客户端库。最受欢迎的一个是Go 客户端库。

本指南介绍如何使用 Kubernetes Go 客户端创建作业和 Cron 作业。在我们进入细节之前,让我们简要了解这些概念。

1.1、工作

Kubernetes作业可用于运行有限时间后必须完成的应用程序类型。这些短期任务(如批处理作业)可能作为一次性进程运行,也可能定期运行。就像部署一样,作业也会创建一个或多个 Pod。要使作业被视为完成,其 Pod 应成功终止。如果作业执行失败,Kubernetes 会重试执行。也可以删除或暂停作业 – 在这两种情况下,作业创建的 Pod 都会被删除。

1.2、重复作业

CronJob 可用于需要按特定时间间隔运行的任务,例如每天/每周/每月一次。根据基于Cron格式的计划定期创建新的作业对象。

二、准备工作

若要完成本指南,需要以下各项:

  • AVultr Kubernetes Engine (VKE) cluster.
  • 在本地工作站上安装Go 编程语言(版本 1.18 或更高版本)。
  • 在本地工作站上安装最新版本的kubectl。

三、初始化项目

在本地计算机上,创建一个目录并切换到该目录:

mkdir k8s-jobs-go-client

cd k8s-jobs-go-client

创建一个新的 Go 模块:

go mod init k8s-jobs-go-client

这将创建一个新的go.mod文件

创建一个新文件main.go

touch main.go

 四、导入库

要导入所需的 Go 模块,请将以下内容添加到main.go文件:

package main



import (

    "context"

    "flag"

    "fmt"

    "log"

    "path/filepath"



    batchv1 "k8s.io/api/batch/v1"

    apiv1 "k8s.io/api/core/v1"



    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"



    "k8s.io/client-go/kubernetes"

    "k8s.io/client-go/tools/clientcmd"

    "k8s.io/client-go/util/homedir"

)

 五、添加初始化函数

将下面的代码添加到main.go

var clientset *kubernetes.Clientset



const jobName = "test-job-clientgo"

const cronJobName = "test-cronjob-clientgo"



func init() {

    var kubeconfig *string

    if home := homedir.HomeDir(); home != "" {

        kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")

    } else {

        kubeconfig = flag.String("kubeconfig","  ", "absolute path to the kubeconfig file")

    }

    flag.Parse()



    config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)

    if err != nil {

        log.Fatal(err)

    }

    clientset, err = kubernetes.NewForConfig(config)

    if err != nil {

        log.Fatal(err)

    }

}

init函数负责以下工作:

  1. 访问用户主目录下的 .kube文件夹中的配置文件
  2. 使用 kube 配置文件的路径创建一个 *rest。Configby invokinclientcmd.BuildConfigFromFlags.
  3. 创建一个kubernetes。通过调用 kubernetes 进行客户端设置。NewForConfig传入 *rest。配置

六、添加函数以创建作业

createJob函数添加到main.go文件:

func createJob() {

    fmt.Println("creating job", jobName)

    jobsClient := clientset.BatchV1().Jobs(apiv1.NamespaceDefault)



    job := &batchv1.Job{ObjectMeta: metav1.ObjectMeta{

        Name: jobName,

    },

        Spec: batchv1.JobSpec{

            Template: apiv1.PodTemplateSpec{

                Spec: apiv1.PodSpec{

                    Containers: []apiv1.Container{

                        {

                            Name: "pi",

                            Image: "perl:5.34.0",

                            Command: []string{"perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"},

                        },

                    },

                    RestartPolicy: apiv1.RestartPolicyNever,

                },

            },

        },

    }



    _, err := jobsClient.Create(context.Background(), job, metav1.CreateOptions{})

    if err != nil {

        log.Fatal("failed to create job", err)

    }



    fmt.Println("created job successfully")

}

如果成功,此函数将创建一个名为test-job-clientgo 的作业

  • 首先,它获得v1。通过调用客户端集的作业接口实例。批处理V1()。具有默认命名空间的 Jobs()
  • 准备新的作业定义
  • 调用 JobInterface.Create 以启动作业创建

七、添加主函数

最后,添加 main 函数,它将调用createJob函数。

func main() {

    createJob()

}

 八、运行程序以创建新作业

确保已将kubectl配置为指向要用于本指南的 Kubernetes 集群。例如,使用kind cluster create来创建一个新的Kubernetes集群。这也将自动配置kubectl

kubectl get nodes

应会看到以下输出:

NAME                 STATUS   ROLES           AGE   VERSION

kind-control-plane   Ready    control-plane   23h   v1.24.0

运行程序:

go run main.go

应会看到以下输出:

creating job test-job-clientgo

created job successfully

确认作业已创建:

kubectl get job/test-job-clientgo

应会看到以下输出:

NAME                COMPLETIONS   DURATION   AGE

test-job-clientgo   1/1           7s         66s

作业创建了一个 Pod,大约需要 7 秒来计算 Pi 的值(到 2000 位)并将其打印出来。

要确认结果,请检查 Pod 日志:

kubectl logs $(kubectl get pods --selector=job-name=test-job-clientgo --output=jsonpath='{.items[*].metadata.name}')

 九、添加删除作业的函数

deleteJob函数添加到main.go

func deleteJob() {

    fmt.Println("deleting job", jobName)



    jobsClient := clientset.BatchV1().Jobs(apiv1.NamespaceDefault)



    pp := metav1.DeletePropagationBackground

    err := jobsClient.Delete(context.Background(), jobName, metav1.DeleteOptions{PropagationPolicy: &pp})

    if err != nil {

        log.Fatal("failed to delete job", err)

    }

    fmt.Println("deleted job successfully")

}

更新函数:注释掉createJob函数并添加deleteJob函数。

func main() {

    //createJob()

    deleteJob()

}

 十、运行程序以删除现有作业

要运行该程序:

go run main.go

应会看到以下输出:

deleting job test-job-clientgo

deleted job successfully

确认作业已删除:

kubectl get job/test-job-clientgo

应会看到以下输出:

Error from server (NotFound): jobs.batch "test-job-clientgo" not found

确认作业创建的 Pod 也已删除:

kubectl get pods --selector=job-name=test-job-clientgo

应会看到以下输出:

No resources found in default namespace.

 十一、添加函数以创建 CronJob

createCronJob函数添加到main.go

func createCronJob() {

    fmt.Println("creating cron job", cronJobName)



    cronJobsClient := clientset.BatchV1().CronJobs(apiv1.NamespaceDefault)



    cronJob := &batchv1.CronJob{ObjectMeta: metav1.ObjectMeta{

        Name: cronJobName},

        Spec: batchv1.CronJobSpec{

            Schedule: "* * * * *",

            JobTemplate: batchv1.JobTemplateSpec{

                Spec: batchv1.JobSpec{

                    Template: apiv1.PodTemplateSpec{

                        Spec: apiv1.PodSpec{

                            Containers: []apiv1.Container{

                                {

                                    Name: "hello",

                                    Image: "busybox:1.28",

                                    Command: []string{"/bin/sh", "-c", "date; echo Hello from the Kubernetes cluster"},

                                },

                            },

                            RestartPolicy: apiv1.RestartPolicyOnFailure,

                        },

                    },

                },

            },

        },

    }



    _, err := cronJobsClient.Create(context.Background(), cronJob, metav1.CreateOptions{})

    if err != nil {

        log.Fatal("failed to create cron job", err)

    }



    fmt.Println("created cron job successfully")

}

如果成功,此函数将创建一个名为test-cronjob-clientgo 的CronJob

  • 首先,它获得v1。通过调用客户端集的 CronJobInterface实例。批处理V1()。具有默认命名空间的 CronJobs()
  • 准备新的CronJob定义
  • 调用CronJobInterface.Create以启动CronJob创建

更新函数:注释掉deleteJob函数并添加createCronJob函数。

func main() {

    //createJob()

    //deleteJob()

    createCronJob()

}

 十二、运行程序以创建 CronJob

要运行该程序:

go run main.go

应会看到以下输出:

creating cron job test-cronjob-clientgo

created cron job successfully

确认已创建CronJob

kubectl get cronjob/test-cronjob-clientgo

应会看到以下输出:

NAME                    SCHEDULE    SUSPEND   ACTIVE   LAST SCHEDULE   AGE

test-cronjob-clientgo   * * * * *   False     0        <none>          17s

CronJob每分钟创建一个Pod,并打印当前时间以及一条消息。检查豆荚:

kubectl get pods

应会看到以下输出:

NAME                                   READY   STATUS      RESTARTS   AGE

test-cronjob-clientgo-27804391-qcjzd   0/1     Completed   0          2m49s

你应该看到名称以test-cronjob-clientgo开头的Pod。

要检查Pod日志,请执行以下操作:

kubectl logs <enter Pod name>

您应该会看到包含当前日期的输出以及一条消息:

Fri Sep 12 14:34:00 UTC 2022

Hello from the Kubernetes cluster

 十三、添加用于删除 CronJob 的函数

添加删除CronJob函数:

func deleteCronJob() {

    fmt.Println("deleting cron job", cronJobName)



    cronJobsClient := clientset.BatchV1().CronJobs(apiv1.NamespaceDefault)



    pp := metav1.DeletePropagationBackground

    err := cronJobsClient.Delete(context.Background(), cronJobName, metav1.DeleteOptions{PropagationPolicy: &pp})

    if err != nil {

        log.Fatal("failed to delete cron job", err)

    }

    fmt.Println("deleted cron job successfully")

}

更新函数:注释掉createCronJob函数并添加deleteCronJob函数。

func main() {

    //createJob()

    //deleteJob()

    //createCronJob()

    deleteCronJob()

}

 十四、运行程序以删除现有的 CronJob

运行程序:

go run main.go

应会看到以下输出:

deleting cron job test-cronjob-clientgo

deleted cron job successfully

确认作业已删除:

kubectl get cronjob/test-cronjob-clientgo

应会看到以下输出:

Error from server (NotFound): cronjobs.batch "test-cronjob-clientgo" not found

确认 cron作业创建的Pod也被删除了

kubectl get pods

你不应该看到名称以test-cronjob-clientgo 开头的Pod。

 十五、结论

本指南介绍了如何使用 Kubernetes Go 客户端库创建和删除Job 和CronJob