学习

Kubernetes 运算符:它是什么以及为什么你应该真正关注它

Ajeet Raina
作者
Ajeet Raina, Redis 前开发人员增长经理

Kubernetes 因其快速部署新应用程序的能力而广受欢迎。得益于“基础设施即数据”(特别是 YAML),今天您可以表达所有 Kubernetes 资源,例如 Pod, 部署, 服务, 等,在 YAML 文件中。这些默认对象使 DevOps 和 SRE 工程师更容易完全表达其工作负载,而无需学习如何在 Python、Java 或 Ruby 等编程语言中编写代码。

Kubernetes 专为自动化而设计。开箱即用,您从 Kubernetes 的核心获得了大量内置自动化功能。它可以通过简化、自动化的部署、更新(滚动更新)以及几乎零停机时间地管理您的应用程序和服务来加快您的开发过程。但是,Kubernetes 本身无法为有状态应用程序自动执行该过程。例如,假设您在多个节点上运行了一个有状态的工作负载,例如数据库应用程序。如果大部分节点都宕机了,您将需要根据特定步骤从特定快照中重新加载数据库。使用 Kubernetes 中现有的默认对象、类型和控制器,这将无法实现。

想想为有状态应用程序扩展节点,或升级到新版本,或进行灾难恢复——这些操作通常需要非常具体的步骤,并且通常需要人工干预。Kubernetes 不可能了解所有有状态的、复杂的、集群化的应用程序。Kubernetes 本身不知道,例如,Redis 数据库集群的配置值,以及其排列的成员资格和有状态的、持久性的存储。此外,在 Kubernetes 中扩展有状态应用程序并非易事,需要人工干预。

有状态与无状态应用程序#

让我们尝试了解 有状态 与 无状态 应用程序之间的区别,并举一个简单的例子。考虑一个 Kubernetes 集群,它运行一个简单的 Web 应用程序(没有任何操作符)。下面的 YAML 文件允许您创建 NGINX 的两个副本(一个无状态应用程序)。

 apiVersion: apps/v1
 kind: Deployment
 metadata:
   name: nginx-deployment
   namespace: web
 spec:
   selector:
     matchLabels:
       app: nginx
   replicas: 2
   template:
     metadata:
       labels:
         app: nginx
     spec:
       containers:
       - name: nginx
         image: nginx:1.14.2
         ports:
         - containerPort: 80

在上面的示例中,名为 nginx-deployment 的部署对象是在命名空间“web”下创建的,由 .metadata.name 字段指示。它创建了两个复制的 Pod,由 .spec.replicas 字段指示。 .spec.selector 字段定义了部署如何找到要管理的 Pod。在这种情况下,您选择一个在 Pod 模板中定义的标签(app: nginx)。模板字段包含以下子字段:Pod 使用 .metadata.labels 字段标记为 app: nginx ,并且 Pod 模板的规范指示 Pod 运行一个容器 nginx,它运行 Docker Hub 镜像中的 nginx,版本为 1.14.2。最后,它创建一个容器并将其命名为 nginx。

运行以下命令以创建部署资源

kubectl create -f nginx-dep.yaml

让我们通过运行以下命令来验证部署是否成功创建

 kubectl get deployments
 NAME               READY   UP-TO-DATE   AVAILABLE   AGE
 nginx-deployment   2/2     2            2           63s

上面的示例显示了命名空间中的部署名称。它还显示了对用户可用的应用程序副本数量。您还可以看到,已更新的所需副本数量为 2,以实现所需状态。

您可以运行 kubectl describe 命令以获取部署资源的详细信息。要显示特定资源或一组资源的详细信息:

 kubectl describe deploy
 Name:                   nginx-deployment
 Namespace:              default
 CreationTimestamp:      Mon, 30 Dec 2019 07:10:33 +0000
 Labels:                 <none>
 Annotations:            deployment.kubernetes.io/revision: 1
 Selector:               app=nginx
 Replicas:               2 desired | 2 updated | 2 total | 0 available | 2 unavailable
 StrategyType:           RollingUpdate
 MinReadySeconds:        0
 RollingUpdateStrategy:  25% max unavailable, 25% max surge
 Pod Template:
   Labels:  app=nginx
   Containers:
    nginx:
     Image:        nginx:1.7.9
     Port:         80/TCP
     Host Port:    0/TCP
     Environment:  <none>
     Mounts:       <none>
   Volumes:        <none>
 Conditions:
   Type           Status  Reason
   ----           ------  ------
   Available      False   MinimumReplicasUnavailable
   Progressing    True    ReplicaSetUpdated
 OldReplicaSets:  <none>
 NewReplicaSet:   nginx-deployment-6dd86d77d (2/2 replicas created)
 Events:
   Type    Reason             Age   From                   Message
   ----    ------             ----  ----                   -------
   Normal  ScalingReplicaSet  90s   deployment-controller  Scaled up replica set nginx-deployment-6dd86d77d to 2

部署负责保持一组 Pod 运行,但同样重要的是公开这些 Pod 的接口,以便其他外部进程可以访问它们。这就是服务资源的作用。服务资源让您可以公开在 Pod 中运行的应用程序,使其可从集群外部访问。让我们创建一个服务资源定义,如下所示

apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  selector:
    app: nginx
  ports:
    - port: 80
      targetPort: 80
  type: LoadBalancer

上面的 YAML 规范创建一个名为“nginx-service”的新服务对象,它针对任何带有 app=nginx 标签的 Pod 的 TCP 端口 80。

 kubectl get svc -n web
 NAME            TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
 nginx-service   LoadBalancer   10.107.174.108   localhost     80:31596/TCP   46s

让我们将部署扩展到 4 个副本。我们将使用 kubectl scale 命令,后跟部署类型、名称和所需的实例数量。输出类似于此:

kubectl scale deployments/nginx-deployment --replicas=4
deployment.extensions/nginx-deployment scaled

已应用更改,我们有 4 个应用程序实例可用。接下来,让我们检查 Pod 的数量是否发生了变化。现在应该有 4 个 Pod 在集群中运行(如下面的图表所示)

 kubectl get deployments
 NAME               READY   UP-TO-DATE   AVAILABLE   AGE
 nginx-deployment   4/4     4            4           4m

有 4 个 Pod,具有不同的 IP 地址。更改已在部署事件日志中注册。

 kubectl get pods -o wide
 NAME                               READY   STATUS    RESTARTS   AGE     IP           NODE             NOMINATED NODE   READINESS GATES
 nginx-deployment-6dd86d77d-b4v7k   1/1     Running   0          4m32s   10.1.0.237   docker-desktop   none             none
 nginx-deployment-6dd86d77d-bnc5m   1/1     Running   0          4m32s   10.1.0.236   docker-desktop   none             none
 nginx-deployment-6dd86d77d-bs6jr   1/1     Running   0          86s     10.1.0.239   docker-desktop   none             none
 nginx-deployment-6dd86d77d-wbdzv   1/1     Running   0          86s     10.1.0.238   docker-desktop   none             none

删除其中一个 Web 服务器 Pod 将触发控制平面中的工作,以恢复四个副本的所需状态。Kubernetes 启动一个新的 Pod 来替换已删除的 Pod。在本节中,替换的 Pod 显示了 STATUS 为 ContainerCreating

 kubectl delete pod nginx-deployment-6dd86d77d-b4v7k

您会注意到,Nginx 静态 Web 服务器可以与任何其他副本或替换其中一个副本的新 Pod 交换。它不以任何方式存储数据或维护状态。Kubernetes 不需要做任何特殊安排来替换失败的 Pod,或通过添加或删除服务器副本来扩展应用程序。现在您可能在想,如果要存储应用程序的状态怎么办?好问题。

扩展有状态应用程序很困难#

在 Kubernetes 中扩展无状态应用程序很容易,但有状态应用程序并非如此。有状态应用程序需要人工干预。上下启停 Pod 并不那么简单。每个节点都有一个标识,并附带一些数据。删除 Pod 意味着丢失其数据并中断系统。

考虑一个 Kubernetes 集群,其中 6 个工作节点托管一个 Nginx Web 应用程序,该应用程序连接到一个持久性卷,如上所示。以下是 StatefulSets YAML 文件的代码片段


apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: "nginx"
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 1Gi

Kubernetes 将物理存储设备以名为 持久卷 的对象的形式提供给您的集群。每个持久卷都由一个 Kubernetes Pod 通过发出 持久卷声明 对象(也称为 PVC)来使用。PVC 对象允许 Pod 使用持久卷中的存储。假设我们要将一个集群从 5 个节点缩减到 3 个节点。突然移除 2 个节点是一个可能造成破坏的操作。这可能会导致所有数据副本丢失。处理节点移除的更好方法是,在执行实际的 Pod 删除操作之前,先将要移除的节点上的数据迁移到系统中的其他节点。需要注意的是,StatefulSet 控制器本质上是通用的,它不可能知道所有可能的管理数据迁移和复制的方法。然而,在实际应用中,StatefulSet 通常不足以处理生产环境中复杂、分布式有状态工作负载系统。

那么,如何解决这个问题呢?这就是 Operator 的作用。Operator 的开发是为了处理默认 Kubernetes 控制器无法处理的复杂、有状态应用程序。虽然像 StatefulSet 这样的 Kubernetes 控制器非常适合部署、维护和扩展简单的无状态应用程序,但它们不具备处理有状态资源的访问,也不具备升级、调整大小和备份更复杂的集群应用程序(如数据库)的功能。Kubernetes Operator 弥合了 Kubernetes 提供的功能和自动化与您的软件如何使用 Kubernetes 之间的差距,实现了与您的软件相关的任务自动化。

Operator 本质上是一个特定于应用程序的控制器,可以帮助您管理 Kubernetes 应用程序。它是一种打包、运行和维护 Kubernetes 应用程序的方式。它旨在扩展 Kubernetes 的功能,并简化应用程序管理。这对于有状态应用程序尤其有用,因为有状态应用程序包括持久存储和其他应用程序外部的元素,可能需要额外的工作来管理和维护。

Kubernetes Operator 的功能#

Kubernetes Operator 使用 Kubernetes API 代表 Kubernetes 用户创建、配置和管理复杂有状态应用程序的实例。有一个名为 OperatorHub.io 的公共存储库,旨在成为查找 Kubernetes Operator 后端服务的公共注册中心。通过 Operator Hub,开发人员可以轻松地基于 Operator 创建应用程序,而无需从头开始构建 Operator 的复杂性。

以下是几个流行的 Kubernetes Operator 及其功能和能力的示例。

Kubernetes Operators:#

  • 帮助您按需部署应用程序(例如,Argo CD Operator (Helm 是一款用于 Kubernetes 的声明式 GitOps 持续交付工具,有助于轻松按需安装和配置)
  • 帮助您安装具有所需配置和应用程序实例数量的应用程序
  • 允许您备份和恢复应用程序状态(例如,Velero Operator 管理灾难恢复、备份和集群组件(如 pv、pvc、部署等)的恢复,以帮助灾难恢复)
  • 处理应用程序代码升级以及更改,例如数据库模式(例如,Flux 是一款用于 Kubernetes 的持续交付解决方案,允许在有新代码要部署时自动更新配置)
  • 可以管理数据库服务器集群(例如,MariaDB Operator 通过定义简单的自定义资源轻松创建 MariaDB 服务器和数据库)
  • 可以安装声明的软件版本和成员数量的数据库集群
  • 扩展或缩减应用程序
  • 持续监视应用程序的运行情况(例如,Prometheus Operator 简化了 Prometheus、Alertmanager 和相关监控组件的部署和配置)
  • 启动升级、自动备份和故障恢复,模拟集群全部或部分的故障以测试其弹性
  • 允许您将服务发布到不支持 Kubernetes API 的应用程序,以发现它们

Operator 如何工作?#

Operator 通过扩展 Kubernetes 控制平面和 API 来工作。Operator 允许您定义一个自定义控制器,该控制器会监视您的应用程序并根据其状态执行自定义任务。您要监视的应用程序通常在 Kubernetes 中定义为一个新对象:一个 自定义资源 (CR),它具有自己的 YAML 规范和对象类型,API 服务器可以很好地理解。这样,您就可以在自定义规范中定义任何特定条件来监视,并在实例与规范不匹配时对其进行协调。Operator 的控制器与规范进行协调的方式与本机 Kubernetes 控制器非常类似,尽管它主要使用自定义组件。

Redis Enterprise Operator 是什么?#

Redis 创建了一个 Operator,它部署并管理 Redis Enterprise Cluster 的生命周期。 Redis Enterprise Operator 是在 Kubernetes 中部署和维护 Redis Enterprise 集群最快、最有效的方式。Operator 从单个 Kubernetes 控制平面创建、配置和管理 Redis Enterprise 部署。这意味着您可以通过创建本机对象(如部署、副本集、有状态集等)来管理 Kubernetes 上的 Redis Enterprise 实例。Operator 允许完全控制 Redis Enterprise 集群的生命周期。

Redis Enterprise Operator 充当 自定义控制器,用于自定义资源 Redis Enterprise Cluster,或 “REC”,它通过 Kubernetes CRD (客户资源定义) 定义,并使用 YAML 文件部署。Redis Enterprise Operator 充当 Kubernetes 基础设施和 Redis Enterprise 集群之间的逻辑“粘合剂”。

Redis Enterprise Operator 如何工作?#

Redis Enterprise Operator 支持两个自定义资源定义 (CRD)

  • Redis Enterprise Cluster (REC):用于创建 Redis Enterprise 集群的 API。请注意,每个 Operator 部署仅支持一个集群。
  • Redis Enterprise Database (REDB):用于在 Redis Enterprise 集群上创建 Redis 数据库的 API。请注意,Redis Enterprise Operator 是命名空间的。解决方案的高级架构和概述可以在 这里 找到。

它的工作原理如下

  1. 1.首先,Redis Enterprise 集群自定义资源(简称“CR”)由 Operator 读取并验证,以获取集群规范。
  2. 2.其次,创建集群有状态集、服务触发器、集群管理员机密、RS/UI 服务。
  3. 3.Redis Enterprise 数据库 CR 由 Operator 读取并验证。
  4. 4.数据库在集群上创建,数据库访问凭据存储在 Kubernetes 机密对象中。
  5. 5.服务触发器发现新数据库并为数据库配置 Kubernetes 服务。
  6. 6.应用程序工作负载使用数据库机密和服务访问数据。

Operator 自动化的示例#

考虑下面的 YAML 文件

apiVersion: app.redislabs.com/v1
kind: RedisEnterpriseCluster
metadata:
  name: rec
spec:
  # Add fields here
  nodes: 3

如果您将节点数量更改为 5,Operator 会与 StatefulSet 交谈,并将副本数量从 3 更改为 5。一旦发生这种情况,Kubernetes 将接管并一次启动一个新节点,并相应地部署 Pod。当每个节点就绪后,新节点将加入集群并变为可用于 Redis Enterprise 主节点。

apiVersion: app.redislabs.com/v1
kind: RedisEnterpriseDatabase
metadata:
  name: redis-enterprise-database
spec:
  redisEnterpriseCluster:
    name: redis-enterprise
    Memory: 2G

为了创建数据库,Operator 会发现资源,与集群 RestAPI 交谈,然后创建数据库。服务器与 API 交谈并发现它。DB 为该数据库创建 Redis 数据库服务端点,该端点将可用。

在下一教程中,您将学习如何从头开始使用 Redis Enterprise Kubernetes Operator,包括如何执行非平凡的任务,如备份、还原、横向扩展等等。敬请关注!

参考资料#

确保您的系统中安装了 Docker。

如果您是新手,请参考 Docker 的安装指南 在 Mac 上安装 Docker。

要拉取并启动 Redis Enterprise 软件 Docker 容器,请在您的操作系统的终端或命令行中运行此 docker run 命令。

请点击您的标题内容,然后按回车键。#